1

原文在我的 github 中,欢迎订阅

我们平常写React是这样的:

class HelloMessage extends React.Component {

  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this); //绑定this
  }
  
  handleClick(){
    console.log( this )
  }

  render() {
    const { count } = this.state;
    
    return (
      <button onClick={ this.handleClick } >Hello</button>
    );
  }
}

ReactDOM.render(
  <HelloMessage/>,document.getElementById('root')
);

上面代码可以完好运行,handleClick 绑定好 this 后,打印如下:

HelloMessage {props: {…}, context: {…}, refs: {…}, updater: {…}, _reactInternalFiber: Hr, …}
    context: {}
    props: {}
    refs: {}
    state: null
    updater: {isMounted: ƒ, enqueueSetState: ƒ, enqueueReplaceState: ƒ, enqueueForceUpdate: ƒ}
    _reactInternalFiber: Hr {tag: 1, key: null, elementType: ƒ, type: ƒ, stateNode: HelloMessage, …}
    __proto__: b

我们把.bind(this)去掉,再打印一次:

undefined

OK,果然this就没有任何东西了。

但是如果把 handleClick 改为箭头函数的写法,就不需要 bind(this) 了:

 handleClick = () => {
    console.log( this )
 }

更改后,this 也能正常打印出来。

我们先看一下render方法中被Babel编译后的样子:

  render() {
    return React.createElement(
      'button',
      { onClick: this.handleClick },
      'Hello'
    );
  }

React 通过 React.createElement 方法模拟 document.createElement 来创建 DOM(当然React创建的是虚拟DOM)。 属性中 onClick 指向 this.handleClick 方法,看起来都没有问题。

下面我们不用 React 来实现上面 this 的现象:

// 创建DOM
function createElement(dom, params) {
  var domObj = document.createElement(dom);
  domObj.onclick = params.onclick;
  domObj.innerHTML = 'Hello';
  document.body.appendChild(domObj);
}

// Demo类
class Demo {

  handleClick() {
    console.log(this);
  }

  render() {
    createElement('button', {
      onclick: this.handleClick
    });
  }
}

// 执行render
new Demo().render();

小伙伴们猜猜,这里的 this 指向什么(手动滑稽)?

来打印结果:

<button>Hello</button>

啊哈哈哈啊,没错 this 并不是 undefined,也没有指向 Demo 对象,而是创建的 button DOM对象本身。

现在再把 handleClick 改为箭头函数:

// Demo类
class Demo {

  handleClick = () => {
    console.log(this);
  }

  render() {
    createElement('button', {
      onclick: this.handleClick
    });
  }
}

// 执行render
new Demo().render();

来看看这次打印的 this :

Demo {handleClick: ƒ}
    handleClick: () => { console.log(this); }
    __proto__: Object

卧槽,居然指向了 Demo 对象,这样就可以在 handleClick 里随意访问 Demo 里的属性和方法了!

说明

其实,如果你 JavaScript 基础够扎实,我想你是不会点进来看这篇文章的。
这里的问题无非就牵扯到两个点:

  1. this 指向问题
  2. 箭头函数的特性

this 指向问题

我打赌,上面的 Demo 实例很多人看的云里雾里的,所有我把它转成 ES5 的写法:

function createElement(dom, params) {
  var domObj = document.createElement(dom);
  domObj.onclick = params.onclick;
  domObj.innerHTML = 'Hello';
  document.body.appendChild(domObj);
}

// 创建 Demo 类,相当于 class Demo {} 的写法
function Demo() {

}

// 在原型上挂载 handleClick 方法
Demo.prototype.handleClick = function() {
   console.log(this);
};

Demo.prototype.render = function() {
  createElement('button', {
    onclick: this.handleClick
  });
};

new Demo().render(); // 运行 render 方法

打印结果:

<button>Hello</button>

严重注意:在ES6 class 内定义方法时,如果不是箭头函数,方法是挂载在 prototype 原型对象上的!

那么下面,把 handleClick 用箭头函数的方式写出来:

function createElement(dom, params) {
  var domObj = document.createElement(dom);
  domObj.onclick = params.onclick;
  domObj.innerHTML = 'Hello';
  document.body.appendChild(domObj);
}

// 创建 Demo 类,相当于 class Demo {} 的写法
function Demo() {
    // 相当于箭头函数 不了解的同学请恶补一下吧
    var _this = this;
    this.handleClick = function(){
        console.log( _this );
    }
}

Demo.prototype.render = function() {
  createElement('button', {
    onclick: this.handleClick
  });
};

new Demo().render(); // 运行 render 方法

这时候再打印:

Demo {handleClick: ƒ}
    handleClick: ƒ ()
    __proto__: Object

厉害了哈~ this 指向了想要的对象环境中。

总结

通过以上代码解析,能知道 React 中,在不适用箭头函数时,需要通过 bind 将函数内 this 指向当前对象才能正常访问。

而使用箭头函数时,由于箭头函数的特性,函数内的 this 就是当前对象上下文,所以不需要 bind 来指向。

如果哪里有不对的地方,欢迎指正,感谢!


alwaysVe
1.9k 声望162 粉丝

前进