2021-10-21 deep JavaScript advanced syntax

*Note: This article is to learn the course notes of in-depth JavaScript Advanced Grammar codewhy new course. It is recommended to find the teacher's course in Tencent class for learning.

1,GO AO VO

1.1. The VO in the function is called AO, and the global VO is called GO (earlier, ES5 was not included before ES5): variables and functions declared in the source code are added as an attribute of VO

1.2. The new ECMA specification is changed to Variable Environment GE AE VE: variables and functions declared in the execution code are added to variable environment (VE) as an Environment Record

2. Code parsing

2.1 When the code is parsed (the definition statements of functions and variables are parsed, rather than the execution of functions or the assignment statements of variables), the v8 engine will help us create an object (· globalobject - > GO ·)· GO · is created in advance before all code is executed.

2.2. When the function is parsed, a function object will be generated, which contains two things. One is the parent scope of the function, and the other is the executor of the function. Therefore, the parent scope of a function is related to its definition location and independent of its call location.

var message = 'hello global';

// Defined globally
function foo() {
	// Find the message attribute in the internal scope of foo function, but it is not found; Then find the parent scope of foo. The parent scope is GO, and the result will be printed as "hello global"
	console.log(message); 
}

function bar(){
 var meaasge = 'hello bar';
 // Call locally
 foo();
}

bar();

The execution result is printed as: hello global

2.3. What to do when parsing code:
Mount the variable message to GO as an attribute. Note that the code here has not been executed, and the value of message is undefined;
Mount the function foo as an attribute on GO, and a function object will be generated, which contains two things. One is the parent scope of the function and the other is the executor of the function. Note that the code foo() has not been executed here.

var message = 'hello world';
function foo() {
	console.log('hello foo');
}
foo();

3. Execute code

3.1. In v8, in order to execute code, there will be an execution context stack (ecstack) (function call stack) inside the v8 engine.

3.2. Because we execute the global code, in order for the global code to execute normally, we need to create a Global Execution Context (only when the global code needs to be executed).
ยท
3.3. When executing global code, a global execution context stack will be created, and the v8 engine will help us create an object VO, where VO points to GO.

3.4. When executing a function, the v8 engine will help us create an object vo. At this time, VO points to AO (the local scope of the corresponding function, which saves the variables defined in the function, that is, temporary variables). After the code inside the function is executed, the function will pop up the stack (that is, the execution context stack corresponding to the function will be destroyed), and the AO object created when the function is executed will be destroyed.

4. In js, the function is a first-class citizen

The use of representation functions is very flexible. Function can be used as the return value or parameter of other functions. js syntax allows redefinition of functions inside functions. Export closure.

5. Closure memory leak

If unused data is not destroyed in memory, it is called a memory leak.

function foo() {
	var name = 'foo';
	var age = 18;
	function bar() {
		console.log(name);
		// From the perspective of js engine optimization, although the AO object of foo function is not destroyed due to closure, the age attribute on the AO object is actually cleared and released, because the age attribute is not used in the bar function
		// console.log(age);
	}
	return bar;
}

var fn = foo();
fn();

There will be a memory leak. Reason: the foo function has been executed, but the AO object corresponding to Foo and the function object generated during bar function parsing still exist in memory.
The reason is that the fn attribute on the global GO object also retains a reference to the bar function object, and the parentScope saved in the bar function object points to the AO object of foo, and the bar attribute in the AO object of foo points to the bar function object.

Solution:

var fn = null;

Reason: cancel the reference of fn attribute on GO to the bar function object, so that the next time the garbage collection mechanism starts, since there is no path from the root object GO to the bar function object, even if the bar function object and the AO object of foo are still referencing each other, they will also be recycled by the garbage collection mechanism.

6. The direction of this

6.1. Under the global scope

6.1.1 browser: point to window(globalObject)
6.1.2 node environment: point to {}

6.2 this in the function is dynamically bound and is determined during execution.

this in the function has nothing to do with the position defined by the function, but has something to do with the way the function is called and the position of the call.

6.2.1 binding rule 1: default binding

Default binding: independent function call - > point to window

Example 1:

function foo() {
	console.log(this); 
}

foo();

Example 2:

function foo1() {
	console.log(this); 
}

function foo2() {
	console.log(this); 
	foo1();
}

function foo3() {
	console.log(this); 
	foo2();
}

foo3();

Example 3:

var obj = {
	name: 'Tom',
	foo: function() {
		console.log(this);
	}
};

var fn = obj.foo;
fn(); // window

Example 4:

function foo() {
	function bar() {
		console.log(this);
	}
}

var fn = foo();
fn();

6.2.2 binding rule 2: implicit binding.

That is, in its call location, it is a function call initiated through an object. Implicit binding: object fn () - > object object will be bound to this in fn function by js engine.

Example 1:

function foo() {
	console.log(this); 
}

var obj = {
	name: 'Tom',
	foo: foo
};

obj.foo(); // Point to obj object

Example 2:

var obj1 = {
	name: 'obj1',
	foo: function() {
		console.log(this);
	}
};

var obj2 = {
	name: 'obj2',
	bar: obj1.foo
};

obj2.bar(); // Point to obj2

6.2.3 binding rule 3: explicit binding.

apply/call/bind can specify the binding object of this.

6.2.3.1,call/apply

function foo() {
	console.log(this); 
}

var obj = {
	name: 'obj'
};

foo.call(obj); // Point to obj
foo.apply(obj); // Point to obj
foo.apply('aaa'); // Point to String constructor

6.2.3.2 difference between call and apply: different parameter transfer methods

function sum(num1, num2, num3) {
	console.log(num1 + num2 + num3);
}

sum.call(null, 1, 2, 3); // Multiple parameter comma splicing and passing
sum.apply(null, [1, 2, 3]); // Multiple parameters are passed through an array

6.2.3.3,bind

function foo() {
	console.log(this); 
}

var newFoo = foo.bind('aaa');
newFoo(); // Point to aaa

The default binding conflicts with the display binding binding: the display binding priority is greater than the default binding.

6.2.4. new binding

When we call a function through new, it will automatically generate an object for us. After the object is generated, this will point to the object. When we add an attribute to this in the function, we will add this attribute to the generated object by default. If the function does not return other objects (the object or array wrapped with {}, which means that even if it explicitly returns such as string and null equivalent, printing p1 is still the object returned by default), the function will return the generated object by default.

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

var p1 = new Person('tom', 18);
console.log(p1.name, p1.age);

7. this other supplement

7.1. Some default bindings inside the browser

Generally, when calling setTimeout or setInterval timer methods, this in the timer callback function must point to window.

setTimeout(function() {
  console.log(this); // window
}, 100);

This is equivalent to that when the setTimeout method is implemented inside the v8 engine, its callback function is called directly, that is, the default binding.

// Pseudo code
setTimeout(fn, duration) {
	fn(); // Default binding
}

7.2. Some implicit bindings inside the browser

<div class='box'>111</div>
var boxDom = document.querySelector('.box');

boxDom.onclick = function() {
  console.log(this); // Point to the boxDom object
}

Equivalent to the internal implementation of the v8 engine, the clicking method of calling div binding is through obj.. FN (), i.e. implicit binding.

// Pseudo code
boxDom.onClick();

7.3. Some explicit bindings inside the browser

<div class='box'>111</div>
var boxDom = document.querySelector('.box');

boxDom.addEventListener('click', function() {
  console.log(this); // Point to the boxDom object
});

Equivalent to the internal implementation of the v8 engine, the clicking method of calling div monitoring is through fn.. Call (boxdom), that is, explicit binding.

7.4. Array: this in forEach/map/filter/find, etc

By default, this in the callback function of the first parameter of the array method points to window.

var names = ['aaa', 'bbb', 'ccc'];
names.forEach(function(item) {
  console.log(item, this); // window
});

You can pass in the second parameter to the array method (thisArg?), Changing the this point in the callback function of the first parameter of the array method is equivalent to explicit binding.

var names = ['aaa', 'bbb', 'ccc'];
names.forEach(function(item) {
  console.log(item, this); // String{'aaa'}
}, 'aaa');

8. this binding rule priority

After learning the four rules, we only need to find out which rule is applied to the function call. However, if multiple rules are applied to a function call location, who has the higher priority?

8.1. The default binding has the lowest priority

8.2. Explicit binding priority > implicit binding priority

apply()/call() binding test:

var obj = {
  name: 'obj',
  foo: function() {
    console.log(this)
  }
}

obj.foo(); // Implicit binding, this points to obj object
obj.foo.call('aaa'); // Implicit binding and explicit binding exist at the same time. this points to String{'aaa'}

bind() binding test:

function foo() {
  console.log(this);
}

var obj = {
  name: 'obj',
  foo: foo.bind('aaa')
}

obj.foo(); // this points to String{'aaa'}

8.3. new binding priority > implicit binding priority

var obj = {
  name: 'obj',
  foo: function() {
    console.log(this);
  }
}

var f = new obj.foo(); // This points to foo{} this function object

8.4. new binding priority > explicit binding priority

Conclusion: the new keyword cannot be used with apply/call.
Therefore, here we test through new and bind:

function foo() {
  console.log(this);
}

var bar = foo.bind('aaa');
var obj = new bar(); // This points to foo{} this function object

8.5 conclusion

new binding > explicit binding (apply / call / bind) > implicit binding (obj. Fn()) > default binding (independent function call)

9. Special binding

9.1. Ignore explicit binding

apply/call/bind: when null/undefined is passed in, this is automatically bound to the global object window.

function foo() {
  console.log(this);
}

foo.apply(null); // this points to window
foo.apply(undefined); // this points to window

var bar = foo.bind(null);
bar(); // this points to window

9.2 indirect function reference

var obj1 = {
  name: 'obj1',
  foo: function() {
    console.log(this)
  }
};

var obj2 = {
  name: 'obj2'
}; // Note that a semicolon must be added here, otherwise when the js engine performs lexical analysis, it will think that the current statement is not finished and will report an error

// Equivalent to the js engine will
// {
//   name: 'obj2'
// }(obj2.bar = obj1.foo)() as a whole

(obj2.bar = obj1.foo)(); // First execute the assignment expression, then call the function and point to window

Note that the above code is different from (obj1.foo)(). Here (obj1.foo)() is equivalent to obj1.foo() Foo() is an implicit call

10. Arrow functionarrow function

  • The arrow function does not bind this and arguments attributes
  • The arrow function cannot be used as a constructor (it cannot be used with new, and an error will be thrown)

10.1 common abbreviations

  1. If there is only one parameter, () can be abbreviated
  2. If the function execution body has only one line of code, {} can also be omitted, and it will take the execution result of this line of code as the return value by default
var nums = [1, 2, 3];
nums.filter(item => item % 2 === 0);
  1. If an arrow function has only one line of code and returns an object, you can wrap a () outside {} at this time
var nums = [1, 2, 3];
var newArr = nums.map(item => ({
    id: item
}))

10.2 this direction in arrow function

The arrow function does not use the four standard rules of this (that is, it does not bind this, that is, the attribute this cannot be found in the internal scope of the arrow function), but determines this according to the external scope:

var foo = () => {
    console.log(this);
};

foo(); // window

var obj = { foo };
obj.foo(); // window

foo.call('abc'); // window

Application examples:

var obj = {
  data: [],
  getData: function() {
  	// Send the network request and put the result in the data attribute above
  	// Solution before arrow function
    // var _this = this;
    // setTimeout(function() {
    //   _this.data = ['a', 'b', 'c'];
    // }, 200)

	// After arrow function
    setTimeout(() => {
      this.data = ['a', 'b', 'c'];
    }, 200)
  }
};

obj.getData();

11. Manually implement the call/apply/bind method

11.1. Implementation of call() method

Function.prototype.hycall = function(thisArg, ...args) {
  // Here you can execute the called function (foo)
  // Question: how can I get which function executes hycall
  
  // 1. Get the function to be executed (principle: hycall() is implicitly bound when called, and this inside hycall() points to Hycall() (the object in front of it)
  var fn = this;
  
  // 2. Convert thisArg to object type (prevent it from passing in non object types)
  thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
  
  // 3. Call the function to be executed
  thisArg.fn = fn; // Bind the fn() function as an attribute to the parameter we passed in
  var result = thisArg.fn(...args); // Implicit binding, pointing this inside fn() to the passed in parameter (thisArg)
  delete thisArg.fn; // After calling the fn() function, you can delete this attribute if you do not need it
  
  // 4. Return the final result
  return result;
}

function foo() {
  console.log('foo Called',this)
}

function sum(num1, num2) {
  console.log('sum Called',this)
  return num1 + num2;
}

foo.hycall('123');

sum.hycall({}, 1,2,3);

11.2 implementation of apply() method

Function.prototype.hyapply = function(thisArg, argArray) {
  // 1. Get the function to be executed
  var fn = this;
  
  // 2. Handle the bound thisArg
  thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
  
  // 3. Call the function to be executed
  thisArg.fn = fn;
  argArray = argArray || [];
  var result = thisArg.fn(...argArray)
  delete thisArg.fn
  
  // 4. Return the final result
  return result
  
}

function foo() {
  console.log('foo Called', this)
}

function sum(num1, num2) {
  console.log('sum Called', this)
  return num1 + num2
}

// system call
// var result = sum.apply('abc', [20, 30])
// console.log(result)

// Self implemented call
foo.hyapply({});

var result = sum.hyapply('abc', [20, 30])
console.log(result)

11.3 implementation of bind() method

Function.prototype.hybind = function(thisArg, ...argArray) {
  // 1. Get the function to be executed
  var fn = this;
  
  // 2. Handle the bound thisArg
  thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
  
  function proxyFn(...args) {
    // 3. Put the function into thisArg and call it
    thisArg.fn = fn;
    // Special: merge two passed in parameters
    var finalArgs = [...argArray, ...args];
    var result = thisArg.fn(...finalArgs)
    delete thisArg.fn
    
    return result
  }
  
  return proxyFn
}

function foo() {
  console.log('foo Executed', this)
}

function sum(num1, num2, num3, num4) {
  console.log(num1, num2, num3, num4)
}

// var bar = foo.hybind('abc')
// var result = bar()
// console.log(result)

var newSum = sum.hybind('abc', 10, 20)
var result = newSum(30, 40)
console.log(result)

12,arguments

12.1 basic use of arguments

function foo(num1, num2, num3) {
  // Array like object (long like an array, essentially an object): arguments
  // console.log(arguments)
  
  // There are three common operations on arguments
  // 1. Get the length of the parameter
  console.log(arguments.length)
  
  // 2. Obtain a parameter according to the index value
  console.log(arguments[3])
  
  // 3.callee gets the function where the current arguments are located
  console.log(arguments.callee)
}

foo(10, 20, 30, 40, 50)

12.2. Class array to array: array prototype. slice. call()

Implementation of slice method inside js:

Array.prototype.hyslice = function(start, end) {
  var arr = this;
  // ...  Some processing of start and end parameters
  start = start || 0;
  end = end || arr.length
 
  var newArray = [];
  for (var i = start; i< end; i++) {
    newArray.push(arr[i])
  }
  return newArray
}

var newArray = Array.prototype.hyslice.call(['aaa', 'bbb', 'ccc'], 1, 3)
console.log(newArray)

12.3. Implementation: convert arguments into array

function foo(num1, num2, num3) {
  // 1.Array.prototype.slice converts arguments to array
  var newArr1 = Array.prototype.slice.call(arguments)
  console.log(newArr1)
  
  var newArr2 = [].slice.call(arguments)
  console.log(newArr2)
  
  // 2. Syntax of ES6
  var newArr3 = Array.from(arguments)
  console.log(newArr3)
  
  var newArr4 = [...arguments]
  console.log(newArr4)
}

foo(10, 20, 30, 40, 50)

Keywords: Javascript Front-end

Added by vinoindiamca on Tue, 18 Jan 2022 15:02:41 +0200