乐趣区

ES6的class本质和react中需要使用bindthis的原因

ES6 的 class

我们知道 ES6 新出一个规范是使用 class 关键字来定义一个类,这在以前是没有的
在以前,需要在 javascript 里实现面向对象,就需要使用 prototype

什么是面向对象?

面向对象有如下几个基本特征,通常认为,只要实现了这几个特性,就认为是实现了面向对象:
1. 封装
2. 继承
3. 多态
这里不对三个基本特征的做详细展开。只需要知道,javascript 实现继承和多态都需要用到 prototype
而 ES6 的 class,本质上还是 ES5 的 prototype 的语法糖

什么是语法糖?

语法糖就是提供了一种全新的方式书写代码,但是其实现原理与之前的写法相同。
语法糖可以说是广泛存在于各种计算机代码中,包括 C 语言中的 a[i]其实就是 *a+ i 的语法糖。而今天对于我们来说,a[i]其实已经很普遍和常用了,所以也没有人提这是语法糖这回事了。因为终极来说,所有语言都是汇编语言的语法糖:)

class foo{constructor(){ }
    a(){}
    b(){}
}

// 等价于
function foo(){};
foo.prototype = {constructor(){},
    a(){},
    b(){},
}

ES6 的 class 跟 ES5 的定义方式用几个不同

1. 没有变量提升
2.this 指向不同

先来看 1:

test(); // 输出 'test'
function test(){console.log('test');
}

我们知道即便在定义 test 函数之前执行 test(),也是会得到结果的。这是因为解析 javascript 代码的时候会把所有的 function test(){}这类代码(即正常定义函数)都提升到最上方去定义。

但是这么执行不行:

let a = new test(); // 报错
class test(){} 

再看 2:

class Animal {printName () {this.print('Hello animal');
    }
    print(name) {console.log(name);
    }
}
const animal = new Animal();
animal.printName(); // 输出 'Hello animal'
const {printName} = animal;
printName(); // 报错: Cannot read property 'print' of undefined

如果执行了 bind

class Animal {constructor(){this.printName = this.printName.bind(this);
    }
    printName () {this.print('Hello animal');
    }
    print(name) {console.log(name);
    }
}
const animal = new Animal();
animal.printName(); // 输出 'Hello animal'
const {printName} = animal;
printName(); // 输出 'Hello animal'

发生了什么?

animal 中的 printName 函数的 this 原本指向的是执行环境,如果不执行 bind,那么 printName 函数的 this 指向 window。
在执行 new Animal()的时候,如果执行了 bind,那么从 animal 中获取的 printName 函数,其 this 对象已经被绑定到了 constructor 的 this, 即 animal 上。
以下是 this 的指向

那么我们为什么需要在 react 里 bind(this)呢?

简单来说,就是 react 在调用 render 方法的时候,会先把 render 里的方法赋值给一个变量(比如变量 foo),然后再执行 foo()。
具体来说,以典型的绑定点击事件为例

<div onClick={this.clickHandler}></div>

react 构建虚拟 DOM 的时候,会把 this.clickHandler 先赋值给一个变量。我们假设是变量 clickFunc = this.clickHandler;
然后,把虚拟 DOM 渲染成真实 DOM 的时候,会把 onClick 属性值替换成 onclick,并给 onclick 赋值 clickFunc

在复杂的情况中,可能存在多次传递,如果不进行 bind,那么 this 的指向是一定会丢失的。

为什么 react 不自己集成 bind 到生命周期里?

1 是,没有特别合适集成 bind 的地方
2 是,并不是所有的函数都需要 bind
3 是,随意集成 bind 可能导致效率低下

退出移动版