JSでのthis指向の変更

5352 ワード

JSでのthis指向の変更
JavaScriptでは、thisの指向性問題は前に要約されていますが、実際には、多くのシーンでthisの指向性を変更する必要があります.this方向の変更について議論します.
callはthis指向を変更します.
callの使用文法:func.call(thisArg,arg 1,arg 2,…)
call方法は、指定されたthis値(thisが指すオブジェクト)と1つまたは複数のパラメータが必要である.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がコール()方法を呼び出してオブジェクトdogObjを渡すと、関数greet内部のthisはオブジェクトdogObjを指す.
関数greetがcall()方法を呼び出して、対象pigObjを渡すと、関数greet内部のthisはオブジェクトpigObjを指す.
call()が参加しない場合、厳格なモードでthisの値はundefinedとなります.そうでないと、グローバルオブジェクトWindowを指します.
匿名関数コール方法:
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は、カルル法により、構造関数CalcA、CalcSのthisをCalc自身に向け、それらの属性および方法を継承した.したがって、構造関数Calcが生成した例示的なオブジェクトも、構造関数CalcA、CalcSにおける属性および方法にアクセスすることができる.
apply方法でthis指向を変更します.
applyの使用文法:func.apply(thisArg,[args Aray])
applyの使い方はcallの方法と似ていますが、callの方法はパラメータのリストにすぎません.上記の例では、callをappyに変更することができますが、apply方法は2つのパラメータしか受け入れられません.2番目のパラメータは1つの配列またはクラスの配列オブジェクトです.
bind方法でthis指向を変更します.
bindの使用文法:func.bind(thisArg,arg 1,arg 2,…)
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は直ちに実行し、遅延効果は無効になります.
ES 6の矢印関数は、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.f 1()の実行後に印刷されるのは2であり、ここでは、Objがf 1関数を呼び出すと、関数のthisは呼び出し対象のObjを指すことが分かります.ここでthisは、関数を実行する際に結合されていることが分かる.
Obj.f 2()実行後に印刷されるのは1です.f 2は矢印関数であり、関数の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がないので、構造関数としては使えない.