JavaScript summary 7.0—The most comprehensive and understandable explanation of this
The appearance of this can make the front-end code more readable and flexible to a certain extent, especially in the encapsulation of triggering events and objects and classes. Although we all know that this points to the problem is very important, it is difficult to really explain it clearly . So record my step-by-step understanding of this since I learned the front-end, I hope it will be helpful to everyone
this binding rule
1 default binding
1.Global Execution Environment
Whether in strict mode or not, in the global execution environment (outside any function body) this refers to the global object window
'use strict'
console.log(this); //window
2.Ordinary functions are called independently
In non-strict mode, this inside the function points to window
function f1(){
return this;
}
console.log(f1()===window);//true
In strict mode, this points to undefined
function f1() {
'use strict';
return this;
}
console.log(f1()); //undefined
PS: Subsequent code defaults to non-strict mode
3.The function is defined in the object, but called independently
Independent function call: call fn() directly, there is no object such as obj.fn() in front of the function fn and there is no call, apply, bind behind to change the point of this
example 1:
var obj = {
bar: function () {
console. log(this);
},
};
var baz = obj. bar;
baz(); //window
obj.bar(); // non-independent function call, this points to the boj object
example 2:
function foo(fn){
fn()
}
var obj = {
bar: function () {
console.log(this); //window
},
};
foo(obj. bar)
//this still points to window, and obj.bar returns a function, which actually calls fn() in the foo function. There is no implicit binding on the left and no explicit binding such as call on the right. It is an independent function call. point to window
4.Execute the function immediately
Immediately execute the function this to point to the global window
var name = 'a';
var obj = {
name: 'b',
func: (function () {
console.log(this.name,this); //a,window
})(),
};
5.Closure this problem
The information on the Internet almost all say that the this of the closure function returns window, but I don’t agree with it. The this point of the closure will also change in different code environments. The following is the code practice verification. When closing the closure, don’t blindly think that this points to window, only by analyzing the execution of the code can you draw the correct conclusion
//The closure of the nested function inside the function
function foo() {
function bar() {
console. log(this);
}
return bar;
}
var fn = foo();
fn(); // this points to window, which is equivalent to an independent function call
var obj = {
eating: fn,
};
obj.eating(); // Implicit binding, this points to the obj object
6.Higher order functions
function test1() {
console.log(this);
}
function test2() {
console.log(this);
test1();
}
function test3() {
console.log(this);
test2();
}
test3();
//this all points to window, because the calls of the three functions are essentially independent function calls
2. Implicit binding
Implicit binding means calling a function through an object:
object.fn() // The object object will be bound to this in the fn function by the js engine
conditions for implicit binding
: there is a reference to the function inside the calling object (eg a property whose value is a function)
It is through this reference that this is indirectly bound to this object. If there is no such reference, an error that the function cannot be found will be reported when the function is called
example1:
function foo() {
console. log(this);
}
var obj = {
bar: foo,
};
obj.bar();//obj object
example2:
var obj1 = {
name: "obj1",
foo: function() {
console.log(this)
}
}
var obj2 = {
name: "obj2",
bar: obj1.foo
}
obj2.bar() //obj2
////obj2.bar returns a function that prints this, implicitly bound to obj2
example3:
function foo() {
console.log(this); // obj2
}
var obj1 = {
name: 'obj1',
foo: foo,
obj2: {
name: 'obj2',
foo: foo,
},
};
obj1.obj2.foo(); // obj1.obj2 returns the obj2 object, and the foo function is called through obj1
//Implicit binding this points to the technique: look at the nearest left binding object when the function is called
3. Display bindings
-
Using call or apply (apply is an array, call is a parameter list) to explicitly bind the object pointed to by this is explicit binding
function foo() { console.log(this); } foo.call({ name: 'fang' }); //{name:'fang'}object foo.apply(window); //window foo.apply('ddl'); //String {'ddl'}
2.bind binding
If you want a function to always be explicitly bound to an object, you can use the bind method (return a new binding function without calling this function)
function foo() {
console.log(this);
}
var obj = {
name: 'fang',
};
var test = foo.bind(obj);
test(); //obj object
4. Binding of built-in functions
1.setTimeout
By default, the this of the timer points to window
setTimeout(function() {
console.log(this) // window
}, 2000)
2.Monitor click and other events
When a function is used as an event handler, its this points to the element that triggered the event
NOTE:Some browsers don't follow this convention when dynamically adding listeners using functions other than addEventListener
var btn = document.querySelector("button")
btn.addEventListener("click", function() {
console.log(this) //button
})
3.The second parameter after the array forEach/map/filter/find is passed into the function is the object pointed to by this
var names = ["a", "b", "c"]
names.forEach(function(item) {
console.log(this) //window
})
names.map(function(item) {
console.log( this) //String {'testtest'}
}, "testtest")
5. new binding
When a function is used as a constructor (using the new keyword), its this is bound to the new object being constructed
function Person(name) {
this.name = name
console.log(this);
}
var p1 = new Person("fang")//Person {name: 'fang'}
var p2 = new Person("xiao")//Person {name: 'xiao'}
If the return value of the constructor is a reference type object or array, the default object bound to this is discarded, and the return value in the constructor is returned
function C(){
this.a = 37;
}
var o = new C();
console.log(o.a); // 37
function C2(){
this.a = 37;
return {a:38};
}
var o2 = new C2();
console.log(o2.a); // 38,return{a:38};
function C3(){
this.a = 37;
return [1,2,3];
}
var o3 = new C2();
console.log(o3); // [1,2,3]
6. The priority of different binding rules
new binding > explicit binding (bind takes precedence over apply/call) > implicit binding (obj.foo()) > default binding (independent function call)
PS: The priority of new binding is higher than that of bind, and new binding, call and apply are not allowed to be used at the same time
Code test:
1. bind is higher than the default binding
function foo() {
console.log(this); //String {'abc'}
}
var obj = {
foo: bar,
};
var test = obj.foo.bind('cba');
test();
//Equivalent to
//var bar = foo.bind('abc');
//obj.foo();
2.new has higher priority than bind
function foo() {
console.log( this)
}
var bindFn = foo.bind("aaa")
bindFn() //String {'aaa'}
new bindFn() //foo {}
3. bind has higher priority than apply/call
function foo() {
console.log( this) //String {'aaa'}
}
var bindFn = foo.bind("aaa")
bindFn.call("bbb")
7. Special case of this binding rule
-
If in the display binding apply/call/bind, when
null or undefined
is passed in, the display binding will be invalid and this will be automatically bound to the global objectfunction foo() { console.log(this); } foo.apply(null); //window foo.apply('abc'); //abc foo.apply({}); //{} foo.apply(undefined); //window var bar = foo.bind(null); bar(); //window
-
Indirect function reference problem
(obj2.foo = obj1.foo)
The result of the assignment (obj2.foo = obj1.foo) is the foo function. After the assignment, the foo function is called directly (independent function call), using the default binding this to point to windowvar obj1 = { name: 'obj1', foo: function () { console.log(this); }, }; var obj2 = { name: 'obj2', }; // obj2.foo = obj1.foo // obj2.foo() //{name: 'obj2', foo: ƒ} (obj2.foo = obj1.foo)(); //window
this of the arrow function
The arrow function determines this according to the outer scope. It does not use the standard rules of this mentioned above. It is only related to the location and environment of the arrow function. In the global code, this will be set to the global object
var foo = () => {
console.log(this)
}
foo()
var obj = {foo: foo}
obj.foo()
foo.call("abc")
foo.bind(123)()
//The printed this is all window, because the arrow function uses call, apply, bind to display binding and obj.foo() implicit binding, etc. will be invalid
var obj = {
bar: function () {
var x = () => console.log(this);
return x;
},
};
var fn = obj.bar()(); //this point to obj
//The environment where x is located is in the bar function, so the this of the arrow function points to the this of the bar function, and the this of the bar function points to obj through the implicit binding of obj
But pay attention to the following situation, if you just refer to the method of obj without calling it, then after calling the arrow function globally, this points to window. Because fn2 is the bar function, the this of bar points to window through fn2()() independent function call
var fn2 = obj. bar;
fn2()() //this points to window
class of this
Strict mode is always inside a class, and like other ordinary functions, the value of this in class methods depends on how they are called
static keyword is used to define a static method of a class. Calling a static method does not need to instantiate the class and cannot call a static method through a class instance. They are just properties of the class itself.
class Person {
constructor(name, age) {
this.name = name;
}
// instance method
running () {
console.log(this.name + 'running');
}
//Static methods can only be properties of the class itself
static displayName = 'Existence is reasonable';
static add(a, b) {
return a + b;
}
}
var p1 = new Person('fang');
var p2 = new Person('Abaaaba');
console.log(p1.displayName); //undefined
console.log(Person.displayName); //Existence is reasonable
console.log(Person.add(1, 2)); //3
p1.running(); //fang running
p2.running(); //Abaaba running
Analysis of this comprehensive question
Example1 :
var name = "window";
var person = {
name: "person",
sayName: function () {
console.log(this.name);
}
};
function sayName() {
var foo = person. sayName;
foo(); // window:foo is the sayName function, without implicit binding on the left and explicit binding on the right, it is an independent function call
person.sayName(); // person: implicitly bound
(person.sayName)(); // person: implicit call
(b = person. sayName)();
// special case of window, this binding rules: indirect function reference, assignment expression (independent function call)
}
sayName();
Example2 :
var name = 'window'
var obj1 = {
name: 'obj1',
foo1: function () {
console.log(this.name)
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name)
}
},
foo4: function () {
return () => {
console.log(this.name)
}
}
}
var obj2 = { name: 'obj2' }
obj1.foo1(); // obj1 (implicit binding)
obj1.foo1.call(obj2); // obj2 (display binding priority is greater than implicit binding), that is, when there are rules on both sides of the function, look at the display binding rules on the right
obj1.foo2(); // window, foo2 is an arrow function, this points to the this of the upper scope obj1, that is, the global window
obj1.foo2.call(obj2); // window, call fails in arrow function
obj1. foo3()();
// window (independent function call), obj1.foo3() returns the function ƒ () {console.log(this.name)}, then direct independent function call
obj1.foo3.call(obj2)();
// window (independent function call), obj1.foo3.call(obj2) changes the this point of foo3, but returns the function that prints this.name, and then directly adds parentheses to make an independent function call
obj1.foo3().call(obj2);
// obj2, call the returned function using explicit binding, this points to the explicitly bound obj2
obj1.foo4()(); // obj1 (the arrow function does not bind this, the this of the upper scope foo4 is obj1)
obj1.foo4.call(obj2)(); // obj2 (this of the upper scope foo4 is explicitly bound to obj2)
obj1.foo4().call(obj2);
// obj1, obj1.foo4() returns an arrow function that cannot be called. According to the this of the upper scope foo4, it points to obj1
Example3 :
When doing the questions, pay attention to see which function is bound by call, apply, etc.
var name = 'window';
function Person(name) {
this.name = name;
this.obj = {
name: 'obj',
foo1: function () {
console.log(this.name);
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name);
};
},
foo4: function () {
return () => {
console.log(this.name);
};
},
};
}
var person1 = new Person('person1');
var person2 = new Person('person2');
person1.obj.foo1(); // implicit binding: obj
person1.obj.foo1.call(person2); // explicit binding: person2
person1. obj. foo2();
// foo2 is an arrow function, this points to search in the upper scope, that is, this of obj points to: person1
person1.obj.foo2.call(person2); // call fails, search in the upper scope: person1
person1.obj.foo3()();
// window (independent function call), person1.obj.foo3() returns the function that prints the name and calls it directly
person1.obj.foo3.call(person2)();
// window (independent function call), the call changes the this point of foo3, not the this point of the return function
person1.obj.foo3().call(person2);
// Explicit binding: person2, call changes the this point of the returned function
person1.obj.foo4()();
//obj, the arrow function finds that the this of the upper scope foo4 points to obj
person1.obj.foo4.call(person2)();
//The arrow function finds the this point of foo4 in the upper scope and displays the binding as: person2
person1.obj.foo4().call(person2);
// Upper scope search: obj, call cannot change the this of the arrow function