JavaScript中的this

JavaScript中的this

Tags: JavaScript 开发


[toc]

最近在学习ReactNative,免不了被坑在JS上,跟Java和Objective-C不同,JS方法里的this显得很另类,有点令人困惑。

什么是this

要说清this是什么,可以从Function.prototype.call()方法说起,这个方法近似于function调用的原始实现,它的方法签名是这样的:

fun.call(thisArg[, arg1[, arg2[, ...]]])

其中thisArg代表的就是方法里的this,而其他参数,则是arg1arg2以可变长度参数的形式传递。调用的过程和结果如下:

function hello(thing) {  
  console.log(this + " says hello " + thing);
}
hello.call("Yehuda", "world") //=> Yehuda says hello world  

JavaScript里的其他方法调用方式大致可以看做这个调用的语法糖[1]

在ES5前,function里的this默认会是window或者global对象,而使用了严格模式的ES5,全局function里的this将会是undefined

// this:
hello("world")
// desugars to:
hello.call(undefined, "world");  

对于一个方法来说this并不是一成不变,具体是什么取决于调用这个方法的时机:

function hello(thing) {  
  console.log(this + " says hello " + thing);
}

person = { name: "Brendan Eich" }  
person.hello = hello;

person.hello("world") // still desugars to person.hello.call(person, "world")

hello("world") // "[object DOMWindow]world" //具体环境决定

通常我们把this形容为上下文就是因为这个。

那么问题来了

JavaScript中,我们经常要将方法传递给其他方法,当做回调函数,比如最近学习的ReactNative,JSX中经常要将方法作为参数传递:

render() {
    return (
        <ListView dataSource={this.props.dataSource} renderRow={this._renderRow}
/>
    )
}
...

_renderRow(itemData) {
    return (
        <TouchableHighlight onPress={this._rowPressed} >
            ...not important
        </TouchableHighlight>
    )
}

_rowPressed() {
    ...not important
}
 

细心的盆友看出来了,我这样写是不行的,ListView的renderRow方法是由react执行的,它调用_renderRow的时候,上下文this必然不是声明render方法所在的实例,_renderRow方法里的this._rowPressed必然是undefined

JavaScript中Function是一等类型,可以被显式传递,所以必然会出现上下文不对的问题,在没有深入研究过的开发者手中,往往忽视这个问题,写出大坑。

如何使this确定

bind

这个问题也困扰了JS开发者很久,如何解决呢?
在ES5之前,人们通过一个简单方法帮助改正错误的this:

var person = {  
  name: "Brendan Eich",
  hello: function(thing) {
    console.log(this.name + " says hello " + thing);
  }
}
var boundHello = function(thing) { return person.hello.call(person, thing); }
boundHello("world"); 

尽管我们可以在任何地方调用boundHello方法,可是boundHello实际上是对上下文和原始hello方法进行了封装,所以最终hello里的this始终是person

这个方法最终被ES5引入的Function.prototype.bind方法所替代,所以现在我们的代码里可以这样使用:

var boundHello = person.hello.bind(person);  
boundHello("world") // "Brendan Eich says hello world"  

所以,前文里面困扰了我的ReactNative问题,解决方法也许是:

render() {
    return (
        <ListView dataSource={this.props.dataSource} renderRow={this._renderRow.bind(this)}
/>
    )
}
...

_renderRow(itemData) {
    return (
        <TouchableHighlight onPress={this._rowPressed.bind(this)} >
            ...not important
        </TouchableHighlight>
    )
}

_rowPressed() {
    ...not important
}

而考虑到ReactNative,render方法的性能要求,不能在render过程中创建新方法,所以最终解决方案是这样的:

constructor() {
    super()
    this._renderRow = this._renderRow.bind(this)
    this._rowPressed = this._rowPressed.bind(this)
}
render() {
    return (
        <ListView dataSource={this.props.dataSource} renderRow={this._renderRow}
/>
    )
}
...

_renderRow(itemData) {
    return (
        <TouchableHighlight onPress={this._rowPressed} >
            ...not important
        </TouchableHighlight>
    )
}

_rowPressed() {
    ...not important
}

另外bind(this)在ES6里还有新语法糖:

this._renderRow = ::this._renderRow
this._rowPressed = ::this._rowPressed

不过在Babel的实现中说明了这个语法是highly experimental的,所以最后用不用,follow your heart~

Fat Arrow

ES6引入了箭头匿名方法,它的方法体里的this是会绑定给初始化它的实例的,所以constructor里的方法也可以改为:

this._renderRow = (rowData) => this._renderRow(rowData)
this._rowPressed = () => this._rowPressed()

这种方法的缺点是参数个数是固化的,如果想处理原方法的更多参数,需要在这里显式写出这些参数,不太灵活,毕竟我们只想绑定this,不关心参数。

鸣谢

bind新语法
what's 'this'
jsx中不推荐使用bind和箭头函数
mozilla bind文档

谢谢收看


  1. 严格来说不是,只是近似,方便下文讲解。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容