JavaScript - this 原理

# 关于 this:

普通函数中的this: 1.this 总是代表它的直接调用者(js的this是执行上下文), 例如 obj.func ,那么func中的this就是obj 2.在默认情况(非严格模式下,未使用 ‘use strict’),没找到直接调用者,则this指的是 window (约定俗成) 3.在严格模式下,没有直接调用者的函数中的this是 undefined 4.使用call,apply,bind(ES5新增)绑定的,this指的是 绑定的对象

箭头函数中的this: 箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象),而不是执行时的对象, 定义它的时候,可能环境是window; 箭头函数可以方便地让我们在 setTimeout ,setInterval中方便的使用this

有点绕, 看不懂继续往下看

# 例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  var obj = {
    foo: function () { console.log(this.bar) },
    bar: 1
  };
  
  var foo = obj.foo;
  var bar = 2;
  
  obj.foo() // 1
  foo() // 2

# 现象解释:

this 指的是函数运行时所在的环境(上下文 context)。对于 obj.foo() 来说,foo 运行在 obj 环境,所以 this 指向 obj;对于 foo() 来说,foo 运行在全局环境,所以 this 指向全局环境。

# 原理:

this 设计与 JavaScript 内存的数据结构有关系, 回忆 JavaScript 数据类型的知识以及各数据类型存储的方式, 上面的例子的变种:

1
2
3
4
  var f = function () {
    console.log(x);
  };
  

引用型对象将对象内容存储在 堆内存中, 地址存储在变量的栈内存中. 也就是说 obj 存储在栈内存上, 内容为一个堆内存的地址, 而堆内存存储的才是对象的真正内容. 同理 JavaScript 中函数也是一个对象, foo 也是一个堆内存地址, 函数内容存储另外的内存上.

而在 JavaScript 中, 函数内可以引用当前运行环境的变量, 函数又可以在不同的环境中运行, 那么当函数在不同环境中运行时, 如何引用当前运行环境中的变量呢, this 就是起这样的作用的, 在函数体中指代当前的运行环境, 以引用 context 中的变量.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  var f = function () {
    console.log(this.x);
  }
  
  var x = 1;
  var obj = {
    f: f,
    x: 2,
  };
  
  // 单独执行
  f() // 1
  
  // obj 环境执行
  obj.f() // 2
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  // 箭头函数
  var x = 2;

  function F() {
    this.x = 3;
    this.foo = () => {
      var x = 1;
      console.log(x, this.x)
    }
  }
  
  const f = new F();
  const foo = f.foo;
  foo();  // 1, 3
  f.foo(); // 1, 3

# bind, apply 和 call

apply 和 call 都是在指定的环境中调用函数, 本质是设置函数体内的 this 对象的值, bind 是直接设置函数的 this 值.

  • apply

    apply(context, arguments) context 代表运行函数的作用域, arguments 参数数组

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
      function sum(n1, n2) {
        return n1 + n2;
      }
    
      function callSum(n1, n2) {
        return sum.apply(this, arguments);
        // return sum.apply(this, [n1, n2]);
      }
    
      callSum(4, 5); // 9
    
  • call

    call(context, … ) context 代表运行函数的作用域, 剩余参数全部列出来.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
      window.color = "red";
      var o = { color: "blue" };
    
      function getColor() {
        alert(this.color);
      }
    
      getColor.call(this); // red
      getColor.call(window); // red
      getColor.call(o); // blue
    
  • bind

    创建一个函数实例, 该函数实例的 this 值会绑定到传给 bind() 函数的值.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
      window.color = "red";
      var o = { color: "blue" };
    
      function getColor() {
        alert(this.color);
      }
    
      var obj = getColor.bind(o);
      obj(); // blue
    
Licensed under CC BY-NC-SA 4.0