Where does this point? Understand this point

preface

js has flexible this direction and various usage scenarios. Although the concept is basic, it is very important. Many hidden bug s come from it. It is very necessary to understand the direction of this. If you can master this skillfully, the code will be more concise and elegant

Who does this point to?

There is a most popular saying about who this points to
this points to whoever calls it
This is not a big problem, but it is not particularly comprehensive, but generally speaking, its direction is right. The direction of this is determined when calling, and this will point to the object calling it in the context. This is the most basic usage

const person = {
	name:'xz',
	sayName:function(){
		console.log(this.name)
	}
}
person.sayName()

This is no problem and very easy to understand, but if you write it differently

var person = {
	name:'xz',
	sayName:function(){
		console.log(this.name)
	}
}
var name = 'jack'
var sayName = person.sayName
sayName()

jack will actually log out here, and a concept will be involved here
When the function is not explicitly or implicitly called, this points to undefined in strict mode and window in non strict mode
That is, when sayName is called directly, its this points to the window, so it log s out the name hanging under the window, that is, 'jack'

Let's take another look at the topic of the upgraded version. If you understand it, there will be no problem with the context call of this

const o1 = {
	text:'o1',
	fn:function(){
		return this.text
	}
}
const o2 = {
	text:'o2',
	fn:function(){
		return o1.fn()
	}
}
const o3 = {
	text:'o3',
	fn:function(){
		var fn = o1.fn
		return fn()
	}
}
console.log(o1.fn())
console.log(o2.fn())
console.log(o3.fn())

The answer is o1, o1, undefined
The first is the simplest
Second O2 FN in fact, the final implementation is o1 FN, so o1
The third is actually a streaking call, so it points to the window and is ultimately undefined

call, apply, bind

call, apply and bind are all used to change the direction of this, but what are the differences between them
call is similar to apply. It calls function after binding this. bind returns a function that is bound well. It needs to be manually invoked.
Some basic usage will not be repeated here

const target = {name:'xz'}
const fn = function(arg1,arg2){
    console.log(arg1,arg2)
    console.log(this.name)
}
// The following three methods are equivalent. Finally, parameter 1, parameter 2 and xz will be log ged
fn.call(target,'Parameter 1','Parameter 2')

fn.apply(target,['Parameter 1','Parameter 2'])

fn.bind(target,'Parameter 1','Parameter 2')()

Of course, the questions about the three swordsmen in the interview are no longer limited to how to use them, but how to realize one. It's not difficult to think carefully, FN Bind (obj) actually needs to change the calling method into obj FN, here is a simple bind implementation

function myBind (){
    // this is the method that needs to be bound
    const me = this
    // Convert parameters to arrays
    var args = Array.from(arguments)
    // The first parameter is the context to bind
    const ctx = args[0]
    // The rest are the parameters of the function
    const fnArgs = args.slice(1,args.length)
    // Returns the bound function
    return ()=>{
        // Use symbol as the key to avoid overwriting the original attribute
        const fn = Symbol('fn')
        // Add this function as an attribute to the context that needs to be bound
        ctx[fn] = me
        // Call function
        ctx[fn](...fnArgs)
        // Remove the property after calling
        delete ctx[fn]
    }
}
Function.prototype.myBind = myBind
const obj1 = {name:'xz'}
function fn(age){
    console.log(`Hi,I am ${this.name},I am ${age} years old`)
}
fn.myBind(obj1,18)()
// Hi,I am xz,I am 18 years old

A suggested bind is completed. If it's call and apply, you don't need to return a function. Just mount the attribute internally, call the function, and then delete the attribute. As long as the three swordsmen master one, the other two will naturally be similar

Constructor's this

When it comes to constructors, we have to mention the new keyword, which are inseparable
What did new do? There are four main things

  1. Create a new object
  2. Point the constructor's this to the new object
  3. Add properties, methods, etc. to this object
  4. Returns the new object

When you understand what new does, it will be clear at a glance where the constructor's this points, and it will point to the newly constructed object of the constructor

function Foo(){
    this.name = 'xz'
}
const me = new Foo()
console.log(me.name) // xz

However, we should pay attention to a special case: the constructor returns. This case is divided into two cases
Returns an object or does not return an object

function Foo1(){
    this.name='xz'
    const o = {name:'zed'}
    return o
}

function Foo2(){
    this.name = 'xz'
    return 1
}

const me1 = new Foo1()
const me2 = new Foo2()
console.log(me1.name)
// zed
console.log(me2.name)
// xz

You can see that if an object is returned, this will point to the returned object
If the object is not returned, this still points to the instance of the construct

this of the arrow function

The arrow function itself does not have this, and its this depends entirely on the outer scope

const foo1 = {
    fn:function(){
        setTimeout(function(){
            console.log(this)
        })
    }
}
const foo2 = {
    fn:function(){
        setTimeout(() => {
            console.log(this)
        })
    }
}
foo1.fn()
// window
foo2.fn()
// foo2

The simple arrow function this point is very simple, but it is not so easy to determine if we integrate various situations and combine the priority of this

this priority

First, consider the following questions

function foo(){
    console.log(this.a)
}
const obj1 = {
    a:1,
    foo
}
const obj2 = {
    a:2,
    foo
}
obj1.foo.call(obj2)

This will eventually log 2. Why not 1? This involves the issue of priority
Generally, we call binding based on call, apply, bind, new and other keywords as explicit binding, and bind this based on call relationship as implicit binding
The order of priority is implicit binding < call, apply, bind < New < arrow function this
Therefore, obj2 bound by call overrides obj1 bound implicitly
Look at another one

function Foo(a){
    this.a=a
}
const obj = {}
const bar = Foo.bind(obj)
bar(2)
console.log(obj.a)

const baz = new bar(3)
console.log(baz.a)

2 and 3 will be log ged here
First, bind this of the function as an obj object through bind, and then assign a value, so obj A is 2
However, when it is constructed through new as a constructor, the priority of new is greater than bind, so its point becomes the object he constructs

Look at the last one

function foo(){
    return ()=>{
        console.log(this.a)
    }
}
const obj1 = {
    a:2
}
const obj2 = {
    a:3
}
const bar = foo.call(obj1)
bar.apply(obj2)

Finally, 2 will be output here. foo is bound to obj1 through call, and the returned arrow function this is bound to obj1 at the same time
At this time, it is impossible to change this of the arrow function through apply. Because the priority of the arrow function is the highest, this still executes obj1 and finally log s 2

Summary

There are many uses of this, and it is really not easy to master it thoroughly. However, as long as you can understand its binding mode and priority, you can also find the specific direction of this in any complex scene. The flexibility of this makes it impossible for him to memorize by rote, and more emphasis is on understanding

Keywords: Javascript bind

Added by patch2112 on Thu, 20 Jan 2022 15:41:57 +0200