JavaScript总是要在一个宿主环境中运行的,最常见的宿主环境就是web浏览器,与之对接的是JavaScript引擎,这才是真正执行JavaScript代码的地方。常见的引擎有V8、JavaScript core。
#global this
在浏览器里,全局范围内,this等价于window对象。用var声明一个变量和给this或window添加属性是等价的。
1 2 3
| var foo = 'bar'; console.log(this.foo); console.log(window.foo);
|
如果声明变量时没有使用var,那就是在给全局的this添加或改变属性值。
1 2 3 4 5 6 7 8 9
| foo = 'bar'; function testThis() { foo = 'foo'; } console.log(this.foo); testThis(); console.log(this.foo);
|
在node环境中,使用REPL执行一条条语句时,最高级的命名空间global与this等价。
但如果是执行一个脚本的形式,this以一个空对象开始作为最高级的命名空间,与global不等价。
1 2 3
| console.log(this); console.log(this === global);
|
运行脚本结果:
#function this
无论是浏览器环境还是node环境,除了在DOM事件处理程序或者给出了thisArg外,如果不是用new调用,在函数里面使用this都是指代全局范围的this。
1 2 3 4 5 6 7 8 9
| foo = 'bar'; function testThis() { this.foo = 'foo'; } console.log(this.foo); testThis(); console.log(this.foo);
|
如果在调用函数时在前面使用了new,this就变成一个新的值,和global脱离。
1 2 3 4 5 6 7 8 9 10 11
| foo = 'bar'; function testThis() { this.foo = 'foo'; } console.log(this.foo); new testThis(); console.log(this.foo); console.log(new testThis().foo);
|
#prototype this
this会沿着原型链往上查找需要的属性值。
1 2 3 4 5 6 7 8 9 10
| function Thing1() { } Thing1.prototype.foo = 'bar'; function Thing2() { } Thing2.prototype = new Thing1(); var thing = new Thing2(); console.log(thing.foo);
|
嵌套函数可以通过闭包捕获父函数的变量,但这个函数没有继承this。
1 2 3 4 5 6 7 8 9 10 11 12 13
| function Thing() { } Thing.prototype.foo = 'bar'; Thing.prototype.logFoo = function () { var info = 'attempting to log this.foo:'; function doIt() { console.log(info, this.foo); } doIt(); } var thing = new Thing(); thing.logFoo();
|
再举一个例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function Thing() { } Thing.prototype.foo = 'bar'; Thing.prototype.logFoo = function() { console.log(this.foo); } function doIt(method) { method(); } var thing = new Thing(); thing.logFoo(); doIt(thing.logFoo);
|
有种做法是将this捕获到变量self中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function Thing() { } Thing.prototype.foo = 'bar'; Thing.prototype.logFoo = function() { var self = this; var info = 'attempting to log this.foo'; function doIt() { console.log(info, self.foo); } doIt(); } var thing = new Thing(); thing.logFoo();
|
但这种写法对上面第二个例子来说,并不能解决问题。可以使用bind函数。
1 2 3 4 5 6 7 8 9 10 11 12 13
| function Thing() { } Thing.prototype.foo = 'bar'; Thing.prototype.logFoo = function () { console.log(this.foo); } function doIt(method) { method(); } var thing = new Thing(); doIt(thing.logFoo.bind(thing));
|
同样可以使用apply或call来在新的上下文中调用方法或函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function Thing() { } Thing.prototype.foo = 'bar'; Thing.prototype.logFoo = function () { function doIt() { console.log(this.foo); } doIt.apply(this); } function doItIndirectly(method) { method(); } var thing = new Thing(); doItIndirectly(thing.logFoo.bind(thing));
|
进一步,可以用bind代替任何一个函数或者方法的this。
1 2 3 4 5 6 7 8 9 10 11 12 13
| function Thing() { } Thing.prototype.foo = 'bar'; function logFoo(aStr) { console.log(aStr, this.foo); } var thing = new Thing(); logFoo.bind(thing)('using bind'); logFoo.apply(thing, ['using apply']); logFoo.call(thing, 'using call'); logFoo('using nothing');
|