1. 程式人生 > 實用技巧 >JS中this的指向問題

JS中this的指向問題

  this:當前執行上下文(global、function、eval)的一個屬性,在非嚴格模式下總是指向一個物件;在嚴格模式下可以為任意值。
全域性上下文環境: 在全域性執行環境(在任何函式體外部)中,無論是否為嚴格模式,this都指向全域性物件(在瀏覽器中全域性物件為window;在Node環境中全域性物件為globalThis); 函式上下文環境: 在函式內容,this的值取決於函式被呼叫的方式。 在非嚴格模式下,直接呼叫f1函式,此時this的指向為全域性物件,因為在瀏覽器中直接呼叫f1函式,相當於window.f1(); 在嚴格模式下直接呼叫f2函式,此時this的值為undefined,因為在直接呼叫f2函式進入f2函式執行環境時沒有設定this的值; 類上下文環境: this在類中的表現與函式中相似,需注意的是:在類的建構函式中,this是一個常規物件;類中所有非靜態方法都會被新增到this的原型中。
在非嚴格模式下使用call()和apply()時,如果指定this的值(即call和apply方法中的第一個引數)不是物件,則會被嘗試的轉化為物件(如:7-->NewNumber(7)),null和undefined會被轉化為全域性物件。 call()和apply()方法的區別:call()方法傳引數時直接寫值,apply()方法傳引數時需以陣列的形式傳遞
箭頭函式中的this 在箭頭函式中,this與封閉詞法環境中的this保持一致,不再取決於函式被呼叫時的環境,而是取決於箭頭函式被建立時的環境. 箭頭函式中的this會被永久繫結到它外層函式的this。
當函式作為物件裡的方法被呼叫時,this被設定為呼叫該函式的物件,且this的繫結只受最接近的成員引用的影響
當一個函式用作建構函式時(使用new關鍵字),它的this被繫結到正在構造的新物件   
        function f1() {
            console.log(this);
        }

        f1();  // Window -- 瀏覽器中,相當於 window.f1(); globalThis  -- Node中

        function f2() {
            'use strict';  // 採用嚴格模式
            console.log(this);
        }

        console.log('直接呼叫f2');
        f2();  // undefined
        console.log('採用window.f2()');
        window.f2();  // Window

        console.log('====================================');

        var obj = {a: 'Custom'};
        var a = 'Global';
        function whatsThis() {
            return this.a;
        }
        console.log(whatsThis());  // Global
        console.log(whatsThis.call(obj));  // Custom
        console.log(whatsThis.apply(obj));  // Custom

        console.log('====================================');
        
        function add(c, d) {
            return this.a + this.b + c + d;
        }
        var o = {a: 1, b: 3};
        console.log(add.call(o, 5, 7));  // 1+3+5+7 = 16
        console.log(add.apply(o, [10, 20]));  // 1+3+10+20 = 34

        console.log('====================================');

        function f(){
            return this.a;
        }
        var g = f.bind({a:"azerty"});  // 變數 g 為一個函式,方法體內容同函式 f 一致,this被永久指向{a:"azerty"}物件
        console.log(g());  // azerty
        var h = g.bind({a:'yoo'});  // 變數 h 為一個函式,方法體內容同函式 g 一致
        console.log(h());  //  azerty  --  bind只生效一次!
        var o = {a:37, f:f, g:g, h:h};
        console.log(o.a, o.f(), o.g(), o.h());  // 37, 37, azerty, azerty

        console.log('====================================');

        var globalObject = this;
        var foo = (() => this);
        console.log(foo() === globalObject);  // true
        var obj = {foo: foo};
        console.log(obj.foo() === globalObject);  // true
        console.log(foo.call(obj) === globalObject);  // true
        foo = foo.bind(obj);
        console.log(foo() === globalObject);  // true

        console.log('====================================');

        var obj = {
            bar: function() {
                var x = (() => this);
                return x;
            }
        };
        var fn = obj.bar();  // fn 為箭頭函式(() => this)
        console.log(fn() === obj);  // true
        var fn2 = obj.bar;  // fn2 為函式function() {var x = (() => this); return x;}
        console.log(fn2()() == window);  // true

        console.log('====================================');

        var o = {
            a: 10,
            b: 20,
            f: function() { 
                return this.a + this.b; 
            }
        };
        var p = Object.create(o);
        p.a = 1;
        p.b = 4;
        console.log(p.f());  // 5

        console.log('====================================');

        function sum() {
            return this.a + this.b + this.c;
        }

        var o = {
            a: 1,
            b: 2,
            c: 3,
            get average() {
                return (this.a + this.b + this.c) / 3;
            }
        };

        Object.defineProperty(o, 'sum', { get: sum, enumerable: true, configurable: true});

        console.log(o.average, o.sum);  // 2, 6