您的位置:首页 > Web前端 > JavaScript

[JavaScript]'this'详解

2016-05-04 22:07 766 查看

文章结构普通的 this
构造函数中的 this

对象方法中的 this

外漏函数中的 this
嵌套函数中的 this
篡改函数中的 this
严格模式的判断
总结
后记

普通的 this

构造函数中的 this

使用new操作符调用构造函数生成对象时,构造函数内的this为当前所new出来的对象。
12345
代码一:function Car() {    console.log(this);}new Car();
输出完整的对象结构:
1
Car {}

对象方法中的 this

下面的代码定义了一个对象benz,并调用该对象的printHistory(),由对象benz调用方法体,所以方法体内的this为对象benz本身。
12345678
代码二:var benz = {    printHistory : function() {        console.log(this);    }};benz.printHistory();
输出完整的类结构:
1
{ printHistory: [Function] }
以上两种情况,this的含义都很明确,即当前的对象本身。

外漏函数中的 this

12345678910
代码三:var benz = {    printHistory : function() {        console.log(this);    }};// 将对象中的方法暴露出来成为外漏函数var printHistory = benz.printHistory;printHistory();
执行代码后,控制台输出如下:
1234567891011121314151617
D:\desk\JavaScript>node this.js{ DTRACE_NET_SERVER_CONNECTION: [Function], // 打印开始...为全局对象或Window  DTRACE_NET_STREAM_END: [Function],    ... ... ...    ... ... ...    ... ... ...  COUNTER_HTTP_CLIENT_REQUEST: [Function], // sodino.com  COUNTER_HTTP_CLIENT_RESPONSE: [Function],  global: [Circular],  process:   process {     title: '管理员: Node.js command prompt - node  this.js',     version: 'v5.7.0',     moduleLoadList:    // sodino.com      [ 'Binding contextify',        'Binding natives',        'NativeModule events',
以上代码打印的this直接为控制台全局对象GLOBAL或浏览器Window了。
这个现象会让很多人困惑。
要理解该现象的原因要从查看调用链去理解:被调用函数是一个对象的属性还是函数自己。
如果它被作为属性调用,那么this的值将变成该属性的对象,否则this的值将被默认赋值为控制台全局对象GLOBAL或浏览器的Window了。

嵌套函数中的 this

对 benz::printHistory()中增加一嵌套函数,如下代码:
1234567891011121314
代码四:var benz = {    printHistory : function() {        // print history after AD2000        console.log(this); // 这里this是benz本身        (function(){            // print history before AD2000            console.log(this); // 这里this是Global或Window        })();    }};benz.printHistory();
控制台输出如下:
123456789101112131415161718
D:\desk\JavaScript>node this.js{ printHistory: [Function] }    // 第一次打印仍为benz本身{ DTRACE_NET_SERVER_CONNECTION: [Function], // 第二次打印开始...为全局对象或Window  DTRACE_NET_STREAM_END: [Function],    ... ... ...    ... ... ...    ... ... ...  COUNTER_HTTP_CLIENT_REQUEST: [Function], // sodino.com  COUNTER_HTTP_CLIENT_RESPONSE: [Function],  global: [Circular],  process:   process {     title: '管理员: Node.js command prompt - node  this.js',     version: 'v5.7.0',     moduleLoadList:    // sodino.com      [ 'Binding contextify',        'Binding natives',        'NativeModule events',
以上代码,第一次打印this,仍是benz本身;但在嵌套函数中打印的this则为控制台全局对象或浏览器的Window了。
this不同于普通的对象声明,this的值无法在被嵌套的函数作用域中保持不变,这是JavaScript的缺陷。
但回想上文中“被调用函数是一个对象的属性还是函数自己”这一判断标准,嵌套函数并不是benz中的属性,所以this值为控制台全局对象或浏览器的Window也可以顺利理解了。如果想在嵌套函数使用到上下文的this,有两种办法:
方法一:新声明一个变量持有上下文this
123456789101112131415
代码五:var benz = {    printHistory : function() {        // print something after AD2000        console.log(this);        var self = this;  // 声明新变量持有this        (function(){            // print something before AD2000            console.log(self);  // 引用上下文this        })();    }};benz.printHistory();
该段代码执行的控制台输出如下:
123
D:\desk\JavaScript>node this.js{ printHistory: [Function] }{ printHistory: [Function] }
方法二:使用Function.prototype.bind(thisArg[, arg1[, arg2[, …]]])
123456789101112131415
代码六:var benz = {    printHistory : function() {        // print something after AD2000        console.log(this);        (function(){            // print something before AD2000            console.log(this);        }).bind(this)();    }};benz.printHistory();
Function.prototype.bind()方法会返回一个新的函数体,新函数体的功能和原先函数保持一致,只是在新函数体内this的值为bind()方法参数中第一个参数的值。
bind()方法返回新函数后,就通过“bind(this) ()“直接被执行了。
由此,为控制台的两处打印信息都是benz本身:
123
D:\desk\JavaScript>node this.js{ printHistory: [Function] }{ printHistory: [Function] }
在维持上下文环境一致的实现选择上,bind()是更受推荐的方法。由此,也引出下面的内容:如何篡改函数体中的this.

篡改函数中的 this

上文已经通过Function.prototype.bind()方法将嵌套函数中this的值指定为bind()的第一个参数值。JavaScript还有另外两个方法也可以实现同样的效果。Function.prototype.apply(thisArg, [argsArray])
Function.prototype.call(thisArg[, arg1[, arg2[, …]]])apply()和call()方法的功能是相同的,都是调用一个对象的一个方法,并用第一个参数的值替换该方法原来的对象,即替换掉方法中的this的值。
apply()与call()方法的区别只是所接收的方法参数不同,除了第一个参数外,apply()接收的第二个参数为数组,即允许将多个参数合并为一个数组传入,而call()只能从第二个参数起逐个一一传入。通过对上文中代码二的修改,使用apply()或call()来篡改函数中this的实现如下代码所示:
1234567891011
代码七:var benz = {    printHistory : function() {        console.log(this);    }};var printHistory = benz.printHistory;printHistory.apply(benz);  // 指定了printHistory的上下文环境仍为benzprintHistory.call(benz);     // 当没有额外的参数时,apply()和call()的用法是相同的
以上代码的控制台输出都打印出了benz本身,如下所示:
123
D:\desk\JavaScript>node this.js{ printHistory: [Function] }{ printHistory: [Function] }
另外,以下的几个方法同样也可以篡改callback函数中的this值,只是能用性没有apply()/call()/bind()强。
Array.prototype.find(callback[, thisArg])Array.prototype.every(callback[, thisArg])Array.prototype.forEach(callback[, thisArg])Array.prototype.some(callback[, thisArg])

严格模式的判断

根据ECMAScript 3和非严格的ECMAScript 5对函数调用的规定,调用上下文(this的值)是全局对象。
在严格模式下,调用上下文(this的值)则是undefined。
所以”this“可以用来判断当前是否是严格模式。方法如下:
123456789
代码八:'use strict'// 'use strict'启用了严格模式,可以注释掉取消该模式var isStrict = (function(){    return !this;}());console.log('isStrict=' + isStrict);
以上代码在控制台输出为:
12
D:\desk\JavaScript>node this.jsisStrict=true

总结

函数体内this就只有两个值,对象本身或运行时环境的全局对象。
在非严格模式下,当在Node.js控制台时,全局对象值为 GLOBAL ;当在HTML浏览器运行时,全局对象值为Window。
在严格模式下,全局对象是undefined。

后记

注意”总结“里这一句”函数体内this就只有两个值“特意说明了是在”函数体内“。那在函数体外呢?
sodino做了下测试,在不同的运行时环境中,函数体外this的值根据运行时环境而有所不同。直接运行代码九中这一句打印语句
12
代码九:console.log(this);
在Node.js控制台运行时,this是个
{}
空对象,如下图:


在Chrome浏览器中,this是Window。如下图:


About Sodino
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: