React解密:React类组件中为什么要绑定this?

我们在开发react组件的时候,总是会在构造函数中或者是事件中去通过bind方法去绑定this。不知道你有没有思考过这是为什么呢?难道不绑定this就不可以吗?为什么react类组件中要绑定this,绑定的方式又有哪些呢?

直白点来说。其实react中绑定this和react本身没有半毛钱关系。哈哈哈,意不意外?惊不惊喜?所以,既然是这样,我们就来说说this绑定的哪些事儿。

默认绑定

function test() {
    console.log(this)
}
test()    //    window

这是一个普通的函数调用。在这种情况下,display() 方法中的 this 在非严格模式下指向 window 或 global 对象。在严格模式下,this 指向 undefined

隐式绑定

var obj = {
 name: 'Saurabh',
 display: function(){
   console.log(this.name); // 'this' 指向 obj
  }
};

obj.display(); // Saurabh 

当我们以一个 obj 对象来调用这个函数时,display() 方法内部的 this 指向 obj。但是,当我们将这个函数引用赋值给某个其他变量并使用这个新的函数引用去调用该函数时,我们在 display() 中获得了不同的this值。

var name = "uh oh! global";
var outerDisplay = obj.display;
outerDisplay(); // uh oh! global

当我们调用 outerDisplay() 时,我们没有指定一个具体的上下文对象。这是一个没有所有者对象的纯函数调用。在这种情况下,display() 内部的 this 值回退到默认绑定。现在这个 this 指向全局对象,在严格模式下,它指向 undefined。在将这些函数以回调的形式传递给另一个自定义函数、第三方库函数或者像 setTimeout 这样的内置JavaScript函数时,上面提到的判断方法会特别实用。

//setTimeout 的虚拟实现
function setTimeout(callback, delay){
   //等待 'delay' 数个毫秒
   callback();
}

setTimeout( obj.display, 1000 );

当调用 setTimeout 时,JavaScript 在内部将 obj.display 赋给参数 callback

callback = obj.display;

这种赋值操作会导致 display() 函数丢失其上下文。当此函数最终在 setTimeout 函数里面被调用时,display()内部的 this 的值会退回至默认绑定

var name = "uh oh! global";
setTimeout( obj.display, 1000 );

// uh oh! global

明确绑定

为了避免这种情况,我们可以使用 明确绑定方法,将 this 的值通过 bind() 方法绑定到函数上。

var name = "uh oh! global";
obj.display = obj.display.bind(obj); 
var outerDisplay = obj.display;
outerDisplay();

// Saurabh

现在,当我们调用 outerDisplay() 时,this 的值指向 display() 内部的 obj

即时我们将 obj.display 直接作为 callback 参数传递给函数,display() 内部的 this 也会正确地指向 obj

非react场景

我们来创建一个名为Foo的类,如果我们不将 this 绑定到事件上,事件内的值会变成 undefined。正如我上文解释的那样,这是由 JavaScript 中 this 绑定的方式决定的,与React的工作方式无关。因此,让我们删除 React 本身的代码,并构建一个类似的纯 JavaScript 示例,来模拟此行为。

class Foo {
  constructor(name){
    this.name = name
  }

  display(){
    console.log(this.name);
  }
}

var foo = new Foo('Saurabh');
foo.display(); // Saurabh

//下面的赋值操作模拟了上下文的丢失。 
//与实际在 React Component 中将处理程序作为 callback 参数传递相似。
var display = foo.display; 
display(); // TypeError: this is undefined

我们不是模拟实际的事件和处理程序,而是用同义代码替代。正如我们在 React 组件示例中所看到的那样,由于将处理程序作为回调传递后,丢失了上下文,导致 this 值变成 undefined。这也是我们在这个纯 JavaScript 代码片段中观察到的。

类声明类表达式的主体以 严格模式 执行,主要包括构造函数、静态方法和原型方法。Getter 和 setter 函数也在严格模式下执行。所以我们需要绑定this。

class Foo {
  constructor(name){
    this.name = name
    this.display = this.display.bind(this);
  }

  display(){
    console.log(this.name);
  }
}

var foo = new Foo('Saurabh');
foo.display(); // Saurabh

var display = foo.display;
display(); // Saurabh

我们不仅可以在构造函数中执行此操作,也可以在其他位置执行此操作。

class Foo {
  constructor(name){
    this.name = name;
  }

  display(){
    console.log(this.name);
  }
}

var foo = new Foo('Saurabh');
foo.display = foo.display.bind(foo);
foo.display(); // Saurabh

var display = foo.display;
display(); // Saurabh

由于构造函数是所有初始化发生的地方,因此它是编写绑定事件语句最佳的位置。

为什么箭头函数不需要绑定this?

在 React 组件内,我们有另外两种定义事件处理程序的方式。

  • 公共类字段语法
class Foo extends React.Component{
  handleClick = () => {
    console.log(this); 
  }
 
  render(){
    return (
      <button type="button" onClick={this.handleClick}>
        Click Me
      </button>
    );
  }
}

ReactDOM.render(
  <Foo />,
  document.getElementById("app")
);
  • 回掉中的箭头函数
    class Foo extends React.Component{
     handleClick(event){
        console.log(this);
      }
     
      render(){
        return (
          <button type="button" onClick={(e) => this.handleClick(e)}>
            Click Me
          </button>
        );
      }
    }
    
    ReactDOM.render(
      <Foo />,
      document.getElementById("app")
    );
    

    这两个都使用了ES6引入的箭头函数。当使用这些替代方法时,我们的事件处理程序已经自动绑定到了组件实例上,并且我们不需要在构造函数中绑定它。

    原因是在箭头函数的情况下,this 是有词法约束力的。这意味它可以使用封闭的函数上下文或者全局上下文作为 this 的值。

    在公共类字段语法的例子中,箭头函数被包含在 Foo 类中或者构造函数中,所以它的上下文就是组件实例,而这就是我们想要的。

    在箭头函数作为回调的例子中,箭头函数被包含在 render() 方法中,该方法由 React 在组件实例的上下文中调用。这就是为什么箭头函数也可以捕获相同的上下文,并且其中的 this 值将正确的指向组件实例。

    在 React 的类组件中,当我们把事件处理函数引用作为回调传递过去。

    <button type="button" onClick={this.handleClick}>Click Me</button>
    
    

    事件处理程序方法会丢失其隐式绑定的上下文。当事件被触发并且处理程序被调用时,this的值会回退到默认绑定,即值为 undefined,这是因为类声明和原型方法是以严格模式运行。

    当我们将事件处理程序的 this 绑定到构造函数中的组件实例时,我们可以将它作为回调传递,而不用担心会丢失它的上下文。

    箭头函数可以免除这种行为,因为它使用的是词法 this 绑定,会将其自动绑定到定义他们的函数上下文。

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值