2

The author is watching the JavaScript you don't know recently, and the explanation about this is very exciting. JavaScript in this this is a core concept, and some students will be a little vague and afraid of it. A lot, makes us think this is no pattern to be found, like a ghost

If you haven't figured it out yet this , or are vague about it, this article is specially prepared for you. If you are relatively familiar with it, then you can also use it as a review to consolidate your knowledge points

This article is a reading note, and of course it also adds a lot of my personal understanding. I think it will definitely be helpful to everyone.

execution context

Before understanding this , let's take a look at what is an execution context

In short, an execution context is an abstraction of the environment in which JavaScript code is evaluated and executed. Whenever Javascript code is running, it is running in the execution context

There are three execution context types in JavaScript

  • Global execution context - This is the default or base context, any code that is not inside a function is in the global context. It does two things: creates a global window object (in the browser case), and sets this to a value equal to this global object. There is only one global execution context in a program
  • Function Execution Context — Whenever a function is called, a new context is created for that function. Each function has its own execution context, but is created when the function is called. There can be any number of function contexts
  • eval Function Execution Context — Executed in eval The code inside the function will also have its own execution context, but because JavaScript developers do not often use eval , so I won't discuss it here

Here we first draw a conclusion that this in both non-strict mode and strict mode points to the top-level object (window in the browser)

 console.log(this === window); // true
'use strict'
console.log(this === window); // true
this.name = 'vnues';
console.log(this.name); // vnues

Later, our discussion is more on the function execution context

what exactly is this? why use this

this is bound at runtime, not at writing time, its context depends on various conditions when the function is called

Remember: The binding of this has nothing to do with where the function is declared, it only depends on how the function is called

When a function is called, an active record (sometimes called an execution context) is created. This record will contain information about where the function was called (call stack), how the function was called, parameters passed in, etc. this is one of the attributes of the record, which will be used during function execution

Look at an example to understand why to use this , sometimes, we need to implement code like the following:

 function identify(context) {
  return context.name.toUpperCase();
}
function speak(context) {
  var greeting = "Hello, I'm " + identify(context);
  console.log(greeting);
}
var me = {
  name: "Kyle"
};
speak(me); //hello, 我是 KYLE

The problem with this code is that it needs to display and pass the context object. If the code becomes more and more complex, this method will make your code look very confusing. Using this is more elegant

 var me = {
  name: "Kyle"
};

function identify() {
  return this.name.toUpperCase();
}
function speak() {
  var greeting = "Hello, I'm " + identify.call(this);
  console.log(greeting);
}
speak.call(me); // Hello, 我是 KYLE

Four binding rules for this

Let's look at the binding rules in the context of functions, there are the following four

  • default binding
  • implicit binding
  • explicit binding
  • new binding

default binding

The most commonly used function call type: independent function call, this is also the one with the lowest priority, this matter this points to the global object. Note: If you use strict mode ( strict mode ), the global object will not be able to use the default binding, so this will be bound to undefined as shown below

 var a = 2;  //  变量声明到全局对象中
function foo() {
  console.log(this.a);   // 输出 a
}

function bar() {
  'use strict';
  console.log(this); // undefined
}
foo();
bar();

implicit binding

We can also say at the beginning: The binding of this has nothing to do with the position of the function declaration, it only depends on how the function is called

Let's look at an example first:

 function foo() {
    console.log(this.a);
  }
  var obj = {
    a: 2,
    foo: foo
  };
  obj.foo(); // 2

When calling obj.foo() , this points to the obj object. When a function reference has a context object, the implicit binding rules bind the function call this to this context object. Because when calling foo() this is bound to obj, this.a and obj.a are the same

Remember: only the top or last level in the chain of object property references affects where the call is made

 function foo() {
      console.log(this.a);
    }
    var obj2 = {
      a: 42,
      foo: foo
    };
    var obj1 = {
      a: 2,
      obj2: obj2
    };
    obj1.obj2.foo(); // 42

indirect reference

Another caveat is that it is possible (intentionally or not) to create an "indirect reference" to a function, in which case calling the function will apply the default binding rules

 function foo() {
  console.log(this.a);
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2

Another caveat is that it is possible (intentionally or not) to create an "indirect reference" to a function, in which case calling the function will apply the default binding rules

The return value of the assignment expression p.foo = o.foo is a reference to the target function, so the call location is foo() instead of p.foo() or o.foo() According to what we said before, the default bindings are applied here

show binding

When analyzing implicit binding, we must include a property inside an object that points to a function, and indirectly reference the function through this property, thus indirectly (implicitly) binding this to this object. So what if we don't want to have a function reference inside an object, but want to force a function to be called on an object?

Javascript provides apply , call and bind methods that allow us to implement

The difference is that call() and apply() are immediate functions and accept arguments in different forms:

  • call(this, arg1, arg2, ...)
  • apply(this, [arg1, arg2, ...])

And bind() creates a new wrapper function and returns instead of executing it immediately

  • bind(this, arg1, arg2, ...)

See the following example:

 function foo(b) {
    console.log(this.a + '' + b);
  }
  var obj = {
    a: 2,
    foo: foo
  };
  var a = 1;
  foo('Gopal'); // 1Gopal
  obj.foo('Gopal'); // 2Gopal
  foo.call(obj, 'Gopal'); // 2Gopal
  foo.apply(obj, ['Gopal']); // 2Gopal
  let bar = foo.bind(obj, 'Gopal');
  bar(); // 2Gopal

this ignored

如果你把null undefined this callapply bind , these values will be ignored when calling, and the default binding rules are actually applied

 function foo() {
  console.log(this.a);
}
var a = 2;
foo.call(null); // 2

Use this usage to "expand" an array using apply(..) and pass it as a parameter to a function.
Similarly, bind(..) can curry parameters (preset some parameters)

 function foo(a, b) {
    console.log("a:" + a + ", b:" + b);
  }
  // 把数组“展开”成参数
  foo.apply(null, [2, 3]); // a:2, b:3
  // 使用 bind(..) 进行柯里化
  var bar = foo.bind(null, 2);
  bar(3); // a:2, b:3

newbinding

When we use the constructor new an instance, what does this instance this point to?

Let's first look at using new to call a function, or what operation will be performed when a constructor call occurs, as follows:

  • Create (or construct) a brand new object
  • This new object will be connected by [[prototype]], binding the object (instance) __proto__ and the constructor prototype
  • This new object will be bound to the function call this
  • If the function does not return another object, then the function call in the new expression will automatically return the new object

The principle implementation is similar to the following:

 function create (ctr) {
    // 创建一个空对象
    let obj = new Object()
    // 链接到构造函数的原型对象中
    let Con = [].shift.call(arguments)
    obj.__proto__ = Con.prototype
    // 绑定this
    let result = Con.apply(obj, arguments);
    // 如果返回是一个对象,则直接返回这个对象,否则返回实例
    return typeof result === 'object'? result : obj;
}

Note: let result = Con.apply(obj, arguments); actually means that the new object will be bound to the function call this

 function Foo(a) {
    this.a = a;
  }
  var bar = new Foo(2);
  console.log(bar.a); // 2

Special Case - Arrow Functions

The four rules we introduced earlier already cover all normal functions. But ES6 introduced a special function type that cannot use these rules: arrow functions

Arrow functions do not use the four standard rules of this , but are determined according to the outer (function or global) scope at the time of definition this . That is to say, the arrow function will not create its own this , it will only inherit from the upper level of its own scope chain this

 function foo() {
  // 返回一个箭头函数
  // this 继承自 foo()
  return (a) => {
    console.log(this.a);
  }
};

var obj1 = {
  a: 2
};
var obj2 = {
  a: 3
};
var bar = foo.call(obj1);
bar.call(obj2); // 2, 不是 3 !

foo() The internally created arrow function will capture the foo() when calling this . foo()thisobj1bar (引用箭头函数)的this Set to obj1 , the binding of arrow functions cannot be modified. ( new also doesn't work!)

Summary - this priority

Determine whether it is an arrow function, if it is, follow the rules of arrow function

Otherwise, if you want to determine the this binding of a running function, you need to find the direct calling location of the function. After finding it, you can apply the following four rules in order to determine the binding object of this

  1. Called by new ? bind to the newly created object
  2. Called by call or apply (or bind )? Binds to the specified object
  3. Called by a context object? Bind to that context object
  4. Default: bind to undefined in strict mode, otherwise bind to the global object

As shown below:

refer to


Gopal
366 声望77 粉丝