this总是让人感到有些费解,在阅读一些代码时时不时就冒出来让人丈二和尚摸不着头脑。由于受到一些其他语言的影响,比如Java类中的this是引用自身的指针,曾让我一度认为一切皆对象的JavaScript中函数的this也指向函数本身,这是完全错误的!一个很简单的测试:
|
|
我们可以很明显地看到this指向的是全局window对象,而不是函数自己。实际上,this是在运行时进行绑定的,并不是在编写时绑定的,它的上下文取决于函数调用时的各种条件。this的绑定与函数声明的位置没有任何关系,只取决于函数的调用方式。
那这里就来梳理一下一个函数调用时,究竟发生了什么。一个函数被执行时,会创建一个执行环境 ExecutionContext
,函数的所有的行为均发生在此执行环境中,构建该执行环境时,JavaScript首先会创建 arguments
变量,其中包含调用函数时传入的参数。接下来创建作用域链。然后初始化变量,首先初始化函数的形参表,值为 arguments
变量中对应的值,如果 arguments变量中没有对应值,则该形参初始化为 undefined。如果该函数中含有内部函数,则初始化这些内部函数。如果没有,继续初始化该函数内定义的局部变量,需要注意的是此时这些变量初始化为 undefined,赋值操作需要在函数执行时才进行,这就是所谓的JavaScript的变量提升,我们总是说尽量不要直接定义 function
,会造成全局的变量污染,也是这个道理。最后为this变量赋值,会根据函数调用方式的不同,赋给this全局对象,当前对象等。至此函数的执行环境 ExecutionContext
创建成功,函数开始逐行执行,所需变量均从之前构建好的执行环境 ExecutionContext
中读取。下面例子包含了大多数情况:
|
|
下面总结一下this的几种情况:
默认情况
默认的this与本文开头一致,为全局window对象,严格模式下为undefined,让我们再看一个例子:
|
|
作为对象方法调用
在上下对象中调用,this绑定上下文对象
|
|
apply、call与this
我们可以用 Function.prototype.apply 与 Function.prototype.call
apply与call的作用是完全一样的,这是参数略有不同,fun.call(this, arg1, arg2, …),fun.apply(this, [arg1, arg2, …])
这里的this就是apply与call的第一个参数所指定的对象
|
|
这样就可以很方便地进行运用一些对象本身所不具有的方法了,举个例子,对一个空对象添加一些数据并对其排序:
|
|
new与this
JavaScript中的”构造函数”与其他语言完全不同。new只是作为语法糖,实际是在new时调用了构造函数。
new来调用函数时,会自动执行下列操作:
- 创建一个全新的对象
- 新对象执行原型的链接
- 新对象绑定到函数调用的this
- 自动返回这个新对象
这里的this指的就是新创建的对象
看一下下面的例子:
|
|
关于bind
在ES5中引入了Function.prototype.bind
方法,将bind方法的第一个参数作为当前函数的执行上下文:
|
|
情况1:
|
|
情况2:
|
|
这也让我明白了,利用ES6的React为何要在constructor中进行手工绑定:
|
|
而在ES6中新增了箭头函数,似乎是默认做了bind绑定:
|
|
当然从本质上讲的话,其实是向函数内部隐式地传送了外部的this:
|
|
今天在JS群里看到一道不错的笔试题,刚好来分享一下:
|
|