欢迎转载,请支持原创,保留原文链接:blog.ilibrary.me

很多新手,特别是非web前端的新手,进来的时候经常遇到这种问题:

undefined is not an object (evaluating 'this.state.username')

如果我没有猜错,抛错的函数应该被当做一个函数指针传给某个回调函数了。正确的做法是在函数做参数的时候bind(this).

下面用ListView的回调函数绑定来说明正确用法

请看代码注释

_renderRow() {
	console.log(this.state.username);
}

_renderFooter(){
	console.log(this)// 不会抛错,this指向window对象
	console.log(this.state) // 抛错,window对象没有state属性
}

render(){
	return(
		<ListView dataSource={this.state.dataSource}

     		renderRow={this._renderRow.bind(this)} // 正确用法

     		onEndReached={this._fetchNextPage.bind(this)} // 正确用法
         	renderFooter={this._renderFoot}// 错误用法,忘记了bind(this)
                            />);

原理解析

所有面向对象的实例函数被调用的时候都会传递一个隐式参数this, 在javascript里面把函数作为参数传递的时候会丢掉这个this, 也就是这个函数的上下文丢失了。这种情况下this 怎么办?

this会指向全局window对象,window是没有state属性的。state属性是React.Component的默认属性,不是window的默认属性,所以访问this.state会出错:

_renderFooter() {
	console.log(this)// 不会抛错,this指向window对象
	console.log(this.state) // 抛错,window对象没有state属性
}

要想访问在_renderFooter里面正确访问’this.state’, 有两种方法,一种是this._renderFooter.bind(this), 一种是用闭包.

方法1, bind(this)

可以通过显示给函数对象bind this指针的方法来解决this指向不对的问题。

render(){
	return(
		<ListView dataSource={this.state.dataSource}

     		renderRow={this._renderRow.bind(this)} // 正确用法

     		onEndReached={this._fetchNextPage.bind(this)} // 正确用法
         	renderFooter={this._renderFoot().bind(this)}
                            />);

方法2, 闭包

还可以通过传递匿名函数(闭包)作为函数参数,闭包里面调用对应的函数时会自动绑定正确的this指针.

render(){
	return(
		<ListView dataSource={this.state.dataSource}

     		renderRow={this._renderRow.bind(this)} // 正确用法

     		onEndReached={this._fetchNextPage.bind(this)} // 正确用法
         	renderFooter={()=>{
         	// 闭包,闭包把当前的上下文(this)给记录了下来。
         	this._renderFoot()
         	}}
                            />);

参考

JavaScript对象模型-执行模型, 讲解非常详细

JavaScript系列—-作用域链和闭包