浅谈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 相关文章推荐
Jquery ui css framework
Jun 28 Javascript
利用json获取字符出现次数的代码
Mar 22 Javascript
web性能优化之javascript性能调优
Dec 28 Javascript
给Flash加一个超链接(推荐使用透明层)兼容主流浏览器
Jun 09 Javascript
jquery提取元素里的纯文本不包含span等里的内容
Sep 30 Javascript
jquery提示效果实例分析
Nov 25 Javascript
浅谈DOCTYPE对$(window).height()取值的影响
Jul 21 Javascript
JS在Chrome浏览器中showModalDialog函数返回值为undefined的解决方法
Aug 03 Javascript
JS动态加载脚本并执行回调操作
Aug 24 Javascript
深入浅出webpack之externals的使用
Dec 04 Javascript
详解关于Vue版本不匹配问题(Vue packages version mismatch)
Sep 17 Javascript
vue-router二级导航切换路由及高亮显示的实现方法
Jul 10 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代码
2013/03/24 PHP
php通过文件流方式复制文件的方法
2015/03/13 PHP
php中请求url的五种方法总结
2017/07/13 PHP
PHP面向对象之里氏替换原则简单示例
2018/04/08 PHP
Laravel 微信小程序后端实现用户登录的示例代码
2019/11/26 PHP
PHPStorm2020.1永久激活及下载更新至2020(推荐)
2020/09/25 PHP
Javascript UrlDecode函数代码
2010/01/09 Javascript
jquery+json 通用三级联动下拉列表
2010/04/19 Javascript
js仿百度贴吧验证码特效实例代码
2014/01/16 Javascript
jQuery实现的原图对比窗帘效果
2014/06/15 Javascript
AngularJS入门教程引导程序
2016/08/18 Javascript
深入nodejs中流(stream)的理解
2017/03/27 NodeJs
Angularjs渲染的 using 指令的星级评分系统示例
2017/11/09 Javascript
学习RxJS之JavaScript框架Cycle.js
2019/06/17 Javascript
vue.js基于v-for实现批量渲染 Json数组对象列表数据示例
2019/08/03 Javascript
Vue退出登录时清空缓存的实现
2019/11/12 Javascript
Python进阶-函数默认参数(详解)
2017/05/18 Python
python读取LMDB中图像的方法
2018/07/02 Python
解决Python pandas plot输出图形中显示中文乱码问题
2018/12/12 Python
pyqt5 键盘监听按下enter 就登陆的实例
2019/06/25 Python
Python列表去重复项的N种方法(实例代码)
2020/05/12 Python
python 实现超级玛丽游戏
2020/11/25 Python
浅析Python模块之间的相互引用问题
2021/02/26 Python
HTML5+css3:3D旋转木马效果相册
2017/01/03 HTML / CSS
纪念建党演讲稿范文
2014/01/13 职场文书
总经理司机职责
2014/02/02 职场文书
如何写一封打动人心的求职信
2014/02/17 职场文书
人事专员的岗位职责
2014/03/01 职场文书
人力资源管理专业应届生求职信
2014/04/24 职场文书
党员承诺书怎么写
2014/05/20 职场文书
服装设计专业求职信
2014/06/16 职场文书
学雷锋活动总结报告
2014/06/26 职场文书
2014年劳动部工作总结
2014/12/11 职场文书
材料员岗位职责范本
2015/04/11 职场文书
MyBatis自定义SQL拦截器示例详解
2021/10/24 Java/Android
Redis+Lua脚本实现计数器接口防刷功能(升级版)
2022/02/12 Redis