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

  1. 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?

  1. In this functionBinding occurs when called, pointing to completeDepends on where the function is executed, right.
  2. 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 itIt’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?

    1. As can be seen from the above three cases, the global functionthisThe execution context does not point to the function itself but to the window
    1. Can be achieved byChange this on function callsPoint to change the function execution context

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

  • throughcall()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:

  1. useThe call (), the apply ()The function is executed directly
  2. use.bind() creates a new function, requires a manual call to execute
  3. .call() and.apply() are used similarly, howeverCall takes several argumentsApply 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:

  1. Add a unique attribute fn to the execution context
  2. Point this to the property
  3. Execute fn function
  4. 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:

  • forobj.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 is galbol
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
  • thisDepending on the outer scope, we can avoid writing const that = this; Code like this

Scenarios where arrow functions are used need to be avoided

  1. Use arrow functions to define methods for objects
let obj  =  {
    value:"Fancy".getValue:() = > console.log(this.value);
}
obj.getValue() //undefined
Copy the code
  1. 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
  1. 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