preface
In JS, this is an important concept. The meaning of this may be different in different places. A simple operation may change the meaning of this, so how to identify this is very important
Execution context
This is a special attribute of the execution context, so to understand this, you must understand the execution context and the execution context in short is the current code execution environment, first run js code will create a global execution context and pressure into the execution stack (lifo), when the calling function will create a new execution context for this function, The function is removed from the stack after the execution of the corresponding execution context, that is, the execution environment is destroyed, and the variables and functions saved in the environment are destroyed
So far we know that there are two kinds of execution contexts (eval is not discussed here) :
-
Global execution context: The this browser usually refers to window
-
Function execution context: the execution context is created when the function is called, not defined, and this as an attribute of the execution context is determined only when the function is called, so it is usually said that whoever calls this is the one who calls this
Let’s look at some demos:
// This demo knows how to execute last-in, first-out stack
function test() {
console.log('I am test')
test1()
console.log('I am test2')}function test1() {
console.log('I am test1')
}
test()
// Execution process:
// 1. Create a global execution context and push the execution stack
// 2. Create the test execution context and push the execution stack
If test1 is encountered, create a test1 execution context and push the execution stack
// 4. After test1 is executed, test1 is removed from the stack and destroyed
// 5. The test execution context is removed from the stack and destroyed
// 6. Close the browser, unstack the global execution context, and destroy it
Copy the code
var a = 1
var obj = {
a: 2.fn: function () { // The function is created, this is undefined, depending on who calls it
console.log('a--'.this.this.a)
return function() {
console.log('c--'.this.this.a)
}
}
}
Call a / /
obj.fn() // the function is called by obj
// Result: a-- {a: 2, fn: ƒ} 2
/ / call 2
var fn = obj.fn
fn()// equivalent to window.fn(), in which case this points to window
A -- Window {Window: Window, self: Window, document: document, name: "", location: location,... 1}
/ / call three
obj.fn()()
// Execution result:
// a-- {a: 2, fn: ƒ} 2
// c-- Window {Window: Window, self: Window, document: document, name: "", location: location,... 1}
// fn() is called by obj, so this refers to obj, a is 2, and the second function is actually called by window, so this is window, a is 1
// The function is called by whom. function, who is who, this is who, no who. this is window
Copy the code
Summary: This is the execution context attribute, the new execution context can have a new this reference, the global and function can create the execution context, except this in the function, the other this are window, this is called: who? The function “this” is the same as the function “window” without “who”
There will be some special existence in JS, let’s look at it together
New
New does the following:
- Create a new object
- The new __proto__ object points to the function’s prototype
- This refers to that object
- Return a new object
Implementation of new:
function Animal(type) {
this.type = type
return
}
Animal.prototype.say = function() {
console.log('say')}// Customize new
function myNew(. values) {
let [ fn, ...others ] = values
let obj = {}
obj.__proto__ = fn.prototype
const result = fn.apply(obj, others)
// fn returns an object if it is an object, or obj if it is not
return Object.prototype.toString.call(result) == '[object Object]' ? result : obj
}
const animal = myNew(Animal, 'v')
console.log('animal', animal)
// animal Animal {type: "v"}
Copy the code
We can see from the implementation of new that changing Animal this refers to the new object obj
Call, Apply, and Bind
Call, apply, and bind can all change the direction of this, and their implementation is as follows:
// call
Function.prototype.myCall = function(. values) {
let [ obj, ...others ] = values
obj = obj ? Object(obj) : window
obj.fn = thisobj.fn(... others)delete obj.fn
return
}
// apply
Function.prototype.myApply = function(. values) {
let [ obj, others ] = values
obj = obj ? Object(obj) : window
obj.fn = this
obj.fn(others)
delete obj.fn
return
}
// bind
Function.prototype.myBind = function(. values) {
let [ obj, others ] = values
let _self = this
let bind = function(vl) {
return _self.apply(obj, [...others, ...vl])
}
bind.prototype = Object.create(this.prototype)
return bind
}
Copy the code
Ok, the above is their implementation, you can see that all three of them have the effect of changing this, again using demo as an example:
var a = 1
var obj = {
a: 2.fn: function () {
console.log('a--'.this.this.a)
return function() {
console.log('c--'.this.this.a)
}
}
}
Call a / /
var fn = obj.fn
fn.call(obj)
} // Result: a-- {a: 2, fn: ƒ} 2
/ / call 2
obj.fn().call(obj)
// Execution result
Fn: ƒ} 2
ƒ} 2
// This is obj when the function is called. This is obj when the function is called
Copy the code
Arrow function
The arrow function is a special existence. It doesn’t have its own this, no prototype, no arguments, and can’t be used as a constructor. It inherits this from the upper scope chain. Let’s watch some demos
var obj = {
fn: () = > {
console.log('this'.this)}}Call a / /
obj.fn()
// This Window {Window: Window, self: Window, document: document, name: "", location: location,... }
/ / call 2
obj.fn.call(obj)
// This Window {Window: Window, self: Window, document: document, name: "", location: location,... }
// The result is window because the arrow function was created with this pointing to window
Copy the code
Then look at one
var obj = {
fn: function() {
return () = > {
console.log('this---'.this)}}}Call a / /
obj.fn()()
// Result: this-- {fn: ƒ}
/ / call 2
const fn = obj.fn()
fn()
// Result: this-- {fn: ƒ}
/ / call three
obj.fn.call(window) ()// This Window {Window: Window, self: Window, document: document, name: "", location: location,... }
// The third time the result is different, because the call of fn's this refers to window
Copy the code
conclusion
Execution context also has some concepts, such as: variable promotion, function promotion, scope chain, etc., do not do too much explanation, do not understand the partner to check