用自然语言的角度理解JavaScript中的this关键字
2015-09-17 17:47
537 查看
转自:http://blog.leapoahead.com/2015/08/31/understanding-js-this-keyword/
在编写JavaScript应用的时候,我们经常会使用
这里的小明是主语,如果没有这个主语,那么后面的代词『他』将毫无意义。有了主语,代词才有了可以指代的事物。
类比到JavaScript的世界中,我们在调用一个对象的方法的时候,需要先指明这个对象,再指明要调用的方法。
在上面的例子中,第8行中的
同样的,对于一个JavaScript类,在将它初始化之后,我们也可以用类似的方法来理解:类的实例在调用其方法的时候,将作为主语,其方法中的
这就是我认为this关键字设计得精彩的地方!如果将调用方法的语句(上面代码的第16行)和方法本身的代码连起来,像英语一样读,其实是完全通顺的。
句子的主语是可以变的,例如在下面的场景中,
在这种情况下,句子还是通顺的。所以,非常完美!
但是如果小明很抠门,不愿意将
那么同一个函数被多次
Function.prototype.call允许你在调用一个函数的时候指定它的
Function.prototype.apply和
那么
当方法失去主语的时候,
其实大家可以发现我的用词,当一个
那么
首先,全局函数的调用就是最简单的一种。
立即调用的函数表达式(IIFE,Immediately-Invoked Function Expression)也是没有主语的,所以它被调用的时候
但是,当函数被执行在严格模式(strict-mode)下的时候,函数的调用时的this就是
在事件的回调函数(第6行开始定义的匿名函数)里面,
这是因为匿名函数是被jQuery内部调用的,我们不知道它调用的时候的主语是什么,或者是否被
如果我们想要在上面的回调函数里面使用obj的
另外一种方法就是为该匿名函数
在编写JavaScript应用的时候,我们经常会使用
this关键字。那么
this关键字究竟是怎样工作的?它的设计有哪些好的地方,有哪些不好的地方?本文带大家全面系统地认识这个老朋友。
这里的小明是主语,如果没有这个主语,那么后面的代词『他』将毫无意义。有了主语,代词才有了可以指代的事物。
类比到JavaScript的世界中,我们在调用一个对象的方法的时候,需要先指明这个对象,再指明要调用的方法。
var xiaoming = { name: 'Xiao Ming', run: function() { console.log(`${this.name} seems happy`); }, }; xiaoming.run();
在上面的例子中,第8行中的
xiaoming指定了
run方法运行时的主语。因此,在
run中,我们才可以用
this来代替
xiaoming这个对象。可以看到
this起了代词的作用。
同样的,对于一个JavaScript类,在将它初始化之后,我们也可以用类似的方法来理解:类的实例在调用其方法的时候,将作为主语,其方法中的
this就自然变成了指代主语的代词。
class People { constructor(name) { // 在用new关键字实例化一个对象的时候,相当于在说, // “创建一个People类实例(主语),它(this)的name是……” // 所以这里的this就是新创建的People类实例 this.name = name; } run() { console.log(`${this.name} seems happy.`) } } // new关键字实例化一个类 var xiaoming = new People('xiaoming'); xiaoming.run();
这就是我认为this关键字设计得精彩的地方!如果将调用方法的语句(上面代码的第16行)和方法本身的代码连起来,像英语一样读,其实是完全通顺的。
this
的绑定
句子的主语是可以变的,例如在下面的场景中,run被赋值到小芳(
xiaofang)身上之后,调用
xiaofang.run,主语就变成了小芳!
var xiaofang = { name: 'Xiao Fang', }; var xiaoming = { name: 'Xiao Ming', run: function() { console.log(`${this.name} seems happy`); }, }; xiaofang.run = xiaoming.run; // 主语变成了小芳 xiaofang.run();
在这种情况下,句子还是通顺的。所以,非常完美!
但是如果小明很抠门,不愿意将
run方法借给小芳以后,
this就变成了小芳的话,那么小明要怎么做呢?他可以通过Function.prototype.bind让
run运行时候的
this永远为小明自己
var xiaofang = { name: 'Xiao Fang', }; var xiaoming = { name: 'Xiao Ming', run: function() { console.log(`${this.name} seems happy`); }, }; // 将小明的run方法绑定(bind)后,返回的还是一个 // 函数,但是这个函数之后被调用的时候就算主语不是小明, // 它的this依然是小明 xiaoming.run = xiaoming.run.bind(xiaoming); xiaofang.run = xiaoming.run; // 主语虽然是小芳,但是最后this还是小明 xiaofang.run();
那么同一个函数被多次
bind之后,到底
this是哪一次
bind的对象呢?你可以自己尝试看看。
call
与apply
Function.prototype.call允许你在调用一个函数的时候指定它的this的值。
var xiaoming = { name: 'Xiao Ming' }; function run(today, mood) { console.log(`Today is ${today}, ${this.name} seems ${mood}`); } // 函数的call方法第一个参数是this的值 // 后续只需按函数参数的顺序传参即可 run.call(xiaoming, 'Monday', 'happy')
Function.prototype.apply和
Function.prototype.call的功能是一模一样的,区别进在于,
apply里将函数调用所需的所有参数放到一个数组当中。
var xiaoming = { name: 'Xiao Ming' }; function run(today, mood) { console.log(`Today is ${today}, ${this.name} seems ${mood}`); } // apply只接受两个参数 // 第二个参数是一个数组,这个数组的元素被按顺序 // 作为run调用的参数 run.apply(xiaoming, ['Monday', 'happy'])
那么
call/
apply和上面的
bind混用的时候是什么样的行为呢?这个也留给大家自行验证。但是在一般情况下,我们应该避免混用它们,否则会造成代码检查或者调试的时候难以跟踪
this的值的问题。
当方法失去主语的时候,this
不再有?
其实大家可以发现我的用词,当一个function被调用的时候是有主语的时候,它是一个方法;当一个
function被调用的时候是没有主语的时候,它是一个函数。当一个函数运行的时候,它虽然没有主语,但是它的
this的值会是全局对象。在浏览器里,那就是
window。当然了,前提是函数没有被
bind过,也不是被
apply或
call所调用。
那么
function作为函数的情景有哪些呢?
首先,全局函数的调用就是最简单的一种。
function bar() { console.log(this === window); // 输出:true } bar();
立即调用的函数表达式(IIFE,Immediately-Invoked Function Expression)也是没有主语的,所以它被调用的时候
this也是全局对象。
(function() { console.log(this === window); // 输出:true })();
但是,当函数被执行在严格模式(strict-mode)下的时候,函数的调用时的this就是
undefined了。这是很值得注意的一点。
function bar() { 'use strict'; console.log('Case 2 ' + String(this === undefined)); // 输出:undefined } bar();
不可见的调用
有时候,你没有办法看到你定义的函数是怎么被调用的。因此,你就没有办法知道它的主语。下面是一个用jQuery添加事件监听器的例子。window.val = 'window val'; var obj = { val: 'obj val', foo: function() { $('#text').bind('click', function() { console.log(this.val); }); } }; obj.foo();
在事件的回调函数(第6行开始定义的匿名函数)里面,
this的值既不是
window,又不是
obj,而是页面上
id为
text的HTML元素。
var obj = { foo: function() { $('#text').bind('click', function() { console.log(this === document.getElementById('text')); // 输出:true }); } }; obj.foo();
这是因为匿名函数是被jQuery内部调用的,我们不知道它调用的时候的主语是什么,或者是否被
bind等函数修改过
this的值。所以,当你将匿名函数交给程序的其他部分调用的时候,需要格外地谨慎。
如果我们想要在上面的回调函数里面使用obj的
val值,除了直接写
obj.val之外,还可以在foo方法中用一个新的变量
that来保存
foo运行时
this的值。这样说有些绕口,我们看下例子便知。
window.val = 'window val'; var obj = { val: 'obj val', foo: function() { var that = this; // 保存this的引用到that,这里的this实际上就是obj $('#text').bind('click', function() { console.log(that.val); // 输出:obj val }); } }; obj.foo();
另外一种方法就是为该匿名函数
bind了。
window.val = 'window val'; var obj = { val: 'obj val', foo: function() { $('#text').bind('click', function() { console.log(this.val); // 输出:obj val }.bind(this)); } }; obj.foo();
总结
在JavaScript中this的用法的确是千奇百怪,但是如果利用自然语言的方式来理解,一切就顺理成章了。
相关文章推荐
- 例子:韩顺平JavaScript----JS乌龟抓小鸡游戏
- JS达到Web指定保存的和打印功能的内容
- JS类的实现
- javascript 和 jq 的调试 用console.log() 函数
- js javascript 获取url,获得当前页面的url,静态html文件js读取url参数
- 用js控制frame框架自适应高度
- createjs-打豆豆小游戏制作(4)
- js之History对象
- js动态引入的四种方式
- Javascript学习笔记【第三章】2
- JS类的封装及实现代码
- javascript控制开始日期,和结束日期在同一个月
- Jsp九大内置对象及四个作用域
- (转)js prototype 详解
- JavaScript编码风格指南(中文版)
- javascript必知必会之prototype
- JSON返回值不是预定的值,而是无效时的值(情况不定)
- js根据URL获取参数的值
- javaScript中的页面传值
- JSP四大作用域