this
是JavaScript的关键字,它最初应该是从Java、C++等面向对象的语言中借鉴来的。javascript
好比,在Java中没有函数只有方法,this只能用在类的成员方法或构造方法中,表示当前实例对象。因此在Java中this的含义很明确,在其余语言中也相似。java
然而到了JavaScript中,this变得复杂了起来:不只函数内能够用,在全部函数外(全局上下文中)也能够用;函数中的this的含义在函数声明时没法肯定,要到运行期才能肯定,并且与调用函数的方式有关;代码是不是严格模式也会影响this的取值。git
在ES5.1中,有一个所谓执行上下文(Execution Context,EC)的概念,简单的说就是JS引擎的执行进入到某块代码区域时,为该代码区域创建的上下文对象,主要用来记录该区域中声明的变量、函数等。github
EC有三个重要组成部分:VE
、LE
和ThisBinding
。前两个是词法环境,暂且无论。第三个 ThisBinding
就是指在该代码区域的this的值。编程
可见,this是跟某块代码区域关联的。而在JS中,代码区域有三种:app
global代码框架
function代码函数
eval代码。this
此文中主要讨论前两种。es5
全局代码区域是全部函数以外的区域。在此区域中的this就是指全局对象window
(在Node.js中是global
)。
示例:
var num = 123; console.log(this.num); // 输出123
参考:http://es5.github.io/#x10.4.1.1
函数代码区域是指某个函数内的代码,可是不包括它所嵌套的函数内的代码。从咱们能够看出:
this是与包裹它的且离它最近的函数相关的,this既不能穿透到外部的函数,也不能穿透进内部的函数。
示例:
btn.addListener('click', function() { var that = this; dosth(function() { console.log(that.name); }); });
一般每一个函数中的this是不一样的,内部函数能够引用外部函数的局部变量,可是不能直接引用外部函数的this。经过将外部函数的this赋值给一个局部变量能够解决这个问题。
函数内的this的具体函数比较复杂,主要与调用这个函数的方式有关。主要包括如下状况:
示例:
var num = 123; function fn() { console.log(this.num); } function fn2() { "use strict" console.log(this.num); } fn(); // 输出123 fn2(); // 报错
直接调用函数时,若是是在严格模式下,this会被设为undefined
;若是是在非严格模式下,this会被设为全局对象window
。
示例:
var student = { name: 'Tom', sayName: function() { console.log(this.name); }; }; student.sayName(); // 输出Tom
做为方法调用时,this指方法所属的对象。
参考:http://es5.github.io/#x10.4.3和 http://es5.github.io/#x11.2.3
除了上述两种固定的状况外,Javascript提供了一种能够为所欲为地根据须要更改函数中this方法。即便用函数对象的call
或apply
方法来调用函数,显然这种方式给编程带来了极大的灵活性。
示例:
function fn() { var args = Array. prototype. slice.call(arguments, 1); console.log(args); } fn(1, 2, 3); // 输出[2, 3]
这种方法经常使用的场景就是:把一个对象的方法"借"给另外一个具备相似结构的对象使用。
与call和apply不一样,bind方法是在调用前就把函数内的this绑定了,并且一旦绑定就不能再改变。实际上bind方法返回了一个原函数的新版本。
示例:
function fn() { console.log(this.age); } var fn2 = fn.bind({age: 18}); fn2() // 输出18 fn2.call({age: 25}) // 输出18
经过bind获得的函数,不论用哪一种方式调用,它的this都是相同的。
参考:http://es5.github.io/#x15.3.4.5
小结:函数中的this是由调用函数的方式决定的。同一个函数,调用它的方式不一样,那么它内部的this就可能不一样。换句话说,this是动态决定的。
当构造函数经过new操做符来调用时,this表示正在建立的对象。
示例:
function Person(name, age) { this.name = name; this.age = age; } var jerry = new Person('Jerry', 12); console.log(jerry.age); // 输出12
正由于这个缘由,咱们能够在构造函数中经过this
给实例添加属性。
参考:http://es5.github.io/#x11.2.2
回调函数也只不过是函数的一种,实际上这种状况已经包含在了前面提到的状况中。可是因为回调函数的调用者每每不是咱们本身,而是回调函数的接收者,即某个库或框架、甚至是JS运行时环境。这样一来,回调函数在中的this是什么就与对方的调用方式有关了,所以变得比较复杂,因此单独拿出来讨论一下。
状况1:没有明确做用对象的状况下,一般this为全局对象
例如setTimeout
函数的回调函数,它的this就是全局对象。你若是但愿本身指定this,能够经过bind函数等方法。
状况2:某个事件的监听器回调函数,一般this就是事件源对象
例如:
button.addEventListener('click', fn)
fn的中的this就是事件源button对象。
状况3:某些API会专门提供一个参数,用来指定回调函数中的this
例如,咱们能够从新设计一个能够指定this的setTimeout:
function setTimeoutExt(cb, period, thisArg) { setTimeout(function() { cb.call(thisArg); }, period); }
另外,在ExtJS中也大量使用了能够指定this的接口。
(代补充)
this,除了面向对象语言中通用的那两种状况(方法和构造函数)外,在JavaScript 中还提供了更多的使用方式,虽然这让JS中的this变得相对难以掌握,可是它使得JS更加丰富更加灵活。咱们能够把this当作函数的一个特殊的隐含的参数,这个参数表明函数正在操做的主体。
注:时间比较仓促,有些地方没有太深刻,代码实例也比较简单。有机会继续完善。