This, call, apply, bind
This, call, apply, bind. I refer to articles by Ruan Yifeng, Hu Yu and related articles on “JavaScript Advanced Programming”. Mainly is the summary of relevant knowledge
Directory:
I. Understanding of this
Second, summarize the orientation of this in different occasions and the problems needing attention
Call, bind, apply
4. Write a call
Write apply by hand
Write bind by hand
This in the arrow function
8, This refers to the topic exercise
I. Understanding of this
Reference article: wangdoc.com/javascript/…
This is the object on which the property or method is “currently” located. Since the property or method of an object can be copied to another object, the object on which the property or method is “currently” located is mutable. So the reference to this is also mutable.
The design of this is related to the data structure in memory.
1. When we define a variable:
Var obj = {name: 'zz'};
The JavaScript engine first generates an object {name: ‘zz’} in memory. The address of memory is then assigned to the variable obj. So what’s stored on OBj is actually an address. When we need to read obj.name, we get the address from obj and then fetch the name value. You should have some sense of how variables are described. Storing variables in memory is in the form of. What we read is the value stored on [[vaule]]
{
name: {
/ / value[[value]] : 'zz',// Is writable
[[writable]]: true.// Whether enumerable
[[enumerable]]: true.// Whether it can be configured
[[configurable]]: true}}Copy the code
2. When we define a function:
The JavaScript engine stores the function separately in memory and then assigns the address of the function to the value of the name property
{
function f () {
console.log(this)}; name: {/ / value[[value]]: address of function f,... }}Copy the code
There is a runtime environment when f() is executed alone. Obj is the runtime environment for f when obj.name() is executed. So when other variables in the current environment are referenced in the function body, the values are different depending on the running environment.
var f = function () {
console.log(this.name);
}
varName = 'zz';var obj = {
name: ‘dd’,
f: f
}
f() // This refers to window, this.name zz
obj.f() // This refers to obj, this.name dd
Copy the code
Hence the sentence: the this in a function (non-arrow function) always refers to the object that last called it.
Second, summarize the direction of this in different occasions
1, global environment: the top-level object window
function f() {
console.log( this= = =window);
}
f();
Copy the code
Constructor: the instance object to which this points
var Persion = function (name) {
this.name = name;
}
var p = newPersion (' zz '); p.name// zz
Copy the code
3. Object methods
If the object’s method contains this, this points to the object on which the method is run. This method assigns to another object, which changes the reference to this.
var obj ={
foo: function () {
console.log(this); }}; obj.foo()// obj
/ / a
(obj.foo = obj.foo)() // window
/ / 2
(false || obj.foo)() // window
/ / is three
(1, obj.foo)() // window
Copy the code
Resolution:
Inside the JavaScript engine, obj and obj.foo are stored at two memory addresses, called address one and address two. When obj.foo() is called this way, it calls address two from address one, so address two runs at address one, and this refers to obj.
However, in each of the above cases, we simply fetch address two, assign the value and call it, so the runtime environment is the global environment, so this refers to the global environment.
/ / a
(obj.foo = function () {
console.log(this); }) ()/ / is equivalent to
(function () {
console.log(this); }) ()Copy the code
/ / 2
(false || function () {
console.log(this); }) ()/ / is three
(1.function () {
console.log(this); }) ()Copy the code
If the method is not in the first layer of the object, this refers only to the current layer of the object and does not inherit from the layer above.
var a = {
p: 'Hello'.b: {
m: function() {
console.log(this.p); }}}; a.b.m()// undefined
Copy the code
4. Problems encountered
(1) Avoid multiple layers of nesting. If nesting is encountered, you need to fix this with a variable on the upper layer, and then call the variable on the inner layer.
var o = {
f1: function() {
console.log(this);
var that = this;
var f2 = function() {
console.log(that); } (); } } o.f1()Copy the code
(2) Avoid this in array handling methods. For example, in foreach, the map callback refers to this as a window. If you want to fix this, you can either define a variable at the top or bind this to the second parameter of foreach
var o = {
v: 'hello'.p: [ 'a1'.'a2'].f: function f() {
var that = this;
this.p.forEach(function (item) {
console.log(that.v+' '+item);
});
}
// Another way
//this.p.forEach(function (item) {
// console.log(this.v + ' ' + item);
//}, this);
}
o.f()
Copy the code
Call, bind, apply
Call () 1, the Function. The prototype.
The call method can specify the point of this inside a function (the scope in which the function is executed).
The call method can take multiple arguments
Func. Call (thisValue, arg1, arg2,...).
The first argument: the object to which this refers. Here thisValue is an object,
(1) If the argument is null, null and undefined are global objects by default
(2) If this is a primitive value, it is converted to the corresponding wrapper object
(3) If it is a normal object, there is no need to do the conversion
var n = 100;
var obj = {n: 200};
function a () {
console.log(this.n);
}
function f () {
return this;
}
a.call() / / 100
a.call(null) / / 100
a.call(undefined) / / 100
a.call(obj) / / 200;
f.call(5) // // Number {[[PrimitiveValue]]: 5}
Copy the code
Other arguments: are the arguments required for a function call
function add (a, b) {
return a + b;
}
add.call(this.1.2) / / 3
Copy the code
2, the Function. The prototype. The apply ()
The usage is similar to that of call, except that it takes an array as an argument to the function.
Func. Apply (thisValue, (arg1, arg2,...) )
3, the Function. The prototype. The bind ()
(1) Bind () binds this in the function body to an object and returns a new function.
The first argument to bind is some object in the function’s this binding. If the first argument is null, or undefined, this is bound to the global object by default.
var counter = {
count: 0.inc: function () {
this.count++; }}const func = counter.inc.bind(counter);
func() / / 1
Copy the code
(2) Bind can accept many more arguments
var add = function (x, y, z) {
return x * this.m + y * this.n + z;
}
var obj = {
m: 2.n: 2
};
var newAdd = add.bind(obj, 5);
const result = newAdd(5.6);
console.log('result----', result);/ / 26
var newAdd = add.bind(obj, 5.5);
const result = newAdd(6);
console.log('result----', result);/ / 26
// Bind binds add with two arguments, and then only passes in the third argument each time newAdd is called.
Copy the code
(3) The bind method returns a new function each time
This way, you cannot cancel the anonymous function
element.addEventListener('click', o.m.bind(o));
element.removeEventListener('click', o.m.bind(o));
Copy the code
Correct method:
var listener = o.m.bind(o);
element.addEventListener('click', listener);
element.removeEventListener('click', listener);
Copy the code
4. Handwritten Call method
Refer to the article: segmentfault.com/a/119000000…
The call method has the following functions and parameters:
(1) Call changes the direction of this
(2) The call-bound method is executed
(3) Call can carry parameters
Steps of simulation
(1) Set the function to the property of the binding object (null, undefined),
(2) Execute this function and pass related arguments
(3) Delete this function
Function.prototype.call2 = function(obj) {
const context = obj || window;
context._func = this;
let args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push(arguments[i]);
}
constresult = context._func(... args);delete context._func;
return result;
}
var value = 2;
var obj = {
value: 1
}
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
bar.call2(null); / / 2
console.log(bar.call2(obj, 'kevin'.18));
/* 1, {value: 1, name: kevin; 18} * /
Copy the code
5. The principle of handwritten Apply and call are the same. The only difference is in parameters
Function.prototype.apply2 = function (obj, arr) {
const context = obj || window;
context._fn = this;
var result;
if(! arr || ! arr.length) { result = context._fn(); }else{ result = context._fn(... arr); }delete context._fn
return result;
}
Copy the code
Write bind by hand
Reference article: github.com/mqyqingfeng…
To summarize: The bind() method creates a new function, and when the new function is called, the first argument to bind will be passed as an argument to the function after this when it runs.
Bind returns a function. The first parameter is the operating environment of the new function, which can be passed in as arguments
(1) The binding part of this can be implemented by call and apply.
Function.prototype.bind2 = function (context) {
var self = this;
return function () {
returnself.apply(context); }}Copy the code
(2) Pass parameters
var foo = {
value: 1
};
function bar(name, age) {
console.log(this.value);
console.log(name);
console.log(age);
}
var bindFoo = bar.bind(foo, 'daisy');
bindFoo('18');
Copy the code
From the above example, we know that bindFoo’s arguments contain arguments passed twice
Function.prototype.bind2 = function (context) {
var self = this;
// Get the bind2 function from the second argument to the last argument. The argument passed when the bind call is made
var args = Array.prototype.slice.call(arguments.1);
return function () {
// Arguments refer to the arguments passed in by the function returned by bind
var bindArgs = Array.prototype.slice.call(arguments);
returnself.apply(context, args.concat(bindArgs)); }}Copy the code
(3) Bind returns a new function, which can be used as a constructor. The value of this specified in bind is invalid, but the value of the parameter passed in is valid.
Function.prototype.bind2 = function (context) {
// Bind must be a function
if (typeof this! = ="function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var args = Array.prototype.slice.call(arguments.1);
// do the transfer of the prototype chain inheritance
var fNOP = function () {};
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
// If this is the function returned by bind called new, determine what this refers to.
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
// Change fBound's.prototype
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
Copy the code
The above code for call, apply, bind was written by Hu Yui, a good article worth sharing. Make a note here to mark their own understanding
This in the arrow function
Reference article: juejin.cn/post/694602…
When you create the arrow function, you already know its This point. The this inside the arrow function points to the outer this.
So to determine the arrow function this, we must first know the direction of the outer this. Refer to the summary of this reference above
8. Exercises
Reference article:
Juejin. Cn/post / 695904…
Juejin. Cn/post / 706939…
In a real problem, there are all kinds of situations. What to grasp is:
This is bound at run time, not at write time, and its context depends on various conditions at the time the function is called
The binding of this has nothing to do with the position of the function declaration, except how the function is called
About how to call: determine the function call location and call stack, determine the binding object for this.
The first question
function foo() {
console.log(this)
console.log( this.a );
}
function doFoo() {
console.log(this)
foo();
}
var obj = {
a: 1.doFoo: doFoo
};
var a = 2;
obj.doFoo()
Copy the code
Correct output:
/* obj:{a: 1.dofoo:} window 2 Obj. DoFoo is called inside of obj. But foo's this binding is the default window foo() output of 2 */
Copy the code
The second question
var a = 10
var obj = {
a: 20.say: () = > {
console.log(this.a)
}
}
obj.say()
var anotherObj = { a: 30 }
obj.say.apply(anotherObj)
Copy the code
The correct output
/* obj.say() is the arrow function inside this. 10 obj.say.apply(anotherObj) now change the operating environment of obj.say to anotherObj, this to the window pointed to by anotherObj: 10 */
Copy the code
The third question
function a() {
console.log(this);
}
a.call(null);
Copy the code
The correct output
windowThe first argument to call isnullTheta points to thetawindow
Copy the code
The fourth question
var obj = {
name : 'cuggz'.fun : function() {
console.log(this.name);
}
}
obj.fun()
new obj.fun()
Copy the code
The correct output
/* fun.fun (); /* fun.fun (); Cuggz new obj.fun() takes obj.fun as a constructor. This points to the constructor because there is no name argument, and output: undefined */
Copy the code
The fifth problem
var obj = {
say: function() {
// var f1 = () => {
// console.log("1111", this);
// }
// f1();
const f1 = function() {
console.log("1111".this);
}
f1();
},
pro: {
getPro:() = > {
console.log(this); }}}var o = obj.say;
o();
obj.say();
obj.pro.getPro();
Copy the code
Correct output
/* var o = obj.say; I'm assigning the say function to o and o is in global scope; O () output: 1111 window obj.say() where say is run in obj, f1 is the arrow function pointing to the environment in which the outer say is run, so output: 1111 obj If f1 is not an arrow function, f1 is run in window. F1 is called inside the say function, but the default binding is window obj.pro.getPro(). GetPro is the arrow function, and this points to the outer this. So it points to obJ's runtime environment window. Output window * /
Copy the code
The sixth question
var myObject = {
foo: "bar".func: function() {
var self = this;
console.log(this.foo);
console.log(self.foo);
(function() {
console.log(this);
console.log(this.foo);
console.log(self.foo); } ());// var fn = function() {
// console.log(this);
// console.log(this.foo);
// console.log(self.foo);
// };
// fn();}}; myObject.func();Copy the code
Is the output
/* myObject.func() this.foo outputs bar self.foo where this and self refer to the same output bar immediately points to a function where this refers to a window and the function is not bound to func. Output: undefined self refers to func, and self.foo outputs bar */
Copy the code
Number 7
window.number = 2;
var obj = {
number: 3.db1: (function test1() {
console.log('ddd'.this);
this.number *= 4;
return function test() {
console.log('return'.this);
this.number *= 5; }}}) ()var db1 = obj.db1;
db1();
obj.db1();
console.log(obj.number);
console.log(window.number);
Copy the code
The correct output
/* obj.db1 is an immediate function that is executed immediately when parsed. This. Number = 2 * 4 = 8; var db1 = obj.db1; Db1 points to the return value of the immediate function corresponding to obj. Db1. Db1 () calls the return function of obj. Db1, return obj, This. number = 15. Obj. number is 15. Window. number is 8 */
Copy the code
The eighth problem
var length = 10;
function fn() {
console.log(this.length);
}
var obj = {
length: 5.method: function(fn) {
fn();
arguments[0]();
}
};
obj.method(fn, 1);
Copy the code
Normal output
/* obj.method(fn, 1); Function fn() {console.log(this.length); function fn() {console.log(this.length); Manufacturer: ƒ, 1, callee: ƒ, Symbol(Symbol. Iterator): ƒ] arguments
Copy the code
Question 9
var a = 1;
function printA() {
console.log(this.a);
}
var obj = {
a: 2.foo: printA,
bar: function() {
printA();
}
}
obj.foo();
obj.bar();
var foo = obj.foo;
foo();
Copy the code
The correct output
/* obj.foo stores a pointer to printA. Obj.foo () now printA is run in obj this.a 2 printA() inside obj.bar() is run in window 1 foo = obj.foo now assigns a pointer to printA to Foo. The runtime environment is Window. Foo () prints 1 */
Copy the code
The first ten questions
var x = 3;
var y = 4;
var obj = {
x: 1.y: 6.getX: function() {
var x = 5;
return function() {
return this.x; } (); },getY: function() {
var y = 7;
return this.y; }}console.log(obj.getX())
console.log(obj.getY())
Copy the code
The correct output
/* obj.getX() // function() { return this.x; } (); // If the function is running in obj, the output is 6 */
Copy the code
The eleventh problem
var a = 10;
var obt = {
a: 20.fn: function() {
var a = 30;
console.log(this.a)
}
}
obt.fn();
obt.fn.call();
(obt.fn)();
Copy the code
The correct output
/* obt.fn() runs the environment obt this.a outputs 20 obt.fn.cal() binds the global environment this.a outputs 10 (obt.fn) is the global environment this.a outputs 10 */
Copy the code
Question 12
function a(xx) {
this.x = xx;
return this
};
var x = a(5);
var y = a(6);
console.log(x.x)
console.log(y.x)
Copy the code
The correct output
/* a(5) returns this as window; x = 5; Y = {x: 6} y = {x: 6
Copy the code
Article 13 questions
function foo(something) {
this.a = something
}
var obj1 = {
foo: foo
}
var obj2 = {}
obj1.foo(2);
console.log(obj1.a);
obj1.foo.call(obj2, 3);
console.log(obj2.a);
var bar = new obj1.foo(4)
console.log(obj1.a);
console.log(bar.a);
Copy the code
The correct output
Call (obj2, 3) obj2.a // 3 obj1.a // 2 bar.a // 4 */
Copy the code
Question 14
function foo(something){
this.a = something
}
var obj1 = {}
var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a);
var baz = new bar(3);
console.log(obj1.a);
console.log(baz.a);
Copy the code
The correct output
/* foo. Bind (obj1) replaces this in foo with obj1, which returns bar bar(2) assigning foo 2 obj1.a // 2 new bar(3) constructor, in which case foo. Obj1.a // 2 baz.a // constructor this.a 3 */
Copy the code