js Advanced Summary (Data Type Closure Prototype Chain Inheritance)

One: JS data type

1. Detailed JS raw and reference data types

  • 1. Basic Types
  • Boolean . Boolean values, true and false.
  • null .A special keyword indicating a null value. JavaScript is case sensitive, so null is completely different from null, NULL, or other variables.
  • undefined . Properties when the variable is undefined.
  • Number . Represents a number, for example: 42 or 3.14159.
  • String . Represents a string, for example, "Howdy"
  • Symbol (New type added in ECMAScript 6).. A data type whose instances are unique and immutable.
  • Bigint

2. Complex types (reference data types):

Object (Contains Ordinary Object, Array Object, Regular Object-RegExp, Date Object, Math Function, Function Object-Function)

The value of a reference type is an object that is stored in both stack memory and stack memory

Unlike other languages, javascript does not allow direct access to memory locations, that is, it does not directly manipulate the memory space of objects. What do we do? Actually, it's a reference to the operation object,
So the value of the reference type is accessed by reference.
To be precise, storage of reference types requires both the stack area of memory, which holds the variable identifier and a pointer to the object in the stack memory.
Or the address of the object in heap memory.


Suppose you have the following objects:

var person1 = {name:'jozo'};
var person2 = {name:'xiaom'};
var person3 = {name:'xiaoq'};

The three objects are stored in memory as follows:

var person1 = {};

var person2 = {};

console.log(person1 == person2); // false

Reference types are accessed by reference, in other words, by comparing the addresses in heap memory of two objects. It is clear that person1 and person2 have different addresses in heap memory.

Immutable property

The basic type is immutable, only the object is mutable. Sometimes we try to "change" the contents of a string, but in JS, any seemingly "change" to a string value actually creates a new string value. Nothing can change the value of a basic type.

   var str = "abc";
   str[0]; // "a"
   str[0] = "d";
   console.log(str); //abc

   var name = 'jozo';
   var upName=name.toUpperCase(); 
   console.log(upName,name); // Output'JOZO''jozo'

Method operation cannot change a value of a base type

  var str = "abc";
  str[0]; // "a"
  str[0] = "d";
  console.log(str); //abc

  var name = 'jozo';
  var upName=name.toUpperCase(); 
  console.log(upName,name); // Output'JOZO''jozo'

As the code above shows, we can't add properties and methods to basic types

The value of the reference type is variable

1     var obj = {x : 0};
2     obj.x = 100;
3     var o = obj;
4     o.x = 1;
5     console.log(obj.x)// 1, modified
6     o = {x:100};  //Equivalent to reassigning, reclaiming memory, not modifying
7     console.log(JSON.stringify(obj),JSON.stringify(o))//{"x":1} {"x":100}
8     obj.x; // 1, will not change because o = {"x":100}

Differences between two data types in parameters and formal parameters

Pass VS by value. Pass by reference

Callby value is the most common evaluation strategy: a function's parameter is a copy of the argument passed when it is called. Modifying the value of a parameter does not affect the argument.

When calling by reference, the parameter of a function receives an implicit reference to the argument instead of a copy. This means that if the value of a function parameter is modified, the argument is also modified. Both point to the same value.

Passing by reference makes tracking function calls more difficult and sometimes causes some subtle BUG s.

Value-by-value delivery has poor performance for some complex types because cloned copies are required each time. Both methods of value transfer have their own problems.

demo instance

The typeof operator, which returns a string indicating the type of the uncalculated operand.

    var person,name;
    person = 'kn';

    name=person;
    person='black and white';
    console.log(person,name,typeof person)
    //Black and white kn string

//black and white kn string

The person change does not change the name, indicating that string s are passed by value. Create a new memory space when assigning

  • Arguments: They can be constants, variables, expressions, functions, etc. No matter what type of arguments they are, they must have certain values when making a function call so that they can be passed to the parameters. Therefore, assignment, input, etc. should be used in advance to obtain the definite value of the actual parameter.
  • Formal parameters: All called "formal parameters" are parameters used when defining the name and body of a function to receive the parameters passed when the function is called.
    The function of formal parameters is to realize the relationship between the main tone function and the adjusted function. Usually, the data processed by the function, the factors that affect the function or the result of the function processing are taken as the formal parameters.
function addNum(param)  //param is a formal parameter relative to the addNum method
{ 
    param+=10; 
     return param; 
} 
var num=10;  
var result=addNum(num); //num is an argument relative to the addNum method
console.log(num);  //10  
console.log(result);//20

In the example above, when num is passed as an argument into the method addNum is accepted as a param eter and used inside the method body, and num is unchanged globally,

But when the argument is a reference type

1 function fun(param)  //param is a formal parameter relative to the fun method
2 { 
3     param[0]=99; 
4      return param; 
5 } 
6 var num=[10];  
7 var result=fun(num); //num is an argument relative to the fun method
8 console.log(num[0]);  //99 
9 console.log(result);//[99]

Changing the parameter within the method body will also change the actual parameter. This is not in any other language

Consider: Just because when a function parameter in js is a reference type, it can affect the argument! Inferring the principle of parameters in callback

​
 1     function fun(data,callback){
 2         var json=[1,2,3];
 3         callback(json)
 4     }
 5 
 6     var data=[];
 7     fun(data,function(result){
 8         data=result;
 9     })
10     console.log(data)//[1, 2, 3]

​

As the example above modifies the variable data in the callback function

Knowledge point 1, function is a data type, can be passed as a parameter 2, array is reference type 3, parameter of reference type will affect argument

1 <body>
 2     <button onclick='log()'>ajax</button>
 3 </body>
 4 <script>
 5     function fun(data,callback){
 6         setTimeout(function(){
 7             var json=[1,2,3];
 8             callback(json)
 9         },4000)
10     
11     }
12 
13     var data=[];
14     fun(data,function(res){
15         data=res;
16     })
17     console.log(data)//[]
18     function log(){
19         console.log(data)//Output in [1, 2, 3] 4 seconds 
20     }
21 
22 </script>

Use setTimeout to simulate ajax requests! (

2. What are the types of conversion in JS?

There are only three types of conversion in JS:

  • Convert to Number
  • Convert to Boolean
  • Convert to String

The specific rules for conversion are as follows:

Note that the result of the line "Boolean to String" refers to an example of true to String

Two: Talk about your understanding of closures

Scope, Scope Chain, Execution Environment, Closure

Scope of variables: global and local variables. (

Global Scope:

Variables defined by outermost functions have global scope, that is, they are accessible to any internal function

Local Scope:

Contrary to global scopes, local scopes are generally accessible only within a fixed snippet of code and not outside a function

Note: When declaring variables inside a function, be sure to use the var command. If not, a global variable is actually declared.

execution environment

Each function runs with an execution environment.

js associates a variable object for each execution environment. All variables and functions defined in the environment are stored in this object. (

The global execution environment is the periphery of the execution environment. The global execution environment is considered a window object, so all global variables and functions are created as properties and methods of the window object. (

The execution order of js is determined by the call of a function, when a function is called, the variable object of the function environment is pushed into an environment stack. After the function executes, the stack pops up the variable object of the function and gives control to the previously executed environment variable object. (

For instance:

 <script>
      var scope = "global"; 
      function fn1(){
         return scope; 
      }
      function fn2(){
         return scope;
      }
      fn1();
      fn2();
</script>

Scope Chain

Each execution environment has a variable object associated with it, in which all variables and functions in the environment are stored. This variable object is inaccessible, but the interpreter will be used in the background.

When code executes in an environment, a scope chain of variable objects is created. The front end of the scope chain is always the variable object of the environment in which the code is currently executed. The next variable object comes from the external containing environment and extends to the global execution environment.

Each function has its own execution environment. When a function is executed, the environment of the function is pushed into an environment stack and the function is ejected after execution.

closure

  • A function that has access to a variable in the scope of another function is usually interpreted as a function. Essentially, closures are bridges that connect the interior and exterior of a function.
  • To be precise: the phenomenon of retaining the active object of an external function in the scope chain of an internal function. *
  • (Closures are based on normal garbage collection mechanisms. That is, a function (function scope) is generally executed and all declared variables are released and recycled by the garbage collector. But closures use a trick to keep variables in the scope from being garbage collected after the function is executed.)
     
  • Role: 1: Can read variables outside its own function (look along the scope chain)
  • 2: Keep these external variables in memory at all times
  • Advantages: 1: Variables reside in memory for a long time;
    2: Avoid pollution of global variables;
    3: the existence of private members;
  • Features: 1: Function set function;
    2: Internal functions can use local variables or parameters of external functions directly;
    3: Variables or parameters will not be recycled by the garbage collection mechanism;
  • Disadvantages: Resident memory increases memory usage. Improper use can cause memory leaks. Detail:

(1) Because closures cause variables in functions to be stored in memory and consume a lot of memory, closures cannot be abused, otherwise performance problems of Web pages may occur and memory leaks may occur in IE. The solution is to delete all unused local variables before exiting the function.

(2) Closure changes the value of the internal variable of the parent function outside the parent function. So if you use a parent function as an object, a closure as its common method, and an internal variable as its private property, be careful not to change the value of the parent function's internal variable.

  1. A common way to create closures is to return one function to another.
function foo(x) {
    var tmp = 3;
    return function (y) {
        alert(x + y + (++tmp));
    }
   }
       
var bar = foo(2); // bar is now a closure
        bar(10)  //16

When var bar = foo(2) is executed; Foo executes and parameter 2 is passed in, but after execution, because tmp variable and parameter x are still waiting in the return value, foo executes, but the foo variable is not released. When return waits to continue using these variables, bar is a closure. Then we execute the bar, and the result is 16, which is the magic of closures, which changes the memory mechanism of JS. (

function foo(x) {
    var tmp = 3;
    function bar(y) {
        alert(x + y + (++tmp));
    }
    bar(10);
}
     
 foo(2)


Foo executes globally and accesses local variables during execution, but he is not a closure. Function foo does not prevent variables in foo scope from being garbage collected after execution, all are not closures.

What is a closure?

  • The definition of a closure in Red Treasure (p178): A closure is a function that has access to a variable in the scope of another function
  • MDN defines closures as functions that have access to free variables. (Free variables are variables used in a function, but they are neither function parameters arguments nor local variables of the function. They are actually variables in another function scope.)

Why do closures occur?

First of all, it is very simple to understand the concept of a scope chain. In ES5, there are only two scopes - global scope and function scope. When accessing a variable, the interpreter first looks for an identifier in the current scope. If not, it looks for a parent scope until it finds the identifier of the variable or is not in the parent scope. This is the scope chain. It is worth noting that each subfunction copies the scope of its parent to form a chain of scopes.

For example:

 var a = 1;
function f1() {
 var a = 2 
function f2() { 
var a = 3; console.log(a);//3
 }
}

In this code, the scope of F1 points to the global scope (window) and itself, while the scope of f2 points to the global scope (window), f1, and itself. Scopes are also searched from the bottom up until the global scope window is found, and errors will be reported if the global does not already exist. It's such a simple thing!

The essence of closures is that there are references to parent scopes in the current environment. Or take the example above:

function f1() { 
var a = 2 
function f2() { 
console.log(a);//2 
}
 return f2;
}
var x = f1();
x();

Here x takes the variable in the parent scope and outputs 2. Because in the current environment, there is a reference to f2, F2 refers exactly to the scope of window s, f1, and f2. Therefore, F2 can access variables in the scope of f1.

Is that the only way to generate a closure is to return a function?

Back to the nature of closures, we just need to have references to parent scopes exist, so we can do the same:

var f3;

function f1() { 
var a = 2 
f3 = function() {
 console.log(a); 
}
}

f1();
f3();

Let F1 execute. Assigning f3 means that now f3 has access to window s, f1, and f3 itself, or does it look up from the bottom. Recently, a was found in f1, so output 2.

Here is the parent-scoped reference to the external variable f3, resulting in closures, which have changed in form but not in nature.

What are the manifestations of closures?

Once you understand the nature, let's see where in a real world can closures really exist?

  • Returns a function. An example has just been given.
  • Pass as function parameter
var a = 1;

function foo(){ 
var a = 2; 
function baz(){ 
console.log(a);
 } 
bar(baz);
}

function bar(fn){ 
// This is a closure
 fn();
}
// Output 2, not 1
foo();
  • In timers, event monitoring, Ajax requests, cross-window communication, Web Workers, or any asynchronization, a callback function is actually a closure.

The following closures save only window s and the current scope.

// timer

setTimeout(

function timeHandler(){

console.log('111');},100)

// event listeners

$('#app').click(function(){ console.log('DOM Listener');})
  • IIFE (Execute Function Expression Now) creates a closure that holds the global scope of window and the scope of the current function, so it can be a global variable.
 var a = 2;

(function IIFE(){console.log(a); // Output 2} ();

How to solve the following circular output problem?

 for(var i = 1; i <= 5; i ++){
 setTimeout(function timer(){ 
console.log(i) 
}, 0)}

Why output all 6? How can I improve it so that it outputs 1, 2, 3, 4, 5? (More methods are better)

Because setTimeout is a macro task, because of the single-threaded eventLoop mechanism in JS, the macro task is executed after the main thread synchronization task is executed, so callbacks in setTimeout are executed after the cycle ends, but when i is output, the current scope does not exist. Look up the previous level and find i. At this time, the cycle has ended and i becomes 6. So all output 6.

Solution:

1. Use IIFE (immediately execute the function expression) to pass the i variable at this time to the timer for each for loop

for(var i = 1;i <= 5;i++){ 
(function(j){ 
setTimeout(
function timer(){ 
console.log(j) }, 0) 
})(i)}

2. Given a timer, pass in the third parameter as the first function parameter of the timer function

for(var i=1;i<=5;i++){

setTimeout(

function timer(j){

 console.log(j)

}, 0, i)}

3. Use let in ES6

 for(let i = 1; i <= 5; i++){

setTimeout(

function timer(){

console.log(i)

},0)}

Lets revolutionize JS, making it functional to block-level, and no longer exist after using let s. The scope of the code is in block level, as shown in the code above:

   // i = 1{ setTimeout(function timer(){ console.log(1) },0)}// i = 2{ setTimeout(function timer(){ console.log(2) },0)}// i = 3...

Therefore, the correct results can be output.

Three: Talk about your understanding of the prototype chain

1. Constructors

Each constructor has a protype property that points to another object. All properties and methods of this object are owned by the constructor.

This also means that we can define the properties and methods that all object instances need to share directly on the protype object.

function Person (name, age) {
  this.name = name
  this.age = age
}

Person.prototype = {
  constructor: Person, // =>Manually point the constructor to the correct constructor
  type: 'human',
  sayHello: function () {
    console.log('My name is' + this.name + ',I am this year' + this.age + 'Age')
  }
}
var p1 = new Person(...)
var p2 = new Person(...)
console.log(p1.sayName === p2.sayName) // => true

At this point, the type attribute and the sayname method of all instances are actually the same memory address, pointing to the ptotype object, which improves efficiency.

2. Relationships among constructors, instances, prototypes

  • Any function has a prototype property, which is an object (prototype object)
  • The prototype object of the constructor has a constructor property by default, which points to the function where the prototype object is located
  • Instance objects derived from constructors contain a pointer u to the prototype object of the constructor proto_u
  • All instances inherit directly or indirectly the members of the prototype object

3. Prototype Chain

Instance objects can access members in prototype objects

Each time the code reads an attribute of an object, a search is performed to target an attribute with a given name

  • Search begins with the object instance itself
  • If an attribute with a given name is found in the instance, the value of the attribute is returned
  • If not found, continue searching for the prototype object pointed to by the pointer, and find attributes with the given name in the prototype object
  • Returns the value of this property if it is found in the prototype object
  • Find it in yourself and return
  • If you can't find it on yourself, look up along the prototype chain and return when you find it
  • Return undefined if not found until the end of the prototype chain
  • JavaScript objects point to the parent object through the prototype until they point to the Object object, which forms a chain that the prototype points to, that is, the prototype chain.

  • hasOwnProperty() of the object to check if the object itself contains the property
  • When using in to check whether an object contains an attribute, it also returns true if there is no object but there is one in the prototype chain

Four: How does JS implement inheritance?

First: with call

function Parent1(){ 
this.name = 'parent1'; 
} 
function Child1(){
 Parent1.call(this); 
this.type = 'child1'
 } 
console.log(new Child1);

When this is written, the subclass can get the attribute value of the parent class, but the problem is that once a method exists in the parent's prototype object, the subclass cannot inherit. Then the following methods are introduced.

Second: with prototype chains

function Parent2() { 
this.name = 'parent2';
 this.play = [1, 2, 3]
 } 
function Child2() { 
this.type = 'child2'; } 
Child2.prototype =new Parent2(); 
console.log(new Child2());

Seemingly OK, both methods and properties of the parent class are accessible, but there is a potential shortage. For instance:

var s1 = new Child2(); 
var s2 = new Child2();
 s1.play.push(4); 
console.log(s1.play, s2.play);

You can see the console:

Clearly, I only changed the play property of s1, so why did s2 follow suit? It's simple, because both instances use the same prototype object.

So is there a better way?

Third: Combine the first two combinations

function Parent3 () { 
this.name = 'parent3';
 this.play = [1, 2, 3]; 
}
function Child3() { 
Parent3.call(this); 
this.type = 'child3'; 
} 
Child3.prototype = new Parent3(); 
var s3 = new Child3(); 
var s4 = new Child3(); 
s3.play.push(4); 
console.log(s3.play, s4.play);

You can see the console:

Previous problems have been solved. A new problem, however, is that the constructor for Parent3 executes more than once (Child3.prototype = new Parent3()); That's what we don't want to see. So how do you solve this problem?

Fourth: Optimize of combinatorial inheritance 1

function Parent4 () { 
this.name = 'parent4';
 this.play = [1, 2, 3]; 
} 
function Child4() {
Parent4.call(this); 
this.type = 'child4'; 
} 
Child4.prototype = Parent4.prototype;

Here we give the parent prototype object directly to the child class, the parent constructor is executed only once, and the parent properties and methods are accessible, but let's test it:

var s3 = new Child4(); 
var s4 = new Child4(); 
console.log(s3)

The constructor for the subclass instance is Parent4, which is obviously incorrect and should be Child4.

Fifth (most recommended): Optimize of combinatorial inheritance 1

function Parent5 () { 
this.name = 'parent5'; 
this.play = [1, 2, 3];
 } 
function Child5() { 
Parent5.call(this); 
this.type = 'child5'; 
} 
Child5.prototype = Object.create(Parent5.prototype); 
Child5.prototype.constructor = Child5;

This is the most recommended approach, near perfect inheritance, and its name is also called parasitic combinatorial inheritance.

Object.create() and setPrototypeof and Child.prototype = Parent.prototype and Child. Prototype = the difference between new Parent () - Lele Senior - Blog Park

Object.create() and setPrototypeof and Child.prototype = Parent.prototype and Child. Prototype = the difference between new Parent ()

Child.prototype = Parent.prototype and Child. Prototype = the difference between new Parent ()

Child.prototype = new Parent() causes multiple instances of Child to point to the same reference, because the new Parent returns a reference type, in which case the properties of one instance of Child are modified, as are the properties of other instances.

//Parent Class
function Parent(){
    this.name = "P_Name";
}
Parent.prototype.func=function(){
    console.log("Parent Func");
}
//Subclass
function Child(){
}
Child.prototype=new Parent();

Child.prototype.func = function(){
    console.log("Child Func");
}
var child = new Child;
var parent = new Parent;

child.func();//Child Func
parent.func();//Parent Func

console.log(child.name);//P_Name
console.log(Child.prototype.constructor) // Parent

2. Child.prototype = Parent.prototype cannot take the constructor property of Parent, the constructor points to Parent, and overriding the prototype method of a subclass causes the prototype method of the parent class to be overridden

function Child2(){
}

# The following assignments will override Parent.prototype.func method
Child2.prototype=Parent.prototype;

Child2.prototype.func = function(){
    console.log("Child2 Func");
}

var child2 = new Child2;

child2.func();//Child2 Func
parent.func();//Child2 Func

console.log(child2.name);//undefined
console.log(Child2.prototype.constructor) // Parent

setPrototypeOf and Object.create difference

  • Only function types can be accessed by prototype, only getPrototypeOf or proto can be used for other types, and other types are also generated by function (String,Number... involves implicit object creation)
  • The object's prototype is just a reference to another object. The nesting between object prototypes constitutes a prototype chain, whose purpose is to maintain queries to access object properties and determine access rights.

If there are two functions, A and B, let the prototype of A point to B

1.setPrortotypeOf

Object.setPrototypeOf(A.prototype,B.prototype)

2.Create

A.prototype = Object.create(B.prototype)

Both can achieve the function of prototyping objects, but there are some differences in the specific performance.

The code is as follows

Use Object.create

Animal.prototype = Object.create(Plants.prototype)
    console.log(Animal.prototype)
    /*
    Plants {}
        __proto__:
            shout: ƒ (xssss)
            genO2: ƒ ()
            constructor: ƒ Plants()
            __proto__: Object
    */
    let cat = new Animal('mimi','miao~miao~')
    
    dog.shout() // pipi wang!wang!
    cat.shout() // mimi miao~miao~ plants tag
    cat.genO2() // mimi generates oxygen.

Use setPrototypeOf

Object.setPrototypeOf(Animal.prototype,Plants.prototype)
    console.log(Animal.prototype)
    /*
    Plants {shout: ƒ, constructor: ƒ}
        shout: ƒ (xssss)
        constructor: ƒ Animal(name,sound)
        __proto__:
        shout: ƒ ()
        genO2: ƒ ()
        constructor: ƒ Plants()
        __proto__: Object
    */
    dog.shout() // pipi wang!wang!
    cat.shout() // mimi miao~miao~
    cat.genO2() // mimi generates oxygen.

summary

  • Use Object.create, Animal. The prototype will point to an empty object whose prototype properties point to the prototytpe of Plants. So we can no longer access the properties in Animal's original prototypoe. Object. The use of create also highlights direct reassignment.
  • Use Object.setPrototypeOf will use Animal. The prototype will point to the original prototype of Animal, then the prototype of the prototype will point to the prototytpe of Plants. So we're going to visit Animal first, then plants.
  • It is better to use setPrototype when delegating between the two prototypes, Object.create is better for quickly delegating an object without a native prototype.

Keywords: Javascript ECMAScript

Added by psionicwrath on Sun, 06 Feb 2022 19:26:57 +0200