翼度科技»论坛 编程开发 JavaScript 查看内容

JavaScript中的this关键字用法详解

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
先举一个生活例子:
  1. 小明正在跑步,他看起来很开心
复制代码
这里的小明是主语,如果没有这个主语,那么后面的代词『他』将毫无意义。有了主语,代词才有了可以指代的事物。
类比到JavaScript的世界中,我们在调用一个对象的方法的时候,需要先指明这个对象,再指明要调用的方法。
  1. var xiaoming = {
  2.   name: 'Xiao Ming',
  3.   run: function() {
  4.     console.log(`${this.name} seems happy`);
  5.   },
  6. };

  7. xiaoming.run();
复制代码
在上面的例子中,第8行中的
  1. xiaoming
复制代码
指定了
  1. run
复制代码
方法运行时的主语。因此,在
  1. run
复制代码
中,我们才可以用
  1. this
复制代码
来代替
  1. xiaoming
复制代码
这个对象。可以看到
  1. this
复制代码
起了代词的作用。
同样的,对于一个JavaScript类,在将它初始化之后,我们也可以用类似的方法来理解:类的实例在调用其方法的时候,将作为主语,其方法中的
  1. this
复制代码
就自然变成了指代主语的代词。
  1. class People {
  2.   constructor(name) {
  3.     // 在用new关键字实例化一个对象的时候,相当于在说,
  4.     // “创建一个People类实例(主语),它(this)的name是……”
  5.     // 所以这里的this就是新创建的People类实例
  6.     this.name = name;
  7.   }
  8.   
  9.   run() {
  10.     console.log(`${this.name} seems happy.`)  
  11.   }
  12. }

  13. // new关键字实例化一个类
  14. var xiaoming = new People('xiaoming');
  15. xiaoming.run();
复制代码
这就是我认为this关键字设计得精彩的地方!如果将调用方法的语句
  1. var xiaoming = new People('xiaoming');
复制代码
和方法本身的代码连起来,像英语一样读,其实是完全通顺的。

this的绑定

句子的主语是可以变的,例如在下面的场景中,
  1. run
复制代码
被赋值到小芳(
  1. xiaofang
复制代码
)身上之后,调用
  1. xiaofang.run
复制代码
,主语就变成了小芳!
  1. var xiaofang = {
  2.   name: 'Xiao Fang',
  3. };

  4. var xiaoming = {
  5.   name: 'Xiao Ming',
  6.   run: function() {
  7.     console.log(`${this.name} seems happy`);
  8.   },
  9. };

  10. xiaofang.run = xiaoming.run;
  11. // 主语变成了小芳
  12. xiaofang.run();
复制代码
但是如果小明很抠门,不愿意将
  1. run
复制代码
方法借给小芳以后,
  1. this
复制代码
就变成了小芳的话,那么小明要怎么做呢?他可以通过Function.prototype.bind让
  1. run
复制代码
运行时候的
  1. this
复制代码
永远为小明自己。
  1. var xiaofang = {
  2.   name: 'Xiao Fang',
  3. };

  4. var xiaoming = {
  5.   name: 'Xiao Ming',
  6.   run: function() {
  7.     console.log(`${this.name} seems happy`);
  8.   },
  9. };

  10. // 将小明的run方法绑定(bind)后,返回的还是一个
  11. // 函数,但是这个函数之后被调用的时候就算主语不是小明,
  12. // 它的this依然是小明
  13. xiaoming.run = xiaoming.run.bind(xiaoming);

  14. xiaofang.run = xiaoming.run;
  15. // 主语虽然是小芳,但是最后this还是小明
  16. xiaofang.run();
复制代码
那么同一个函数被多次
  1. bind
复制代码
之后,到底
  1. this
复制代码
是哪一次
  1. bind
复制代码
的对象呢?你可以自己尝试看看。

call与apply
  1. Function.prototype.call
复制代码
允许你在调用一个函数的时候指定它的
  1. this
复制代码
的值。
  1. var xiaoming = {
  2.     name: 'Xiao Ming'
  3. };

  4. function run(today, mood) {
  5.     console.log(`Today is ${today}, ${this.name} seems ${mood}`);
  6. }

  7. // 函数的call方法第一个参数是this的值
  8. // 后续只需按函数参数的顺序传参即可
  9. run.call(xiaoming, 'Monday', 'happy')
复制代码
  1. Function.prototype.apply
复制代码
  1. Function.prototype.call
复制代码
的功能是一模一样的,区别进在于,
  1. apply
复制代码
里将函数调用所需的所有参数放到一个数组当中。
  1. var xiaoming = {
  2.     name: 'Xiao Ming'
  3. };

  4. function run(today, mood) {
  5.     console.log(`Today is ${today}, ${this.name} seems ${mood}`);
  6. }

  7. // apply只接受两个参数
  8. // 第二个参数是一个数组,这个数组的元素被按顺序
  9. // 作为run调用的参数
  10. run.apply(xiaoming, ['Monday', 'happy'])
复制代码
那么
  1. call
复制代码
/
  1. apply
复制代码
和上面的
  1. bind
复制代码
混用的时候是什么样的行为呢?这个也留给大家自行验证。但是在一般情况下,我们应该避免混用它们,否则会造成代码检查或者调试的时候难以跟踪
  1. this
复制代码
的值的问题。

当方法失去主语的时候,this不再有?

其实大家可以发现我的用词,当一个
  1. function
复制代码
被调用的时候是有主语的时候,它是一个方法;当一个
  1. function
复制代码
被调用的时候是没有主语的时候,它是一个函数。当一个函数运行的时候,它虽然没有主语,但是它的
  1. this
复制代码
的值会是全局对象。在浏览器里,那就是
  1. window
复制代码
。当然了,前提是函数没有被
  1. bind
复制代码
过,也不是被
  1. apply
复制代码
  1. call
复制代码
所调用。
那么
  1. function
复制代码
作为函数的情景有哪些呢?
首先,全局函数的调用就是最简单的一种。
  1. function bar() {
  2.   console.log(this === window); // 输出:true
  3. }
  4. bar();
复制代码
立即调用的函数表达式(IIFE,Immediately-Invoked Function Expression)也是没有主语的,所以它被调用的时候
  1. this
复制代码
也是全局对象。
  1. (function() {
  2.   console.log(this === window); // 输出:true
  3. })();
复制代码
但是,当函数被执行在严格模式(strict-mode)下的时候,函数的调用时的this就是
  1. undefined
复制代码
了。这是很值得注意的一点。
  1. function bar() {
  2.   'use strict';
  3.   console.log('Case 2 ' + String(this === undefined)); // 输出:undefined
  4. }
  5. bar();
复制代码
不可见的调用

有时候,你没有办法看到你定义的函数是怎么被调用的。因此,你就没有办法知道它的主语。下面是一个用jQuery添加事件监听器的例子。
  1. window.val = 'window val';

  2. var obj = {
  3.   val: 'obj val',
  4.   foo: function() {
  5.     $('#text').bind('click', function() {
  6.       console.log(this.val);
  7.     });
  8.   }
  9. };

  10. obj.foo();
复制代码
在事件的回调函数(第6行开始定义的匿名函数)里面,
  1. this
复制代码
的值既不是
  1. window
复制代码
,又不是
  1. obj
复制代码
,而是页面上
  1. id
复制代码
  1. text
复制代码
的HTML元素。
  1. var obj = {
  2.   foo: function() {
  3.     $('#text').bind('click', function() {
  4.       console.log(this === document.getElementById('text')); // 输出:true
  5.     });
  6.   }
  7. };

  8. obj.foo();
复制代码
这是因为匿名函数是被jQuery内部调用的,我们不知道它调用的时候的主语是什么,或者是否被
  1. bind
复制代码
等函数修改过
  1. this
复制代码
的值。所以,当你将匿名函数交给程序的其他部分调用的时候,需要格外地谨慎。
如果我们想要在上面的回调函数里面使用obj的
  1. val
复制代码
值,除了直接写
  1. obj.val
复制代码
之外,还可以在foo方法中用一个新的变量
  1. that
复制代码
来保存
  1. foo
复制代码
运行时
  1. this
复制代码
的值。这样说有些绕口,我们看下例子便知。
  1. window.val = 'window val';

  2. var obj = {
  3.   val: 'obj val',
  4.   foo: function() {
  5.     var that = this; // 保存this的引用到that,这里的this实际上就是obj
  6.     $('#text').bind('click', function() {
  7.       console.log(that.val); // 输出:obj val
  8.     });
  9.   }
  10. };

  11. obj.foo();
复制代码
另外一种方法就是为该匿名函数
  1. bind
复制代码
了。
  1. window.val = 'window val';

  2. var obj = {
  3.   val: 'obj val',
  4.   foo: function() {
  5.     $('#text').bind('click', function() {
  6.       console.log(this.val); // 输出:obj val
  7.     }.bind(this));
  8.   }
  9. };

  10. obj.foo();
复制代码
总结

在JavaScript中
  1. this
复制代码
的用法的确是千奇百怪,但是如果利用自然语言的方式来理解,一切就顺理成章了。不知道你读完这篇文章的时候理解了吗?
以上就是JavaScript中的this关键字用法详解的详细内容,更多关于JavaScript this关键字的资料请关注脚本之家其它相关文章!

来源:https://www.jb51.net/article/284012.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具