this keyword you don't know

I. What is this

The value of this: The current execution code environment object, this direction does not depend on where it is created, completely depends on where the function is called, this can not be assigned during execution, and the value of this may be different each time the function is called.

Value of this

In a global environment (outside of any function call), this value is a global object (in browsers, window s objects, node s, global objects)

In the internal environment of a function, the value of this depends on how the function is called.

III. this Pointing Rule

Summarize a simple principle: this always points to the object that calls it last.

  • Function binding

this points to global objects when calling functions directly

var name = 'Window.name'
function foo() {
  console.log('this',this.name)
}
foo() // Window.name

Note: If the strict pattern is used, the global object is undefined

var name = 'Window.name'
function foo() {
  'use strict'
  console.log('this',this.name)
}
foo() // Uncaught TypeError: Cannot read property 'name' of undefined
  • Implicit binding (method call)

When a function is called as an object's property, it belongs to implicit binding. At this point, this points to the object of the function.

var obj = {
    num:0,
    add:function() {
      console.log('this',this) // this {num: 0, add: ƒ}
      this.num +=1
    }
}
obj.add()
console.log('obj',obj) // obj {num: 1, add: ƒ}

Look at the following chestnut:

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

Declare an obj object, although assigning a reference to obj.foo to res, but in fact res() is an unmodified function call (in the case of the first function binding), so the value printed at this time is ahwgs

Now let's make some changes:

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

This is the normal implicit call, where this is obj itself

Looking at a chestnut:

var name = 'ahwgs'
function doFoo(fn) {
    console.log('fn',fn)
    fn() //f(){console.log('name',this.name)}  fn=obj.foo
}
var obj = {
    name:'aaa',
    foo:function() {
      console.log('name',this.name)
    }
}
doFoo(obj.foo) // name ahwgs

At this point, obj.foo is passed as a parameter to doFoo, which is actually called doFoo. This point to the global object, so the print is ahwgs.

The last chestnut:

var name = 'ahwgs'
function foo() {
    var name = 'aaa'
    fn()
    function fn() {
      console.log('name',this.name)
    }
}
foo() // ahwgs

In the first sentence, this points to the object to which the function belongs, so fn points to the global object.

  • Explicit binding

Explicit binding using call/apply/bind method

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

var obj = {
    name:'obj'
}

foo.apply(obj) // name obj
foo.call(obj)// name obj
foo.bind(obj)()// name obj

At this point, this pointer is explicitly bound to obj, and after that, no matter how the function is called, obj will always be bound to this in foo.

  • new binding

Functions called by new keywords belong to the new binding mode. The this keyword then points to the newly created object.

function User(name,age) {
    this.name = name
    this.age = age
    this.getInfo = function() {
      console.log('info',this.name + '--->'+this.age)
    }
}
var user = new User('ahwgs',20)
console.log(user) // User {name: "ahwgs", age: 20, getInfo: ƒ}
console.log(user.getInfo()) // info ahwgs--->20
  • Summary:
  1. Is the function a new binding? If so, this points to the newly created object
  2. Is the function explicitly bound or hard bound by call/apply/bind? If so, this points to the specified object.
  3. Is the function implicitly invoked in a context object? If so, this binds the context object.
  4. If none of the above is true, the default binding is used. If in strict mode, it is bound to undefined, otherwise it is bound to the global window object.
  5. Remember: this always points to the object that last called it

4. How to Change this Direction

  • Use the ES6 arrow function

This of the arrow function always points to this when the function is defined, not when it is executed. There is no this binding in the arrow function. Its value must be determined by looking up the scope chain. If the arrow function is included by the non-arrow function, this binding is the latest non-arrow function. Otherwise, this is undefined.

Look at a chestnut:

var name = 'ahwgs'
var obj = {
    name:'aaa',
    foo1:function() {
      console.log('this.name',this.name)
    },
    foo2:function() {
      setTimeout(()=>{
        this.foo1()
      },100)
    }
}
obj.foo2() // aaa

Since the arrow function is used in the timer, the arrow function is not used in the previous level, so this is bound to the last level of non-arrow function, here, obj.

  • Within the function const self = this
var name = 'ahwgs'
var obj = {
    name:'aaa',
    foo1:function() {
      console.log('this.name',this.name)
    },
    foo2:function() {
     var self = this
     setTimeout(function() {
       self.foo1()
     })
    }
}
obj.foo2() // aaa

Define a variable that changes the current this pointing to self, so that when foo1 is called, this pointing to obj is the object

  • Use apply, call, bind

Let's first look at how these three functions are used:

  1. apply
function.apply(obj, [param1,params2,...])
// obj: this to bind
// The second parameter: an array of classes or arrays, passed in as a function parameter
// Immediate implementation
  1. call
function.call(obj, param1, param2, ...)
// obj: this to bind
// The second parameter: the parameters of the function, separated by commas
// Immediate implementation
  1. bind
function.bind(obj, param1, param2, ...)
// obj: this to bind
// The second parameter: the parameters of the function, separated by commas
// Returns a function

Here are three ways to modify this pointing

  1. Use apply
var name = 'ahwgs'
var obj = {
    name:'aaa',
    foo1:function() {
      console.log('this.name',this.name)
    },
    foo2:function() {
      setTimeout(function() {
        this.foo1()
      }.apply(obj),100)
    }
}
obj.foo2() // aaa
  1. Using call
var name = 'ahwgs'
var obj = {
    name:'aaa',
    foo1:function() {
      console.log('this.name',this.name)
    },
    foo2:function() {
      setTimeout(function() {
        this.foo1()
      }.call(obj),100)
    }
}
obj.foo2() // aaa
  1. Using bind
var name = 'ahwgs'
var obj = {
    name:'aaa',
    foo1:function() {
      console.log('this.name',this.name)
    },
    foo2:function() {
      setTimeout(function() {
        this.foo1()
      }.bind(obj)(),100)
    }
}
obj.foo2() // aaa

Note: bind is different from the other two methods because it returns a function, so we () need to call it.

  1. Using null/undefined as a parameter of bind/call/apply
var name = 'ahwgs'
var obj = {
    name:'aaa',
    foo1:function() {
      console.log('this.name',this.name)
    },
    foo2:function() {
      setTimeout(function() {
        this.foo1()
      }.call(null),100)
    }
    }
obj.foo2() //Uncaught TypeError: this.foo1 is not a function

If null/undefined is used as a parameter, it will be ignored when it is called, so when obj.foo2() is called, this points to the global object, but there is no foo2 function in the global object, so an error is reported.

  • New instantiates new objects

See the new binding example above.

summary

  • this in js refers to the allowable context, unlike the back-end language
  • this is not a one-size-fits-all, it changes with the environment.
  • this is different between strict mode and non-strict mode.
  • There are many ways to modify the directions of this
  • This article is first published in: this keyword you don't know

Tags: Javascript

Posted on Sat, 07 Sep 2019 04:47:09 -0700 by gca07738