王福朋老师的 JavaScript原型和闭包系列 文章看了不下三遍了,最为一个初学者,每次看的时候都会有一种 "大彻大悟" 的感受,而看完以后却老是一脸懵逼。原型与闭包 能够说是 JavaScirpt 中理解起来最难的部分了,固然,我也只是了解到了一些皮毛,对于 JavaScript OOP 更是缺少经验。这里我想总结一下 Javascript 中的 this
关键字,王福朋老师的在文章里也花了大量的篇幅来说解 this
关键字的使用,能够说 this
关键字也是值得重视的。javascript
咱们都知道,每个 "代码段" 都会执行在某一个 上下文环境 当中,而在每个代码执行以前,都会作一项 "准备工做",也就是生成相应的 上下文环境,因此每个 上下文环境 均可能会不同。html
上下文环境 是什么?咱们能够去看王福朋老师的文章(连接在文末),讲解的很清楚了,这里不赘述了。java
"代码段" 能够分为三种:node
eval
代码与之对应的 上下文环境 就有:git
(elav
就不讨论了,不推荐使用)github
固然,这和 this
又有什么关系呢?this
的值就是在为代码段作 "准备工做" 时赋值的,能够说 this
就是 上下文环境 的一部分,而每个不一样的 上下文环境 可能会有不同的 this
值。浏览器
每次在寻找一个问题的解决方案或总结一个问题的时候,我总会去尝试将这个问题进行合适的分类,而从不一样的方面去思考问题。
因此,这里我大胆的将 this
关键字的使用分为两种状况:闭包
this
this
(你也能够选择其余的方式分类。固然,这也不重要了)app
this
在全局执行上下文中(在任何函数体外部),this
都指向全局对象:函数
// 在浏览器中, 全局对象是 window console.log(this === window) // true var a = 'Zavier Tang' console.log(a) // 'Zavier Tang' console.log(window.a) // 'Zavier Tang' console.log(this.a) // 'Zavier Tang' this.b = 18 console.log(b) // 18 console.log(window.b) // 18 console.log(this.b) // 18 // 在 node 环境中,this 指向global console.log(this === global) // true
this
在函数内部,this
的值取决与函数被调用的方式。
this
的值在函数定义的时候是肯定不了的,只有函数调用的时候才能肯定 this
的指向。实际上 this
的最终指向的是那个调用它的对象。(也不必定正确)
对于全局的方法调用,this
指向 window
对象(node下为 global
):
var foo = function () { return this } // 在浏览器中 foo() === window // true // 在 node 中 foo() === global //true
但值得注意的是,以上代码是在 非严格模式 下。然而,在 严格模式 下,this
的值将保持它进入执行上下文的值:
var foo = function () { "use strict" return this } f2() // undefined
即在严格模式下,若是 this
没有被执行上下文定义,那它为 undefined
。
在生成 上下文环境 时:
- 若方法被
window
(或global
)对象调用,即执行window.foo()
,那this
将会被定义为window
(或global
);- 若被普通对象调用,即执行
obj.foo()
,那this
将会被定义为obj
对象;(在后面会讨论)- 但若未被对象调用,即直接执行
foo()
,在非严格模式下,this
的值默认指向全局对象window
(或global
),在严格模式下,this
将保持为undefined
。
经过 this
调用全局变量:
var a = 'global this' var foo = function () { console.log(this.a) } foo() // 'global this'
var a = 'global this' var foo = function () { this.a = 'rename global this' // 修改全局变量 a console.log(this.a) } foo() // 'rename global this'
因此,对于全局的方法调用,this
指向的是全局对象 window
(或global
),即调用方法的对象。(注意严格模式的不一样)
当函数做为对象的方法调用时,它的 this
值是调用该函数的对象。也就是说,函数的 this
值是在函数被调用时肯定的,在定义函数时肯定不了(箭头函数除外)。
var obj = { name: 'Zavier Tang', foo: function () { console.log(this) console.log(this.name) } } obj.foo() // Object {name: 'Zavier Tang', foo: function} // 'Zavier Tang' //foo函数不是做为obj的方法调用 var fn = obj.foo // 这里foo函数并无执行 fn() // Window {...} // undefined
this
的值同时也只受最靠近的成员引用的影响:
//接上面代码 var o = { name: 'Zavier Tang in object o', fn: fn, obj: obj } o.fn() // Object {name: 'Zavier Tang in object o', fn: fn, obj: obj} // 'Zavier Tang in object o' o.obj.foo() // Object {name: 'Zavier Tang', foo: function} // 'Zavier Tang'
在原型链中,this
的值为当前对象:
var Foo = function () { this.name = 'Zavier Tang' this.age = 20 } Foo.prototype.getInfo = function () { console.log(this.name) console.log(this.age) } var tang = new Foo() tang.getInfo() // "Zavier Tang" // 20
虽然这里调用的是一个继承方法,但 this
所指向的依然是 tang
对象。
参考:《Object-Oriented JavaScript》(Second Edition)
若是函数做为构造函数,那函数当中的 this
即是构造函数即将 new
出来的对象:
var Foo = function () { this.name = 'Zavier Tang', this.age = 20, this.year = 1998, console.log(this) } var tang = new Foo() console.log(tang.name) // 'Zavier Tang' console.log(tang.age) // 20 console.log(tang.year) // 1998
当 Foo
不做为构造函数调用时,this
的指向即是前面讨论的,指向全局变量:
// 接上面代码 Foo() // window {...}
apply
、call
、 bind
时当一个函数在其主体中使用 this
关键字时,能够经过使用函数继承自Function.prototype
的 call
或 apply
方法将 this
值绑定到调用中的特定对象。即 this
的值就取传入对象的值:
var obj1 = { name: 'Zavier1' } var obj2 = { name: 'Zavier2' } var foo = function () { console.log(this) console.log(this.name) } foo.apply(obj1) // Ojbect {name: 'Zavier1'} //'Zavier1' foo.call(obj1) // Ojbect {name: 'Zavier1'} //'Zavier1' foo.apply(obj2) // Ojbect {name: 'Zavier2'} //'Zavier2' foo.call(obj2) // Ojbect {name: 'Zavier2'} //'Zavier2'
与 apply
、call
不一样,使用 bind
会建立一个与 foo
具备相同函数体和做用域的函数。可是,特别要注意的是,在这个新函数中,this
将永久地被绑定到了 bind
的第一个参数,不管以后如何调用。
var foo = function () { console.log(this.name) } var obj1 = { name: 'Zavier1' } var obj2 = { name: 'Zavier2' } var g = foo.bind(obj1) g() // 'Zavier1' var h = g.bind(ojb2) // bind只生效一次! h() // 'Zavier1' var o = { name: 'Zavier Tang', f:f, g:g, h:h } o.f() // 'Zavier Tang' o.g() // 'Zavier1' o.h() // 'Zavier1'
箭头函数是 ES6 语法的新特性,在箭头函数中,this
的值与建立箭头函数的上下文的 this
一致。
在全局代码中,this
的值为全局对象:
var foo = (() => this) //在浏览器中 foo() === window // true // 在node中 foo() === global // true
其实箭头函数并无本身的 this
。因此,调用 this
时便和调用普通变量同样在做用域链中查找,获取到的便是建立此箭头函数的上下文中的 this
。
当箭头函数在建立其的上下文外部被调用时,箭头函数即是一个闭包,this
的值一样与原上下文环境中的 this
的值一致。因为箭头函数自己是不存在 this
,经过 call
、 apply
或 bind
修改 this
的指向是没法实现的。
做为对象的方法:
var foo = (() => this) var obj = { foo: foo } // 做为对象的方法调用 obj.foo() === window // true // 用apply来设置this foo.apply(obj) === window // true // 用bind来设置this foo = foo.bind(obj) foo() === window // true
箭头函数 foo
的 this
被设置为建立时的上下文(在上面代码中,也就是全局对象)的 this
值,并且没法经过其余调用方式设定 foo
的 this
值。
与普通函数对比,箭头函数的 this
值是在函数建立建立肯定的,并且没法经过调用方式从新设置 this
值。普通函数中的 this
值是在调用的时候肯定的,可经过不一样的调用方式设定 this
值。
this
关键字的值取决于其所处的位置(上下文环境):
this
的值指向全局对象( window 或 global )。this
的取值取决于其所在函数的调用方式,也就是说 this
的值是在函数被调用的时候肯定的,在建立函数时没法肯定。固然,箭头函数是个例外,箭头函数自己不存在 this
,而在箭头函数中使用 this
获取到的即是建立其的上下文中的 this
。同时,使用函数的继承方法 call
、 apply
和 bind
会修改 this
的指向。但值得注意的是,使用 bind
方法会使 this
的值永久的绑定到给定的对象,没法再经过调用 call
和 apply
方法修改 this
的值,箭头函数调用 call
、 apply
或 bind
方法没法修改 this
。参考: