(编辑:jimmy 日期: 2024/11/15 浏览:2)
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: "htmlcode">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: "htmlcode">// 注意,此时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(); // 1obj.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, 所以也就不能用作构造函数使用了。