原文在我的 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 基础够扎实,我想你是不会点进来看这篇文章的。
这里的问题无非就牵扯到两个点:
- this 指向问题
- 箭头函数的特性
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 来指向。
如果哪里有不对的地方,欢迎指正,感谢!