是时候看清JS 中的 this 了

this 到底是个什么东西

MDN 上对于 this 如此定义:

A property of an execution context (global, function or eval) that, in non–strict mode, is always a reference to an object and in strict mode can be any value.

上面这段话,主要讲的是 this 的存在形式,这当然不太够,也许我们更想要搞明白的是,它是为了什么存在。

事实上,当我们在获取一个变量的时候,我们想要知道,它是谁的?当我们执行一个函数的时候,我们又得知道,这一次函数的执行,到底是针对谁的?

我想,这就是 this。

如何确定 this? call, bind, apply

它在非严格模式下总是一个对象,在严格模式下,可以是任何值。

当我们没有特别指定 this 的时候,this 实际上就是它所出现在的执行环境所对应的对象,我们在 window 中写个 this,代码跑到这里的时候,编译器就知道,这个 this 就是指 window.

而如果我们想要(需要)主动设置 this …… 那,就要深入谈一谈 call / bind / apply 了

这三个方法主要是用于函数的,当我们调用一个函数的时候,这个函数的这一次执行,究竟是“针对谁”,这是每个函数在每一次执行的时候都要考虑的。我们所习以为常的 fn() 实际上是一个语法糖,当我们在 window 里面执行这个函数的时候,它的原始面貌是:
fn.call(window)

call / bind / apply 的第一参数就的 this.

也就是说,如果我们不偷懒的话, fn.call 才是真正的“函数调用”(即,调用了函数的 call 方法),我们每次调用函数的时候都要告诉函数 this 是什么。这里,我们只比较 call 与 bind,因为,apply 与 call 很类似,只是参数的形式不一样。
bind 是什么呢?当 bind 出现的时候,实际上表达的意思是 “我要重新创一个看起来一样的函数,但是这个函数执行的时候 this 永远指向我最初指定的。”
所以,fn.bind(window) 得到,不是函数的执行,而是一个 this 被“绑”死了的函数。那么,它与 call 的差异显而易见: call 是执行函数,bind 是创建函数,就算是 bind 生成的函数在执行的时候,依然需要用到 call 方法,只不过,call 指定的 this 是无效的。

let b = fn.bind(window)
b.call(a) // 实际还是 b.call(window),因为 b 变量对应的函数,在创建的时候 this 已经 bind.

这一点有什么用呢?举个例子:
MDN 中对于数组的 slice 方法有一个描述,

slice method can also be called to convert Array-like objects / collections to a new Array.
slice 方法可以接收一个类数组,返回一个新数组。

于是我们就可以构建一个“数组转换器”:

let slice = Function.prototype.call.bind(Array.prototype.slice)
// 这是 MDN 中的例子

你没看错,我们在 call 后面在接一个 bind,因为 call 也是一个函数,
这段代码翻译成人话就是,slice 这个变量,被赋值了一个 call 函数,这个 call 函数的 this 绑定了 Array.prototype.slice
(有意思,不只是普通函数有 this,call 函数也有 this 的)
于是,我们就可以用了:

slice({0:"a", 1:"b", length:2}) // → ["a", "b"]

好处是,上面这段代码就等价于:

Array.prototype.slice({0:"a", 1:"b", length:2})
//为了对比,我们采用不用 call 的语法糖写法

你看,这一下子就省了1… 2… 3 …… 好多好多字符呢~

总结下来,每当我们执行一个函数(非箭头函数)的时候,只要看一看这个函数相关的 bind,再把它转化为 fn.call(this) 的形式就能够很好确定 this 了。

对了,语法糖写法的 this 就是被调用的函数前面的一大堆东西:

a.b.c.fn() // 等价于 a.b.c.fn.call(a.b.c)

用 class 生成对象时的 this

官方说了算:this 指向新生成的对象

arrow funcion

官方说,箭头函数里面的 this 由箭头函数执行时的环境 this 决定,换而言之,箭头函数出不出现,并不影响 this 的指向,我们不需要告诉箭头函数它是针对谁,反正它很“随遇而安” ……

他们说了算 ……

其它

是的,总是“他们说了算”,当我们调用任何一个 API 的时候,实际的代码对于我们来说就好像是藏在“黑匣子”里,this 是什么,只有开发人员知道:

dom.onclick = fn

在dom API 的事件触发函数中, this 是触发当前事件的元素。

再比如,React 在调用 onClick 的时候,会强制把 this 指向 undefined …… 所以我们在 React 中做父子通信的时候常常要用到 bind。

严格模式

还是看回最初的那段话:

A property of an execution context (global, function or eval) that, in non–strict mode, is always a reference to an object and in strict mode can be any value.

它是执行环境下的一个属性,在非严格模式下,总是指向对象,在严格模式下,可以是任何值。
非常有意思,在非严格模式下,我们尝试把 this 指向 字符串、数字、布尔值 的时候,浏览器会把这些值转换为对象的形式。
而如果我们尝试把 this 指向 undefined, null 的时候,this 会变成 window.

this1.png

而在严格模式下,我们指定 this 是什么,它就真的是什么

写在最后

在讨论 bind & call 的时候,我们就注意到一个有去的想象:bind / call 本身就是函数,那么他们有没有 call, bind 函数呢?肯定有,因为 javascript 中这些方法都只不过是一个继承

this2.png

就像最初的例子, call.bind 执行,call.call 也存在,之所以 fn.call.call() 会报错,那是因为,默认情况下,使用 call 又没输入 this 参数的时候,this 默认是 window, 而 call 的 this 必须是一个函数(别忘了,函数是一类特殊的对象):

this3.png

其实还有点什么想说

当我在思考 this 的时候,我总不由自主地受到“作用域”的影响。
现在看来,还是挺明了的:this 跟作用域关系不大的,作用域讨论的是函数的固有属性,而在函数里面讨论到 this 是在确定函数某一次执行的特定归属 ……

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

推荐阅读更多精彩内容