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
- Create a new object
- Point the constructor's this to the new object
- Add properties, methods, etc. to this object
- 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