JS中this指向的更改


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, 所以也就不能用作構造函數使用了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM