本文来源 | 前端开发核心知识进阶
作者 | 侯策
编辑 | 林瑟
JavaScript 中的 this
,因其灵活的指向、复杂的使用场景一直是面试中的热点,不论是初级还是中高级开发者,这都是一道必考题。这个概念虽然基础,但是非常重要,是否能深刻理解 this
,是前端 JavaScript 中进阶的重要一环。
this
指向多变,很多隐蔽的 bug 都缘于它。与此同时,this
强大灵活,如果能熟练驾驭,就会写出更简洁、优雅的代码。
本文this
相关知识点如下:
01
this 到底指向谁
曾经在面试阿里某重点部门时,面试官从多个角度考察过我对 this
的理解:全局环境下的this
、箭头函数的 this
、构造函数的 this
、this
的显隐性和优先级,等等。尽管我能一一作答,可是最后的问题:请用一句话总结this
的指向,注意只用一句话,我却犯难了。有一种广泛流传的说法是:
谁调用它,
this
就指向谁。
也就是说,this
的指向是在调用时确定的。这么说没有太大的问题,可是并不全面。面试官要求我用更加规范的语言进行总结,那么他到底在等什么样的回答呢?
我们还要回到 JavaScript 中一个最基本的概念分析——执行上下文,事实上,调用函数会创建新的属于函数自身的执行上下文。执行上下文的调用创建阶段会决定 this
的指向。到此,我们可以得出的一个结论:
this
的指向,是在调用函数时根据执行上下文所动态确定的。
具体环节和规则,可以先“死记硬背”以下几条规律:
在函数体中,简单调用该函数时(非显式/隐式绑定下),严格模式下
this
绑定到undefined
,否则绑定到全局对象window
/global
;一般构造函数
new
调用,绑定到新创建的对象上;一般由
call
/apply
/bind
方法显式调用,绑定到指定参数的对象上;一般由上下文对象调用,绑定在该对象上;
箭头函数中,根据外层上下文绑定的
this
决定this
指向。
当然,真实环境多样,我们来看几个实例分析一下。
02
实战例题分析
例题1. 全局环境下的 this
这种情况相对简单直接,函数在浏览器全局环境中被简单调用,非严格模式下this
指向 window
;在 use strict
指明严格模式的情况下就是undefined
。我们来看例题,请描述打印结果:
这样的题目比较基础,但是需要候选人格外注意其变种,请再看一道题目:
这里 this
仍然指向的是 window
。虽然 fn
函数在 foo
对象中作为方法被引用,但是在赋值给 fn1
之后,fn1
的执行仍然是在 window
的全局环境中。因此输出 window
和 undefined
,它们相当于:
这样的题目比较基础,但是需要候选人格外注意其变种,请再看一道题目:
这里 this
仍然指向的是 window
。虽然 fn
函数在 foo
对象中作为方法被引用,但是在赋值给 fn1
之后,fn1
的执行仍然是在 window
的全局环境中。因此输出 window
和 undefined
,它们相当于:
还是上面这道题目,如果调用改变为:
将会输出:
因为这个时候 this
指向的是最后调用它的对象,在 foo.fn()
语句中this
指向 foo
对象。请记住:
在执行函数时,如果函数中的 this
是被上一级的对象所调用,那么this
指向的就是上一级的对象;否则指向全局环境。
以上只是一道实战例题分析,不知是否已经把你绕晕了。事实上,this
的指向涉及的规范繁多,优先级也较为混乱。刻意刁难并不是很好的面试做法,一些细节候选人如果没有记住也不是太大的问题。作为面试官,我往往会另辟蹊径,出一些开放性题目。
03
实现一个 bind
函数
作为面试者,我也曾经在头条的面试流程中被问到模拟 bind
。这道题并不新鲜,有的朋友也会有自己的解答思路,而且社区上关于原生 bind
的研究也很多。但是,我这里想强调的是。在回答时,我往往先实现一个初级版本,然后根据 ES5-shim 源码进一步说明。
这样的实现已经非常不错了。但是,就如同之前 this
优先级分析所示:bind
返回的函数如果作为构造函数,搭配 new
关键字出现的话,我们的绑定 this
就需要“被忽略”。
为了实现这样的规则,开发者就应该需要考虑如何区分这两种调用方式。具体来讲 bound
函数中就要进行 this instanceof
的判断。
另外一个细节是,函数具有 length
属性,表示形参的个数。上述实现方式形参的个数显然会失真。我们的实现就需要对 length
属性进行还原。可是难点在于:函数的 length
属性值是不可重写的。
这样的内容一般属于“超纲”范畴,但在面试中能够很好地体现面试者平时的积累,以及对源码的阅读和思考,显然是加分项。
通过本文的分析,我们看到 this
纷繁多象,确实不容易彻底掌握。
在我的课程《前端开发核心知识进阶》中我会尽可能系统地进行讲解、说明,例题尽可能地覆盖更多 case。与此同时,大家也要在阅读之外继续进行消化与吸收。只有“记死”,才能“用活”。
关注前端领域的读者
欢迎扫码订阅这门课程
▼
接下来会更新更多大家感兴趣的话题,点击阅读原文,订阅课程,让我们一起进步。