What is this
When a function is called, an active object (sometimes called the execution context) is created. This record includes information about where the function was called (the call stack), how the function was called, the parameters passed in, and so on. This is an attribute recorded in the live object and used during the execution of the function function.
The relationship of this to scope
This does not point to a lexical scope at any time. A scope is indeed similar to an object in that its visible identifiers are its attributes, but a scope object cannot be accessed through JS code; it lives inside the JS engine. Have a chestnut:
function foo(){
var a = 2;
this.bar();
}
function bar() {
console.log(this.a);
}
foo();
Copy the code
Note: This is not possible when mixed with lexical scope lookup. This is bound when the function is called, and what it points to depends entirely on where the function is called.
What problem does this solve
- This provides a more elegant way to implicitly “pass” an object reference, so the API can be designed to be cleaner and easier to reuse.
To illustrate:
function identify(){
return this.name.toUpperCase();
}
function speak() {
var greeting = "Hello, I'm " + identidy.call(this);
}
var me = {
name:"Rose",}var you = {
name:"Jack"
}
identify.call(me);
identify.call(you);
speak.call(me);
speak.call(you);
Copy the code
What if you don’t use this?
function identify(context){
return context.name.toUpperCase();
}
function speak(context) {
var greeting = "Hello, I'm " + identidy(context)
}
identify(you) ; Jack
speak(me); //Hello, I'm Rose
Copy the code
In plain English, you can use this to let objects that do not have a method or property access that property or call that method
What does this depend on?
- In this function
Binding occurs when called
, pointing to completeDepends on where the function is executed, right
. - Always adhere to one principle:
This always refers to the object that last called it
,This always refers to the object that last called it
,This always refers to the object that last called it
It’s so important that it should be repeated for three times.
Let’s take a quiz to see what you know about this:
var number = 5;
var obj = {
number: 3.fn1: (function () {
var number;
this.number *= 2;
number = number * 2;
number = 3;
return function () {
var num = this.number;
this.number *= 2;
console.log(num);
number *= 3;
console.log(number); }}}) ()var fn1 = obj.fn1;
fn1.call(null);
obj.fn1();
console.log(window.number);
Copy the code
Chestnut 1 Analysis:
1.Fn1 has a self-calling function in obj that executes itself first,this.number *= 2; Is thewindow.number *= 2; At this timewindow.number = 10; The self-call declares a variable number with a value ofundefined,
nudefined *2, orundefined, and finally assign the value of number inside the self-calling function to3.
2.Fn1 actually refers to the anonymous function returned by fn1, fn1.call(null), refers to the inside of an anonymous functionthisforwindow, the implementation ofvar num = this.number; Is the num =windowNumber, so'output is 10'.this.number *= 2; At this timewindow.number *= 2; window.number = 20;
3.Anonymous functions do not declare number internally, so you need to go to the upper scope, number =3; The number * command is executed3, the number of the upper scope is assigned to9.The output is 9
4.The number declared in the self-calling function =9;
5.Obj.fn1 (), will anonymize the function insidethisImplicitly converted to obj,var num = this.number; Num = obj.number =3;
3 ` ` output ,number *= 3; Since there is no number inside the anonymous function, go to the upper scope and find number =9; number *=3; so27 ` ` output.6.At this timewindow.number = 20;
Copy the code
Misunderstanding points to self
Chestnut 2:
function countor(num){
console.log('countor',num)
// Records the number of times foo is called
this.count ++;
}
countor.count = 0;
var i = 0;
for(i = 0; i < 10 ; i++) {
if(i > 5) {
countor(i)
}
}
// foo:6
// foo:7
// foo:8
// foo:9
console.log(countor.count)
Copy the code
-
Why is countor still 0 when it’s called 4 times?
-
What are the solutions if you want Countor to be able to count the number of times a function is called?
Method one:
function countor(num){
console.log('countor',num);
countor.count ++;
}
countor.count = 0;
for(i = 0; i <10; i++){
if(i > 5 ){
countor(i)
}
}
console.log(countor.count)
Copy the code
Method 2:
function countor(num){
console.log('countor',num);
data.count ++;
}
var data = {
count: 0
}
var i ;
for(i = 0; i <10; i++){
if(i > 5 ){
countor(i)
}
}
console.log(data.count)
Copy the code
Although the problem is solved, this is avoided
This is avoided by calling countor.count instead of this by pointing the execution context of countor.count directly to the current function
Solution 3:
function countor() {
console.log('countor',num)
// Records the number of times foo is called
this.count ++;
}
countor.count = 0;
var i = 0;
for(i = 0; i < 10 ; i++) {
if(i > 5) {
// Change the context of the function call directly, pointing this to countor itself
countor.bind(countor,i)
}
}
Copy the code
The above example illustrates a misconception;
This does not in any case refer to the lexical scope of the function
Now let’s answer thatWhy is countor still 0 when it's called 4 times?
-
- As can be seen from the above three cases, the global function
this
The execution context does not point to the function itself but to the window
- As can be seen from the above three cases, the global function
-
- Can be achieved by
Change this on function calls
Point to change the function execution context
- Can be achieved by
The position of the this call, the top of the stack for the current execution context
Depending on the invocation location, decide which of the following four binding rules to use:
Binding rules
Default, implicit, explicit, new binding
The default binding
The default binding for global methods is Window. Strictly, you cannot use a global object for the default binding, so this is bound to undefined
Topic 1.1 a
function foo() {
console.log(this.a);
}
var a = 2;
Copy the code
1.2 the topic 2
function foo(){
'use strict';
console.log(this.a);
}
var a =2;
foo();
Copy the code
Errors are reported in strict mode. TypeError: Cannot read property ‘a’ of undefined
What if we call ‘use strict’ instead?
function foo() {
console.log(this.a);
}
var a = 2;
(function(){
'use strict';
foo()
})()
Copy the code
Conclusion: The default binding (in non-strict mode this refers to the global object, in strict mode this is bound to undefined) is declared, not called.
Implicit binding
When a function references a context object, the implicit binding rule binds this in the function call to that context object
Topic 2.1 a
function foo() {
console.log(this.a);
}
var obj = {
a:2.foo:foo
}
obj.foo();
Copy the code
2.1 the topic 2
What if you reference layer 2?
function foo() {
console.log(this.a);
}
var obj2 = {
a:2.foo:foo
}
var obj1 = {
a:2.obj2:obj2
}
obj1.obj2.foo()
Copy the code
Implicit loss: Common in callback functions
Why is the implicitly bound this lost?
Topic 3.1 a
function foo() {
console.log(this.a);
}
var obj = {
a:2.foo:foo
}
var bar = obj.foo;
var a = "global";
bar();
Copy the code
Since obj.foo refers to the function itself, the location of the bar() call is global, so this.a prints global
This chestnut is subtler and more unexpected
3.2 the topic 2
function foo() {
console.log(this.a);
}
function doFoo(fn){
fn();
}
var obj = {
a:'local'.foo:foo
}
var a = "global";
doFoo(obj.foo)
Copy the code
3.3 the title three
Now instead of calling doFoo from window, we’ll put it in obj2 and call it from obj2:
function foo(){
console.log(this.a);
}
function doFoo(fn){
console.log(this);
fn();
}
var obj = {a:"local",foo};
var a = "global";
var obj2 = {a:"obj2", doFoo};
obj2.doFoo(obj.foo);
Copy the code
Don’t you feel so easy, here’s a challenge:
3.4 the title four
var length = 10;
function fn () {
console.log(this.length);
}
var obj = {
length: 5.method: function (fn) {
console.log(this.length)
fn();
arguments[0]();
}
};
obj.method(fn, 1);
Copy the code
Why arguments[0]() has the value 2?
According to the binding
- through
call()
orapply()
,bind()
Method directly specifies the binding object for this, such as foo.call(obj).
There are three points to note when using display bindings:
- use
The call (), the apply ()
The function is executed directly - use
.bind() creates a new function
, requires a manual call to execute - .call() and.apply() are used similarly, however
Call takes several arguments
Apply accepts an array.
Write a pseudo-code implementation to see how bind works:
Function.prototype.call = function (context, ... args){
const fn = Symbol(a); context.fn =this;
constresult = context.fn(... args);delete context.fn;
return result;
}
Copy the code
Bind does four things:
- Add a unique attribute fn to the execution context
- Point this to the property
- Execute fn function
- Deletes the context property fn and returns the result of the FN call
Topic 4.1 a
function foo() {
console.log(this.a);
}
var obj = {a: 1};
var a = 2;
foo();
foo.call(obj);
foo.apply(obj);
foo.bind(obj);
Copy the code
Foo.bind (obj), because bind creates a new function that needs to be received and called with a variable, so it won’t be executed here.
Note: If call, apply, or bind receives null or undefined as the first argument, this argument will be ignored.
function foo() {
console.log(this.a);
}
var a = "global";
foo.call();
foo.call(null);
foo.call(undefined);
Copy the code
Now that we know about the display binding, let’s look at one of its great uses
4.2 the topic 2
var obj1 = {
a:1
}
var obj2 = {
a:2.foo1:function(){
console.log(this.a);
},
foo2:function() {
console.log(this);
setTimeout( function (){
console.log(this);
},0)}}var a = "global";
obj2.foo1();
obj2.foo2();
Copy the code
What if I want this to point to obj1?
4.3 the title three
setTimeout(function(){
console.log(this);
}.call(obj1),0)
Bind (obj1).bind(obj1)
Copy the code
So some people might ask, “Can’t I write this way?”
obj2.foo2.bind(obj1)
Copy the code
Note ⚠️ : this actually changes the this inside foo2. SetTimeout this has nothing to do with this inside foo2. This inside timer calls is always window
4.4 the title four
OK👌, instead of a timer, let’s kill it and replace it with a function:
var obj1 = {
a:"obj1"
}
var obj2 = {
a:"obj2".foo1:function() {
console.log(this.a);
},
foo2:function() {
console.log(this.a);
function inner() {
console.log(this.a);
}
inner()
}
}
var a = 3;
obj2.foo1();
obj2.foo2()
Copy the code
What if I changed inner() to show bindings instead?
inner.call(obj1)
Copy the code
4.5 the topic 5
Let’s see what happens next?
function foo() {
console.log(this.a);
}
var obj = {
a:1
}
var a = "global";
foo();
foo.call(obj);
foo().call(obj)
Copy the code
Uncaught TypeError: Cannot read property ‘call’ of undefined because call must be called by a function.
What if function foo returns a function?
4.6 the title six
function foo() {
console.log(this.a);
return function() {
console.log(this.a)
}
}
var obj = {
a:1
}
var a = "global";
foo();
foo.call(obj);
foo().call(obj);
Copy the code
What happens if you replace call with bind?
4.7 the title seven
function foo() {
console.log(this.a);
return function () {
console.log(this.a)
}
}
var obj = {
a:1
}
var a = "global";
foo();
foo.bind(obj);
foo().bind(obj)
Copy the code
Note that ⚠️ : foo.bind(obj) will not be executed because the new function returned needs to be received and called by the variable.
4.8 the title eight
Is this inside the function related to this outside the function? Who does the inner this point to? Again, our important mantra :this is determined by the last object to call it
function foo () {
console.log(this.a)
return function () {
console.log(this.a)
}
}
var obj = { a: 1 }
var a = 2
foo.call(obj)()
Copy the code
What if you put the above function return function into an object?
4.9 the title nine
var obj = {
a:"obj".foo:function() {
console.log('foo'.this.a);
return function() {
console.log('inner'.this.a)
}
}
}
var obj2 = { a: "obj2"};
var a = 2
obj.foo();
obj.foo.call(obj2)();
obj.foo().call(obj2)
Copy the code
Let’s play around with this parameter
4.10 the title ten
var obj = {
a:1.foo:function(b) {
b = b || this.a;
return function(c) {
console.log(this.a + b + c)
}
}
}
var a = 2;
var obj2 = { a: 3};
obj.foo(a).call(obj2,1);
obj.foo.call(obj2)(1)
Copy the code
The new binding
What did New do?
function myNew (Person) {
const context = Object.create(Person);
let result = context.call(context);
if((typeofresult ! = ='null') && (typeof result === 'object' || typeof result === 'function')) {
return result;
}else {
returncontext; }}Copy the code
To sum up 4 sentences:
- New a new object,
- The new object prototype points to the constructor
- And point this to the new object being created
- The function returns the newly created object if no other object is returned, or if the constructor returns an object or function, the object or function is returned
New binding priority issue
Topic 5.1 a
function foo() {
console.log(this.a);
}
var obj1 = {
a:2.foo:foo
}
var obj2 = {
a:3.foo:foo
}
obj1.foo.call(obj2);
obj2.foo.call(obj1);
Copy the code
You can see that the display binding has a higher priority
Let’s look at new binding versus implicit binding priority
5.2 the topic 2
function foo(something){
this.a = something;
}
var obj1 = {
foo:foo
}
var obj2 = {};
obj1.foo(1);
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
We can see that the new binding has higher priority than the implicit binding.
5.3 the title three
Since we couldn’t pass the new foo.call(obj1) test, we implemented it indirectly through hard binding
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(baz.a);
console.log(bar.a);
Copy the code
New Bar does not change obj1.a to an internal implementation of 3. bind as expected: it determines if new is called, and if so replaces hard-bound this with a newly created this
5.4 the title four
var name = 'window'
function Person (name) {
this.name = name
this.foo = function () {
console.log(this.name)
return function () {
console.log(this.name)
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.foo.call(person2)()
person1.foo().call(person2)
Copy the code
Answer:
Person1.foo ().call(person2) can be understood as binding to person2 the context of the name function returned by the person1.foo() callCopy the code
In conclusion, judge this:
To determine which rules a function should use at a particular call location based on priority, you can do so in the following order
-
Is the function called in new (new binding)? If the this binding is the newly created object. var bar = new foo():
-
Is the function called by call, apply, (show binding) or hard binding? If so, this binds to the specified object.
-
Is the function called in a context (implicit binding)? If so, this binds to that context object. var bar = obj1.foo();
-
If not, use the default binding, binding to undefined in non-strict mode, otherwise binding to the global object var var = foo();
Arrow function
-
For the above problem, this always refers to the last object to which it was bound, but the same is not true for arrow functions.
-
The arrow function’s this is determined by the outer scope and refers to this at definition, not execution time.
What does it mean that this is defined by the outer scope?
Arrow functions have no this binding, and its value must be determined by searching the scope chain. If the arrow function is contained by a non-arrow function, this points to this of the nearest non-arrow function, otherwise this is undefined
Topic 6.1 a
A simple chestnut:
var name = 'window'
var obj1 = {
name: 'obj1'.foo: function () {
console.log(this.name)
}
}
var obj2 = {
name: 'obj2'.foo: () = > {
console.log(this.name)
}
}
obj1.foo()
obj2.foo()
Copy the code
6.2 title 2
var obj = {
name:"obj".foo1:() = > {
console.log(this.name);
},
foo2:function (){
console.log(this.name);
return () = > {
console.log(this.name); }}}var name = "galbol";
obj.foo1();
obj.foo2()();
Copy the code
Problem solving:
- for
obj.foo1()
Function call, its outer scope is window, object obj is of course not in scope, (scope only global scope and local scope created by the function), so the output isgalbol
6.3 title 3
var name = 'window'
function Person (name) {
this.name = name
this.foo1 = function () {
console.log(this.name)
}
this.foo2 = () = > {
console.log(this.name)
}
}
var person2 = {
name: 'person2'.foo2: () = > {
console.log(this.name)
}
}
var person1 = new Person('person1')
person1.foo1()
person1.foo2()
person2.foo2()
Copy the code
Problem solving:
1.The arrow function in the constructor person1.foo2(), the arrow function'This' is determined by the outer scope and refers to the definition time, not the execution timeThe outer scope is the function Person and the constructornewI'm generating a new object, so at this pointthisPointing to person1Copy the code
6.4 the title four
var name = 'window'
var obj1 = {
name: 'obj1'.foo1: function () {
console.log(this.name)
return () = > {
console.log(this.name)
}
},
foo2: () = > {
console.log(this.name)
return function () {
console.log(this.name)
}
}
}
var obj2 = {
name: 'obj2'
}
obj1.foo1.call(obj2)()
obj1.foo2().call(obj2)
Copy the code
To summarize the points to note about the arrow function:
-
The this inside is determined by the nearest outer function and refers to this when the function is defined, not when it is executed
-
The object created by the literal is scoped to window, and this refers to window if the arrow function is an attribute
-
The constructor creates an object whose scope can be understood as this constructor, and this constructor refers to the newly created object
-
This in the arrow function cannot be changed by bind, apply, or call, but can be changed indirectly by changing the this pointer in the scope.
Advantages:
- The arrow function can make the code have a cleaner syntax
this
Depending on the outer scope, we can avoid writing const that = this; Code like this
Scenarios where arrow functions are used need to be avoided
- Use arrow functions to define methods for objects
let obj = {
value:"Fancy".getValue:() = > console.log(this.value);
}
obj.getValue() //undefined
Copy the code
- Define the prototype method
function Foo(value) {
this.value = value;
}
Foo.prototype.getValue = () = > console.log(this.value);
const foo1 = new Foo(1);
foo1.getValue(); //undefined;
Copy the code
- As a callback function for the event
const button = document.getElementsById("myButton");
button.addEventListener("click".() = >{
console.log(this= = =window);//true
this.innerHTML = "Clicked button";
})
Copy the code
4. Constructors use arrow functions
const Foo = (value) = > {
this.value = value;
}
const foo1 = new Foo(1);
console.log(foo1); // Foo is not a constructor
Copy the code
Finally, to synthesize the question:
var name = 'window'
function Person (name) {
this.name = name
this.foo1 = function () {
console.log(this.name)
},
this.foo2 = () = > console.log(this.name),
this.foo3 = function () {
return function () {
console.log(this.name)
}
},
this.foo4 = function () {
return () = > {
console.log(this.name)
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.foo1()
person1.foo1.call(person2)
person1.foo2()
person1.foo2.call(person2)
person1.foo3()()
person1.foo3.call(person2)()
person1.foo3().call(person2)
person1.foo4()()
person1.foo4.call(person2)()
person1.foo4().call(person2)
Copy the code
Solution idea;
person1.foo2()
person1.foo2.call(person2)
// Notice that in the constructor, this.foo2 = () => console.log(this.name);
// This is determined by the outer scope and refers to the function definition rather than the execution. The outer scope is the function Person and the constructor. New is generated
// person1, so this points to person1
Copy the code
Refer to the reading
Js Parts 2 that You Don’t Know
40. Understand this