This 与闭包

229 阅读3分钟

这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

This 原理

JS 之所以有this的设计取决于内存的数据结构

  1. 对象

     let obj = {foo: 5}
    

    上文是将一个对象赋值给obj。JS 引擎会先生成对象,再将对象内存地址赋值obj。即实际是以这样的形式保存:

    image-20211002202752118

    foo属性值保存在value属性。

  2. 函数

    问题在于属性值是函数时:

     let obj = { foo: function () {} }
    

    这时,JS 会将函数单独保存在内存,再将函数地址赋值给foovalue属性。

    image-20211002203318284

    由于函数可以在不同环境上下文执行

     let f = function () {};
     let obj = { f: f };
     // 单独执行
     f();
     // obj 环境执行
     obj.f();
    
  3. 环境变量:

    JS 允许函数体内部引用当前环境其他变量:

     let f = function () {
       console.log(x); // x 由运行环境提供
     }
    

    问题来了,由于函数可在不同环境执行。所以需要一种机制:在函数体内获得当前运行环境(context)。因此,This 出现了,其设计目的就是在函数内部指代函数当前运行环境

     let f = function () {
       console.log(this.x);  // 指当前运行环境的`x`
     }
    

    再有:

     let f = function () {
       console.log(this.x);
     }
     ​
     let x = 1;
     let obj = {
       f: f,
       x: 2,
     }
     // 单独执行
     f() // 1
     // obj 环境
     obj.f() // 2
    

    上面代码中:函数f全局环境执行,this.x指向全局环境x

    image-20211002204825652

    obj环境执行,指向obj.x

    image-20211002204904330

    所以为什么let foo = obj.foofoo就变成在全局环境执行:

     let obj = {
       foo: function () { console.log(this.bar) },
       bar: 1
     }
     ​
     let foo = obj.foo;
     let bar = 2;
     ​
     obj.foo() // 1
     foo() // 2
    

    因为obj.foo()是通过obj找到foo,所以在obj环境执行;一旦let foo = obj.foo,变量foo直接指向函数本身,foo()变成在全局环境执行。

this 与 function

函数执行时,this 关键字并不会指向正在运行的函数本身,而是指向调用该函数的对象。所以,如果你想在函数内部获取函数自身的引用,只能使用函数名或者使用arguments.callee属性(严格模式下不可用),如果该函数是一个匿名函数,则你只能使用后者。

this 与箭头函数

如定义为箭头函数,this 值与定义时环境无关。

this 与 Class

原型和静态方法(static)的 this

调用原型或静态方法时没指定 this 的值时,方法内的 this 将为undefined

 class Animal {
   speak() { return this }
   static eat() { return this }
 }
 ​
 let obj = new Animal();
 // new-会绑定this
 obj.speak();  // Animal{}
 ​
 Animal.eat()  // class Animal
 let eat = Animal.eat;
 eat();  // undefined

如果静态方法包含this关键字,这个this指的是类,而不是实例。

new

new关键字进行如下操作:

  1. 创建新对象{}
  2. {}添加__proto__属性指向构造函数的原型对象( .prototype
  3. 将该对象的 this 指向构造函数并传参调用构造函数
  4. 该函数没有返回对象就返回this

实例属性

实例属性需定义在在类的方法里:

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

静态或原型的属性需定义在类定义外面

案例

  • 闭包与 for 循环

    for (var i = 0; i < 5; i++) {
        (function fn(i) {
            setTimeout(function() {
                console.log(i)
            }, 0)
        })(i)
    }	// 0 1 2 3 4
    for (var i = 0; i < 5; i++) {
        setTimeout(function() {
            console.log(i)
        }, 0)
    }	// 5 5 5 5 5
    

参考链接

JavaScript 的 this 原理 - 阮一峰的网络日志

\