浅谈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判断样式className同时增加class或删除class
Jan 30 Javascript
jQuery实现表格颜色交替显示的方法
Mar 09 Javascript
js实现图片无缝滚动
Dec 23 Javascript
JavaScript的String字符串对象常用操作总结
May 26 Javascript
JavaScript面向对象分层思维全面解析
Nov 22 Javascript
如何使用vuejs实现更好的Form validation?
Apr 07 Javascript
基于JavaScript实现新增内容滚动播放效果附完整代码
Aug 24 Javascript
bootstrap时间插件daterangepicker使用详解
Oct 19 Javascript
Vue下滚动到页面底部无限加载数据的示例代码
Apr 22 Javascript
JS实现计算小于非负数n的素数的数量算法示例
Feb 26 Javascript
jQuery添加新内容的四个常用方法分析【append,prepend,after,before】
Mar 19 jQuery
使用element-ui +Vue 解决 table 里包含表单验证的问题
Jul 17 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
在线竞拍系统的PHP实现框架(二)
2006/10/09 PHP
php 文章调用类代码
2011/08/11 PHP
PHP stream_context_create()函数的使用示例
2015/05/12 PHP
解读PHP中上传文件的处理问题
2016/05/29 PHP
JavaScript实现删除电脑的关机键
2016/07/26 PHP
Python中使用django form表单验证的方法
2017/01/16 PHP
Jquery数独游戏解析(一)-页面布局
2010/11/05 Javascript
jQuery 淡入淡出 png图在ie8下有黑色边框的解决方法
2013/03/05 Javascript
getComputedStyle与currentStyle获取样式(style/class)
2013/03/19 Javascript
extjs中form与grid交互数据(record)的方法
2013/08/29 Javascript
js添加select下默认的option的value和text的方法
2014/10/19 Javascript
JS 拼凑字符串的简单实例
2016/09/02 Javascript
Bootstrap table使用方法详细介绍
2016/12/09 Javascript
AngularJS中$http使用的简单介绍
2017/03/17 Javascript
vue v-model表单控件绑定详解
2017/05/17 Javascript
详解angularJS+Ionic移动端图片上传的解决办法
2017/09/13 Javascript
详解Vuex管理登录状态
2017/11/13 Javascript
JavaScript解析机制与闭包原理实例详解
2019/03/08 Javascript
vue鼠标悬停事件实例详解
2019/04/01 Javascript
详解webpack-dev-middleware 源码解读
2020/03/23 Javascript
JS实现悬浮球只在一侧滑动并且是横屏状态下
2020/08/19 Javascript
详解 javascript对象创建模式
2020/10/30 Javascript
Python3实现生成随机密码的方法
2014/08/23 Python
Python异常处理例题整理
2019/07/07 Python
对于Python深浅拷贝的理解
2019/07/29 Python
Python (Win)readline和tab补全的安装方法
2019/08/27 Python
详解Python 中的容器 collections
2020/08/17 Python
Pytorch 扩展Tensor维度、压缩Tensor维度的方法
2020/09/09 Python
销售工作人员的自我评价分享
2013/11/10 职场文书
2016学习雷锋精神活动倡议书
2015/04/27 职场文书
2015年学校禁毒工作总结
2015/05/27 职场文书
尼克胡哲观后感
2015/06/08 职场文书
Feign调用传输文件异常的解决
2021/06/24 Java/Android
低版本Druid连接池+MySQL驱动8.0导致线程阻塞、性能受限
2021/07/01 MySQL
Python可变与不可变数据和深拷贝与浅拷贝
2022/04/06 Python
解决MySQL报“too many connections“错误
2022/04/19 MySQL