[JavaScript]'this'详解
2016-05-04 22:07
766 查看
文章结构普通的 this
构造函数中的 this
对象方法中的 this
外漏函数中的 this
嵌套函数中的 this
篡改函数中的 this
严格模式的判断
总结
后记
输出完整的对象结构:
输出完整的类结构:
以上两种情况,this的含义都很明确,即当前的对象本身。
执行代码后,控制台输出如下:
以上代码打印的this直接为控制台全局对象GLOBAL或浏览器Window了。
这个现象会让很多人困惑。
要理解该现象的原因要从查看调用链去理解:被调用函数是一个对象的属性还是函数自己。
如果它被作为属性调用,那么this的值将变成该属性的对象,否则this的值将被默认赋值为控制台全局对象GLOBAL或浏览器的Window了。
控制台输出如下:
以上代码,第一次打印this,仍是benz本身;但在嵌套函数中打印的this则为控制台全局对象或浏览器的Window了。
this不同于普通的对象声明,this的值无法在被嵌套的函数作用域中保持不变,这是JavaScript的缺陷。
但回想上文中“被调用函数是一个对象的属性还是函数自己”这一判断标准,嵌套函数并不是benz中的属性,所以this值为控制台全局对象或浏览器的Window也可以顺利理解了。如果想在嵌套函数使用到上下文的this,有两种办法:
方法一:新声明一个变量持有上下文this
该段代码执行的控制台输出如下:
方法二:使用Function.prototype.bind(thisArg[, arg1[, arg2[, …]]])
Function.prototype.bind()方法会返回一个新的函数体,新函数体的功能和原先函数保持一致,只是在新函数体内this的值为bind()方法参数中第一个参数的值。
bind()方法返回新函数后,就通过“bind(this) ()“直接被执行了。
由此,为控制台的两处打印信息都是benz本身:
在维持上下文环境一致的实现选择上,bind()是更受推荐的方法。由此,也引出下面的内容:如何篡改函数体中的this.
Function.prototype.call(thisArg[, arg1[, arg2[, …]]])apply()和call()方法的功能是相同的,都是调用一个对象的一个方法,并用第一个参数的值替换该方法原来的对象,即替换掉方法中的this的值。
apply()与call()方法的区别只是所接收的方法参数不同,除了第一个参数外,apply()接收的第二个参数为数组,即允许将多个参数合并为一个数组传入,而call()只能从第二个参数起逐个一一传入。通过对上文中代码二的修改,使用apply()或call()来篡改函数中this的实现如下代码所示:
以上代码的控制台输出都打印出了benz本身,如下所示:
另外,以下的几个方法同样也可以篡改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])
在严格模式下,调用上下文(this的值)则是undefined。
所以”this“可以用来判断当前是否是严格模式。方法如下:
以上代码在控制台输出为:
在非严格模式下,当在Node.js控制台时,全局对象值为 GLOBAL ;当在HTML浏览器运行时,全局对象值为Window。
在严格模式下,全局对象是undefined。
sodino做了下测试,在不同的运行时环境中,函数体外this的值根据运行时环境而有所不同。直接运行代码九中这一句打印语句
在Node.js控制台运行时,this是个
在Chrome浏览器中,this是Window。如下图:
About Sodino
构造函数中的 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
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的值将变成该属性的对象,否则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不同于普通的对象声明,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] } |
123456789101112131415 | 代码六:var benz = { printHistory : function() { // print something after AD2000 console.log(this); (function(){ // print something before AD2000 console.log(this); }).bind(this)(); }};benz.printHistory(); |
bind()方法返回新函数后,就通过“bind(this) ()“直接被执行了。
由此,为控制台的两处打印信息都是benz本身:
123 | D:\desk\JavaScript>node this.js{ printHistory: [Function] }{ printHistory: [Function] } |
篡改函数中的 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()的用法是相同的 |
123 | D:\desk\JavaScript>node this.js{ printHistory: [Function] }{ printHistory: [Function] } |
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); |
{}空对象,如下图:
在Chrome浏览器中,this是Window。如下图:
About Sodino
相关文章推荐
- JS 操作style属性
- JSP中的遗留问题
- JSP技术概述
- JavaScript对象的常用函数
- JavaScript之单例实战
- BabylonJS文件格式---基于WebGL的H5 3D游戏引擎
- JavaScript学习之DOM编程
- WebBasic05-JS
- Javascript学习之常用对象
- javascript 闭包
- JSP网页之间get传参乱码
- JSTL下载与配置
- JavaScript -- 制作简易瀑布流
- JavaScript学习随记——错误类型
- html+Ajax和JSP的比较
- 5种JavaScript和CSS交互的方法
- JS无提示关闭浏览器窗口脚本以及不同浏览器对window.close()不同表现
- js 实现数字的进制转换
- JavaScript 设计模式之工厂模式
- 原生JS实现字符串分割