浅谈JavaScript中this的指向更改


Posted in Javascript onJuly 28, 2020

JS中this指向的更改

JavaScript 中 this 的指向问题 前面已经总结过,但在实际开中, 很多场景都需要改变 this 的指向。 现在我们讨论更改 this 指向的问题。

call更改this指向

call 的使用语法:func.call(thisArg, arg1, arg2, ...)

call 方法需要一个指定的 this 值( this要指向的对象 )和一个或者多个参数。提供的 this 值会更改调用函数内部的 this 指向。

// 使用 call 方法改变调用函数执行上下文的 this 指向
var animal = '小猫';
var times = '15小时';
function greet() {
 let str = this.animal + '睡觉时间一般为:' + this.times;
 console.log(str);
}
var dogObj = {
 animal: '小狗',
 times: '8小时'
};
var pigObj = {
 animal: '小猪',
 times: '13小时'
}
greet(); // 小猫睡觉时间一般为:15小时
greet.call(dogObj); // 小狗睡觉时间一般为:8小时
greet.call(pigObj); // 小猪睡觉时间一般为:13小时
greet.call(); // 小猫睡觉时间一般为:15小时

当直接调用函数 greet 时,函数 greet 内部的 this 指向的是全局对象 Window。

函数 greet 调用 call() 方法并传递对象 dogObj 时,函数 greet 内部的 this 就指向了对象 dogObj 。

函数 greet 调用 call() 方法并传递对象 pigObj 时,函数 greet 内部的 this 就指向了对象 pigObj 。

call()不传参的话,在严格模式下,this 的值将会是 undefined;否则将会指向全局对象 Window。

匿名函数调用call方法:

var books = [{
 name: 'CSS选择器',
 price: 23
}, {
 name: 'CSS世界',
 price: 35
}, {
 name: 'JavaScript语言设计',
 price: 55
}];
for (var i = 0; i < books.length; i++) {
 (function (i) {
  // 这里this指向的是call绑定的数组的每一个元素对象
  this.printf = function () {
   console.log(`${i} ${this.name}: ¥${this.price}`);
  }
  this.printf();
 }).call(books[i], i);
}
// 打印结果如下:
// 0 CSS选择器: ¥23
// 1 CSS世界: ¥35
// 2 JavaScript语言设计: ¥55

call实现继承:

// 实现两个数相加的构造函数
function CalcA(){
 this.add = function(a, b){
  return a + b;
 }
}
// 实现两个数相减的构造函数
function CalcS(){
 this.sub = function(a, b){
  return a - b;
 }
}
// 计算构造函数
function Calc(){
 console.log(this); // Calc {}
 CalcA.call(this);
 CalcS.call(this);
 console.log(this); // Calc {add: ƒ, sub: ƒ}
}
var calc = new Calc();
console.log(calc.add(2, 3)); // 5
console.log(calc.sub(10, 1));// 9

构造函数 Calc 通过 call 方法使构造函数 CalcA、CalcS中的 this 指向了 Calc 自己,从而继承了它们的属性及方法。所以,构造函数 Calc 生成的实例对象也能够访问构造函数 CalcA、CalcS中的属性及方法。

apply方法更改this指向

apply 的使用语法:func.apply(thisArg, [argsArray])

apply 的用法与 call 方法类似,只不过 call 方法接受的是参数列表,而 apply 方法接受的是一个数组或者类数组对象。上面的例子完全可以将 call 更换为 apply,只不过 apply 方法只能接受两个参数,而且第二个参数是一个数组或者类数组对象。

bind方法更改this指向

bind 的使用语法:func.bind(thisArg, arg1, arg2, ...)

bind 的参数与 call 相同,但是 bind 返回的是一个改变this指向后的函数实例。

var petalNum = 100;
function Flower() {
 this.petalNum = Math.ceil(Math.random() * 10) + 1;
}
Flower.prototype.declare = function() {
 console.log(this);
 console.log('this is a beautiful flower with ' + this.petalNum + ' petals');
}
Flower.prototype.bloom = function() {
 console.log(this); // Flower {petalNum: 7}
 // 如果不绑定 this 就会指向 Window 全局对象
 window.setTimeout(this.declare, 1000);
 // bind 绑定 this,指向 Flower 的原型对象
 window.setTimeout(this.declare.bind(this), 2000);
}
var flower = new Flower();
flower.bloom();

实例对象 flower 调用 bloom 方法后,bloom 内的 this 指向构造函数的原型对象。

1 秒后延迟函数调用构造函数的 declare 方法, 此时执行函数 declare 中的 this 指向 Window 。打印的结果如下:

// Window {parent: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
// this is a beautiful flower with 100 petals

2 秒后延迟函数调用构造函数的 declare 方法,此时执行函数 declare 通过 bind 将 this(构造函数的原型对象)绑定。打印的结果如下:

// 注意,此时petalNum的值时随机取的。
// Flower {petalNum: 7}
// this is a beautiful flower with 7 petals

这里将 bind换 成 call,apply 会导致立即执行,延迟效果会失效。

ES6的箭头函数更改this指向

箭头函数中的 this 是在定义函数的时候绑定,而不是在执行函数的时候绑定。 所谓定义时候绑定,就是指 this 是继承自父执行上下文的 this。

var a = 1;
var obj = {
 a: 2,
 f1: function(){
  console.log(this.a)
 },
 f2: () => {
  console.log(this.a)
 }
}
obj.f1(); // 2
obj.f2(); // 1

obj.f1() 执行后打印的是 2,这里好理解,obj 调用 f1 函数,那么函数中的 this 就指向调用对象 obj。可以看出,这里 this 是在执行函数的时候绑定的。

obj.f2() 执行后打印的是 1。f2 是箭头函数,那么函数中的 this 是继承自父执行上下文的 this。这里箭头函数的父级是对象 obj,obj 的执行上下文就是全局对象 Window,那么箭头函数中的 this 就指向了全局对象了。

再看一个例子:

var a = 11;
function test() {
 this.a = 22;
 let b = () => { console.log(this.a) }
 b();
}
test(); // 22

按着定义的理解,应该打印出 11 才对呀,因为箭头函数父级的执行上下文就是 Window 全局对象,此时打印的是全局对象的 a。

先不要着急,先慢慢分析,上面的分析是对的,箭头函数的 this 就是指向 Window 对象。test 函数在全局环境下调用时其内部的 this 就指向了全局 Window 对象,代码中的 this.a = 22;就将全局中的 a 重新赋值了,所以箭头函数在全局对象中找到的 a 值就是 22。我们可以在控制台上输入 window.a 查看全局对象中的 a 值,结果打印 22,所以我们就不难理解箭头函数中打印的结果为什么是 22 了。如果将代码中的 this.a = 22; 修改为 var a = 22;,那么箭头函数中打印的结果就是 11 了。

箭头函数会继承外层函数调用的 this 绑定,这和 var self = this;的绑定机制一样。箭头函数中,this 指向固定化,箭头函数根本就没有自己的 this, 所以也就不能用作构造函数使用了。

到此这篇关于浅谈JavaScript中this的指向更改的文章就介绍到这了,更多相关JavaScript中this指向更改内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
我见过最全的个人js加解密功能页面
Dec 12 Javascript
背景图跟随鼠标移动的Mootools插件实现代码
Dec 12 Javascript
js修改input的type属性问题探讨
Oct 12 Javascript
一款基于jQuery的图片场景标注提示弹窗特效
Jan 05 Javascript
自己编写的支持Ajax验证的JS表单验证插件
May 15 Javascript
js检测离开或刷新页面时表单数据是否更改的方法
Aug 02 Javascript
Bootstrap表格制作代码
Mar 17 Javascript
Angualrjs 表单验证的两种方式(失去焦点验证和点击提交验证)
May 09 Javascript
详解用node编写自己的cli工具
May 23 Javascript
微信小程序列表渲染功能之列表下拉刷新及上拉加载的实现方法分析
Nov 27 Javascript
基于vue和websocket的多人在线聊天室
Feb 01 Javascript
jdk1.8+vue elementui实现多级菜单功能
Sep 24 Javascript
Postman内建变量常用方法实例解析
Jul 28 #Javascript
使用Webpack 搭建 Vue3 开发环境过程详解
Jul 28 #Javascript
解决Vue使用bus总线时,第一次路由跳转时数据没成功传递问题
Jul 28 #Javascript
解决vue bus.$emit触发第一次$on监听不到问题
Jul 28 #Javascript
vue+element-ui JYAdmin后台管理系统模板解析
Jul 28 #Javascript
vue实现虚拟列表功能的代码
Jul 28 #Javascript
vue.js 解决v-model让select默认选中不生效的问题
Jul 28 #Javascript
You might like
短波的认识
2021/03/01 无线电
一个简单的PHP投票程序源码
2007/03/11 PHP
php面向对象 字段的声明与使用
2012/06/14 PHP
php file_get_contents抓取Gzip网页乱码的三种解决方法
2013/11/12 PHP
php中magic_quotes_gpc对unserialize的影响分析
2014/12/16 PHP
PHP数据分析引擎计算余弦相似度算法示例
2017/08/08 PHP
jquery 图片轮换效果
2010/07/29 Javascript
jquery与js函数冲突的两种解决方法
2013/09/09 Javascript
不要使用jQuery触发原生事件的方法
2014/03/03 Javascript
js实现单击图片放大图片的方法
2015/02/17 Javascript
JavaScript判断手机号运营商是移动、联通、电信还是其他(代码简单)
2015/09/25 Javascript
jQuery simpleModal插件的使用介绍
2016/08/30 Javascript
NodeJS遍历文件生产文件列表功能示例
2017/01/22 NodeJs
jquery dialog获取焦点的方法
2017/02/09 Javascript
angularjs通过过滤器返回超链接的方法
2018/10/26 Javascript
小程序如何构建骨架屏
2019/05/29 Javascript
详解express使用vue-router的history踩坑
2019/06/05 Javascript
使用 Element UI Table 的 slot-scope方法
2019/10/10 Javascript
微信小程序如何实现在线客服功能
2019/10/16 Javascript
详细介绍Python语言中的按位运算符
2013/11/26 Python
python将每个单词按空格分开并保存到文件中
2018/03/19 Python
python中如何使用分步式进程计算详解
2019/03/22 Python
TensorFlow命名空间和TensorBoard图节点实例
2020/01/23 Python
Pycharm新手使用教程(图文详解)
2020/09/17 Python
瑞典廉价机票预订网站:Seat24
2018/06/19 全球购物
在线吉他课程,学习如何弹吉他:Fender Play
2019/02/28 全球购物
介绍下WebSphere的安全性
2013/01/31 面试题
DataReader和DataSet的异同
2014/12/31 面试题
什么是ESB?请介绍一下ESB?
2015/05/27 面试题
集团公司总经理岗位职责
2013/12/20 职场文书
幼儿园保育员辞职信
2014/01/12 职场文书
一体化教学实施方案
2014/05/10 职场文书
普通员工辞职信范文
2015/05/12 职场文书
2016国庆节67周年寄语
2015/12/07 职场文书
大学生志愿者心得体会
2016/01/15 职场文书
MySql存储过程之逻辑判断和条件控制
2021/05/26 MySQL