This week to learn this, this abuse me thousands of times, I treat this as the first love, every time I see this will, is to point to its caller, as soon as I do the problem will be abandoned, this time dig root, thoroughly understand the mechanism of this

origin

Why this

Instead of using this, pass a context object explicitly

function identify(person){
  return person.name
}
​
function speak(person){
  var greeting = "Hello , I am " + identify(person)
  console.log(greeting)
}
​
const p1={
  name:"ABC"
}
const p2={
  name:"DEF"
}
​
identify(p1) // ABC
speak(p2) // Hello , I am DEF
Copy the code

With this, object references can be passed implicitly, so the API can be designed to be cleaner and easier to reuse

function identify(){
  return this.name
}
const p1={
  name:"ABC"
}
​
identify.call(p1) // ABC
​
Copy the code

This misunderstanding

  1. This points to itself (this is wrong!!)

    function fn(num){ console.log(num) this.count++ } fn.count = 0; for(var i=0; i<5; i++){ fn(i) } console.log(fn.count) //0====>?????Copy the code

    This. Count++ is the same as foo.count++. It should have printed 5, but it printed 0.

    The solution

    • Create a global count attribute (using global lexical scope, bypassing this)

      var count =0; function fn(){ this.count++ } for(var i=0; i<5; i++){ fn(i) } console.log(this.count) //5Copy the code
    • Create a function count attribute (using the lexical scope of the fn variable, bypassing this)

      function fn(){ fn.count++ } fn.count =0; for(var i=0; i<5; i++){ fn(i) } console.log(fn.count) //5Copy the code
    • Force this to point to fn

      function fn(){ this.count++ } fn.count=0; for(var i=0; i<5; i++){ fn.call(fn,i) } console.log(fn.count) //5Copy the code
  2. Scope of this

    This refers to the scope of the function, which is incorrect. In no case does this refer to the lexical scope of the function

    function foo(){
      var a = 2;
      this.bar()
    }
    function bar(){
      console.log(a)
    }
    foo() //Uncaught ReferenceError: a is not defined
    Copy the code

    Lexical scope

    window
      foo
        a:
        bar:function
    Copy the code

    Whenever you want to mix this with lexical lookup, remind yourself that this is not possible.

  3. What is this

    This is bound at run time, and its context depends on various conditions at the time the function is called.

    The binding of this has nothing to do with the position of the function declaration, except how the function is called.

    This refers neither to the function itself nor to the lexical scope of the function.

    As a review of JavaScript execution notes, js binds this at execution time (function calls).

Full resolution

The position

Call location: where a function is called in code (not where the function is declared)

function baz(){
  console.log('baz')
  bar()
}
function bar(){
  console.log('bar')
  foo()
}
function foo(){
  debugger
  console.log('foo')
}
baz();
Copy the code

In the Call Stack, this points to the window

Const obj = {name:' test ', baz:function(){console.log('baz') this.bar()}, const obj = {name:' test ', baz:function(){console.log('baz') this.bar()}, bar:function(){ console.log('bar') this.foo() }, foo:function(){ console.log(foo) } } obj.baz()Copy the code

In the Call Stack shown here this points to OBj

const person = { name:'ddd' } function speak(){ console.log(this.name) } speak(); Call (person) // This points to personCopy the code

Binding rules

How does the location of a function call during execution determine what this refers to?

1. Default binding

Independent function call

  • Non-strict mode, this refers to window

    function foo(){
      console.log(this.a) 
    }
    var a = 2;
    foo()//2
    Copy the code
  • Strictly, this refers to undefined

    function foo(){
        "use strict"
      console.error(this.a) 
    }
    var a = 2;
    foo()//VM156:3 Uncaught TypeError: Cannot read property 'a' of undefined
    Copy the code

2. Implicit binding

Whether the function call location has a context object, or whether it is contained or owned by an object

function foo(){
  console.log(this.a)
}
var a = 'window'
​
var obj ={
  a:'obj',
  foo:foo
}
obj.foo() //'obj'
Copy the code

The call location makes the obj context reference the function, and the implicit binding rule refers this to the current context object obj

Only the last, or last, level in the object attribute invocation chain plays a role in the invocation location

function foo(){
  console.log(this.a)
}
var a = 'window'
​
var obj1 ={
  a:'obj1',
  foo:foo
}
var obj2 ={
  a:'obj2',
  obj1:obj1,
  foo:foo
}
obj2.obj1.foo() // 'obj1'
Copy the code

Implicit loss problem

  • Foo is actually a reference to foo. Bar is called without any modifiers, hiding the application of the default binding rules

    function foo(){
      console.log(this.a)
    }
    var a = 'window'
    ​
    var obj1 ={
      a:'obj1',
      foo:foo
    }
    var bar = obj1.foo
    bar() // window
    Copy the code
  • Function passed as an argument, which is actually an implicit assignment, doFn(obj.foo) var fn = obj.foo; fn() ; Fn is called without any modifiers, hiding the application of default binding rules

    function foo(){ console.log(this.a) } var a = 'window' var obj ={ a:'obj', Foo :foo} function doFn(fn){fn()// call position} doFn(obj.foo) // windowCopy the code
  • The callback function this is missing

    function foo(){
      console.log(this.a)
    }
    var a = 'window'
    ​
    var obj ={
      a:'obj',
      foo:foo
    }
    setTimeout(obj.foo,100) //'window'
    Copy the code

    SetTimeout internally implements pseudocode that actually assigns obj.foo to fn, which refers to foo’s own function

    Function setTimeout(fn,delay){function setTimeout(fn,delay){Copy the code

3. Display the binding

Review the above implicit binding, we must be within an object contains a pointer to a function attributes, and through the object attribute to indirect references to call a function, thus the implicit binding to the context object, so if we don’t through the way of object contains function, and want to force the function in object, then need to display the binding, This can be done through the call() and apply() methods of functions.

call(obj,arg1,arg1,…) ,apply(obj,[arg1,arg2,..] ), their first argument is the object, which is ready for this and is bound to this when the function is called. In this way you can point directly to the object bound to this, which is called the display binding

function foo(){
  console.log(this.a)
}
var a = 'window'
​
var obj ={
  a:'obj',
}
foo.call(obj)
Copy the code

Can display binding solve implicit loss problems?

function foo(){
  console.log(this.a)
}
var a = 'window'
​
var obj ={
  a:'obj',
}
foo.call(obj) // 'obj'
setTimeout(foo,100)// 'window'
Copy the code

Foo. Call (obj) refers this to obj, but shows that the binding is bound to this when called. After the function is called, this is no longer under your control, but there is no guarantee that the function is passed as an argument to another function. How other functions call this function.

For example, you use a utility function, this function is asynchronous, need you to do something at the end of the asynchronous operation, the operation of the traditional is through the callback function, a tool such as functions of the asynchronous operation completed, call you transfer function, but your own function used in this, and expect this point to a particular object, Use call or apply for binding, to display the binding is the binding when a function call, this time your function has been carried out, and desired is asynchronous execution tools function call again after their functions, and tool functions inside how to call your own, we don’t know, can be called directly, We can show bind this, at which point the reference to this is out of our control.

function myFun(){ console.log(this.name) } var name = 'window' var p1 = { name:'p1' } var p2 = { name:'p2' } myFun.call(p1); Function util(fn){// function util(fn){// function util(fn){ } util(myFun);} util(myFun);} util(myFun)Copy the code

Because we know that the function as a parameter or the callback is passed, we are not clear how the internal call, it is this binding is not controlled by us, to solve the problem is when the function is passed to this binding let function call does not occur at the same time, so no matter how tools function calls your function, This points to all definite

Function myFun(){console.log(this.name)} var name = 'window' var p1 = {name:'p1'} var p2 = {name:'p2'} // function myFun(){console.log(this.name)} var name = 'window' var p1 = {name:'p1'} var p2 = {name:'p2'} // Function bindFn(obj){return function(){myfun.call (obj)}} var fn = function util(fn){setTimeout(fn,100) BindFn (p1) // returns a function that avoids being called util(fn) directlyCopy the code

Create a function wrap that binds this to the desired object each time the function executes, and returns a function that avoids being called directly.

Bind source code: developer.mozilla.org/zh-CN/docs/…

if(! Function.prototype.mBind)(function(){ var slice = Array.prototype.slice Function.prototype.mBind = function(){ var self = this; var args = slice.call(arguments,1) var target = arguments[0] if(typeof self ! =='function'){ throw new TypeError('Function.prototype.bind - ' + 'what is trying to be bound is not callable'); } return function(){ return self.apply(target,args.concat(slice.call(arguments))) } } })() var person = { name:'ddd', say:function(){ console.log(this.name) } } var say = person.say; var b =say.mBind(person) b()Copy the code

4. The new binding

Using new to call a function, or when a function is called, the following operations are performed automatically

(1) Create a new object

(2) This new object performs the [[Prototype]] connection

(3) The new object is bound to the function call’s this

(4) If the function returns no other object, the function in the new expression automatically returns the new object

Take a look at the result of the new operation

function Person(name,age){
  this.name = name ;
  this.age = age;
}
var  p = new Person('sss',122)
console.log(p)
Copy the code

Self-implementation:

Function newOperator(ctor){// If (typeof ctor! =='function'){ throw 'newOperator function the first param must be a function'; Create a new object var obj = {} // 2. Create [[prototype]] link obj.__proto__ = ctor. Prototype // 3. Bind this to a new object var args = Array. The prototype. Slice. The call (the arguments, 1); var result = ctor.apply(obj,args) // 4. Var isObject = typeof result === 'object' && result! == null; var isFunction = typeof result === 'function'; return isObject || isFunction ? result : obj; } function Person(name,age){ this.name = name ; this.age = age; } var p1 = newOperator(Person,"ddd",'123') console.log(p1)Copy the code

Step 1 and step 2 can use es6’s object.create (ctor. Prototype) to create a new Object and attach it to [[prototype]]

priority

The this binding has four rules, so what is the priority if more than one is applied at the same time?

The default binding, of course, has the lowest priority

Implicit binding and display binding
function say(){ console.log(this.name) } var p1 ={ name:'p1', say:say } var p2 = { name:'p2', say:say } p1.say.call(p2); // p2 p1.say.call(p1)// p1Copy the code

This shows that binding takes precedence over implicit binding

New binding and implicit binding
function foo(name){
    this.name = name
  }
  var p1 = {
    foo:foo
  }
  var p2 ={}
​
  p1.foo('p1') 
  console.log(p1.name) //p1
​
  p1.foo.call(p2,'p2') 
  console.log(p2.name) //p2
​
  var p3 = new p1.foo('p3')
  console.log(p1.name) //p1
  console.log(p3.name) //p3
​
Copy the code

The new binding takes precedence over the implicit binding

conclusion

  1. Is there a new call in the function? If this is binding to the newly created object

    var p = new Person()
    Copy the code
  2. Function called by call, apply, bind? If it is this, it points to the bound object

    foo.call(p)
    Copy the code
  3. Is the function called in the context of an object (called implicitly)? If it is this, it points to the context object

    var obj = {
      name:'dd',
      foo:foo
    }
    obj.foo()
    Copy the code
  4. If none of the above is true, this is bound to undefined by default, while non-strict this is bound to Window

extension

Arrow function

Instead of using the four rules for this, arrow functions determine this based on the outer function or global scope

The call to realize

Function.prototype.myCall = function(context){
    var context = context ? context : window
​
    context.fn = this
    var args = Array.prototype.slice(arguments,1)
    var result = context.fn(args)
​
    delete context.fn
    
    return result
  }
  
Copy the code

Apply to realize

Function.prototype.myApply = function(context) { context = context ? Object(context) : window context.fn = this let args = [...arguments][1] if (! args) { return context.fn() } let result = context.fn(args) delete context.fn; return result }Copy the code

practice

var obj = { a: 10, b: this.a + 10, fn: function() { return this.a; } } console.log(obj.b); //NaN this.a this points to window this.a = undefined console.log(obj.fn()); // 10 this points to obj implicit binding rulesCopy the code
var a = 20; var obj = { a: 10, getA: function() { return this.a; } } console.log(obj.getA()); Var test = obj.geta; console.log(test()); // this points to the global default binding in test after assignmentCopy the code
var a = 5; // This refers to window function fn1() {var a = 6; console.log(a); console.log(this.a); } function fn2(fn) {fn = f1 var a = 7; fn(); } var obj = {a: 8, getA: fn1} fn2(obj.geta); // a=6; 5 This refers to the global scope WindowCopy the code
function fn() { // "use strict"; // In strict mode: disallow this from pointing to the global object console.log(this); //undefined var a = 1; Var obj = {a: 10, c: this.a + 20} return obj.c; } console.log(fn()); //Cannot read property 'a' of undefinedCopy the code
function Person(name, age) { this.name = name; this.age = age; console.log(this); Person} person.prototype. getName = function() {console.log(this); Person} var p1 = new Person("test", 18) p1.getName()Copy the code
Var obj = {foo: "test", fn: function() {var mine = this; console.log(this.foo); //"test" console.log(mine.foo); //"test" // The internal function of the method this points to window (function(){console.log(this.foo); //undefined console.log(mine.foo); //"test" mine refers to the method's local variable mine, finally pointing to obj object})()}} obj.fn();Copy the code
function foo() { console.log(this.a); } var a = 2; var o = { a: 3, foo: foo } var p = { a: 4, } o.foo(); // 3 this refers to o (p.foo = o.foo)(); 2 This self-calling function refers to the global p.foo = o.foo; p.foo(); 4 // this points to pCopy the code
function foo() { console.log(this.a); } var obj1 = { a: 3, foo: foo }; var obj2 = { a: 5, foo: foo }; obj1.foo(); //3 implicitly bind obj2.foo(); //5 implicitly bind obj1.foo.call(obj2)//5 show bind obj2.foo.call(obj1)//3 Show bindCopy the code