In-depth understanding of JavaScript this

definition

This is the internal object automatically generated when the function is running, that is, the object that calls the function. (It is not necessarily a precise definition, but it is easy to understand.) In most cases, the value of this is determined by the way the function is called. It cannot be set by assignment during execution. It may be different for each execution value.

Global execution environment (outside function)

In the global execution environment, this always points to the global object, whether in strict mode or non-strict mode.

Code 1

console.log(this.document === document);   //true

// 在浏览器中,window对象也就是全局对象(global object)
console.log(this === window);   //true

this.a  = 37;
console.log(window.a);   //37

Function execution environment (inside function)

In the function execution environment, the value of this depends on how the function is called.

There are four main ways to call functions:

  • Direct function call
  • Object method call
  • Constructor call
  • call / apply / bind
  • Arrow function (ES6)

Direct function call

When the following code is executed in non-strict mode, the value of this will point to the global object; and in strict mode, the value of this will default to undefined.

Code 2

/* 非严格模式 */
function f1 () {
  return this;
}
console.log(f1() === window);   //true

// in node;
console.log(f1() === global);   //true

/* 严格模式 */
function f2 () {
  'use strict'
  return this;
}
console.log(f1() === undefined);   //true

call / apply / bind to change the direction of this

call / apply

The usage of call and apply is very similar, except that the input form of the following parameters is different.

Code 3

function add(c, d) {
  return this.a + this.b + c + d;
}

var o = {a: 1, b: 3};

// call的第一个参数 是对象,也就是this的指向对象。后面的参数就是函数arguments对象的成员
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16

// call的第一个参数 是对象,也就是this的指向对象。后面的参数是数组,数组里的成员也就是函数arguments对象成员
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34

Note that when using call and apply , when the value of the first parameter passed in is not an object, JavaScript will try to use the ToObject operation to convert it into an object.

Code 4

function bar() {
  console.log(Object.prototype.toString.call(this));
}

bar.call(7); // [object Number]

bind method

ECMAScript 5 introduced  Function.prototype.bind . Calling f.bind(someObject) will create a function with the same function body and scope as f, but in this new function, this will be permanently bound to the first parameter of bind, no matter how the function is Called.

Code 5

function f() {
  return this.a;
}

var g = f.bind({a: 'azerty'}); //生成一个绑定函数g
console.log(g()); // azerty

var o = {a: 10, f: f, g: g};
console.log(o.f(), o.g());   //10, azerty

//需要注意的是,绑定函数不可以再bind
var h = g.bind({a: 'foo'});
console.log(h());  //azerty,不会变成foo

Object method call

When a function is called as a method in an object, their this is the object that called the function.

In the following example, when of() is called, this in the function will be bound to the o object.

var prop = 36;
var o = {
  prop: 37,
  bar: function() {
    return this.prop;
  }
};
console.log(o.bar());  //37

Constructor call

Code 6

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.introduce = function () {
    console.log('My name is ' + this.name + ', I\'m ' + this.age);
  };
}
var Joseph = new Person('Joseph', 19);
Joseph.introduce();  // "My name is Joseph, I'm 19"

From the above code, you can clearly see that this is bound to the newly created object.

Note : When the default value returned by the constructor is an object referenced by this, you can manually set it to return other objects. If the return value is not an object, return this. (This sentence seems more difficult to understand, let's look at the next example).

Code 7

function Fn2() {
  this.a = 9;  // dead code
  return {a: 10};
}
var o = new Fn2();
console.log(o.a);  // 10

This example shows that when the constructor returns an object, the value of this will become the object returned at this time. 'this.a = 9'became a zombie code.

Arrow function

In  Arrow functions , the value of this is determined by the closed execution environment. In the global environment, then it is assigned to the global object.

var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true

What's more important is that it is different from other situations in that no matter how the function is called, the value of this above is always the global object. Call / bind cannot change its value.

Code 8

// 作为对象方法被调用
var obj = {foo: foo};
console.log(obj.foo() === globalObject); // true

// 尝试用 call 改变this的值
console.log(foo.call(obj) === globalObject); // true //this的值并未变成obj

// 尝试用 bind 改变this的值
foo = foo.bind(obj);
console.log(foo() === globalObject); // true

Case study

I have read all the knowledge points in this article. Let's look at a few cases and check our grasp.

Example 1

var prop = 36;
var o = {
  prop: 37,
  bar1: function() {
    function foo1() {
      return this.prop;
    }
    return foo1;
  },
  bar2: function() {
    var foo2  = (() => this.prop); //ES6箭头函数
    return foo2;
  }
};
console.log('result1:'+o.bar1()()); // result1 ?
console.log('result2:'+o.bar2()()); // result2 ?
var fn2 = o.bar2;
console.log('result3:'+fn2()()); // result3 ?

First reveal the answer: Example 1 result1 = 36, result2 = 37, result3 = 36. My understanding is that in result1, the execution of o.bar1() causes the foo function to return to the global environment, and then the execution becomes the global execution, so the value of 36 in the global is obtained. What about result2? Because this is in the arrow function. Its value will not change. So this still points to o. Then why did result3 change again? Because at this time'var fn2 = o.bar2' is equivalent to redefining a function, and of course the value of this becomes a global object.

// 相当于这样
var fn2 = function() {
    function foo1() {
      return this.prop;
    }
    return foo1;
  }
fn2()();

Example 2

function sum(a,b) {
 return a+b;
};
var o = {
  num: 1,
  fn: function() {
        function handle() {
          return this.num = sum(this.num, this.num);
        }
    handle();
  }
};
console.log('result:'+o.fn());  // result ?

Also announce the answer first: result = undefined, you can see with the console that this points to window instead of o. This is a pit that is easier to fall into (generally considered to be the original language design error, which has been criticized a lot). It seems that the function is called by the object method, but in fact, if you are careful, we can see. The execution of the handle function has no objects in front. In this case, this points to the global object. The solution is also very simple.

// 1、取消 handle函数的定义,直接在对象的方法中使用this
fn2: function() {
    this.value = sum(this.value, this.value);  //2
},
///2、使用变量保存外部函数的this。
fn3: function() {
    var that = this;   // that == o
    function handle() {
        that.value = add(that.value, that.value);
    }
    handle();
}

In addition, I did a series of front-end interview questions in my spare time. Friends in need can click on the blue letters below to read:

Guess you like

Origin blog.csdn.net/hugo233/article/details/113062941