iT邦幫忙

1

JavaScript-基礎篇-This指向問題-上

  • 分享至 

  • xImage
  •  

1. This 的指向問題

默认绑定规则

    console.log(this) // window, strict模式下返回undefined
    function test() {
        console.log(this); // window
    }
    test(); // 純粹的調用 (Simple call) ==> window.test() 指向window

隱式綁定規則 (誰呼叫就指向誰)

    let a = 0;
    let obj = {
        a:2,
        foo: function(){
            console.log(this); // obj  =>  obj 呼叫了 foo, 因此 foo 裡的 this 指向為 obj
        }
    }

    obj.foo()
    let a = 0;
    let obj = {
        a:2,
        foo: function(){
            console.log(this); // 返回obj

            function test(){
                console.log(this) // window
            }
            test() // obj 呼叫了 foo, 但 test 並不是由 obj呼叫的, 而是純粹調用, 因此 this 指向 window
        }
    }

    obj.foo()

IIFE

    let a = 0;
    let obj = {
        a:2,
        foo: function(){
            (function(){
                console.log(this); // window
            })(); // 立即函式(IIFE)都屬於純粹調用, 所以 this 的指向都是 window
        }
    }
    obj.foo()

閉包

    let a = 0;
    let obj = {
        a: 2,
        foo: function () {
            function test() {
                console.log(this) // window
            }
            return test
        }
    }
    
    obj.foo()() // obj.foo() 的返回值為 test, 因此該表達式 == test() 屬於純粹調用, 因此 this 指向 window

變數賦值的情況

    let a = 0;
    function foo(){
        console.log(this);
    }
    
    let obj = {
        a:2,
        foo // ES6簡寫
    }
    obj.foo() // obj

    let bar = obj.foo // obj.foo == foo, 該表達式的結果是把 foo 的引用地址賦值給了 bar
    bar() // 因此 bar == foo , 屬於純粹調用, this 的指向為 window

參數賦值的情況

    let a = 0;
    function foo(){
        console.log(this);
    }

    function bar(fn){
        // 將傳進來的 obj.foo == foo 賦值給形參 fn
        fn() // == foo()  屬於純粹調用, 因此 this 的指向為 window
        // 因此 this 的指向只需要關注函式最終是如何執行的即可
    }

    let obj = {
        a:2,
        foo
    }
    bar(obj.foo) // obj.foo == foo 作為實參



阿里巴巴面試題

    var name = 222;
    var a = {
        name: 111,
        say: function () {
            console.log(this.name);
        },
    };
    var fun = a.say;
    fun(); // ?
    a.say(); // ?

    var b = {
        name: 333,
        say: function (fun) {
            fun(); 
        },
    };
    b.say(a.say); // ?
    b.say = a.say;
    b.say(); // ?

答案: 
    var name = 222;
    var a = {
        name: 111,
        say: function () {
            console.log(this.name);
        },
    };
    var fun = a.say;
    fun(); // 222
    a.say(); // 111

    var b = {
        name: 333,
        say: function (fun) {
            fun(); // 在這裡執行了 (a.say)(), 因此是純粹調用, this 的指向為 window
        },
    };
    b.say(a.say); // 222
    b.say = a.say;
    b.say(); // 333

forEach 內的 this 指向

    let arr = [1,2,3,4,5]

    let obj = { name : "obj" }
    // 在對應的api接口文檔中指明
    arr.forEach(function(item, index, arr){
        console.log(this); // 每次都輸出 obj
    }, obj) // 這裡的 obj 就是 arr.forEach 遍歷時的 this 指向
arr.forEach(function callback(currentValue[, index[, array]]) {
    //your iterator
}[, thisArg]);

參數: 
callback
這個 callback 函式將會把 Array 中的每一個元素作為參數,帶進本 callback 函式裡,每個元素各執行一次,接收三個參數:

currentValue
代表目前被處理中的 Array 之中的那個元素。

index (選擇性)
代表目前被處理中的 Array 之中的那個元素的 index.

array (選擇性)
呼叫 forEach() 方法的那個 Array 本身,也就是上面語法中的 arr。

thisArg (選擇性)
執行 callback 回呼函式的 this(即參考之 Object)值。

source: https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

顯式綁定: call, apply, bind

    function foo() {
        console.log(this); // 打印
    }
    let obj = {
        a: 2,
        foo
    }

    let bar = obj.foo

    obj.foo() // obj
    
    以下三種方式都能改變 this 的指向, 將 bar函式 的 this 指向改為 obj
    bar.call(obj) // obj
    bar.apply(obj) // obj
    bar.bind(obj)() // obj

補充

function foo(){
    console.log(this)
}

foo.call(1) // Number {1}
foo.call(null) // this 指向 window
foo.apply(undefined) // this 指向 window

建構式的顯式綁定實例

    function Product(name, price) {
        this.name = name;
        this.price = price;

        if (price < 0)
            throw RangeError('Cannot create product "' + name + '" with a negative price');
        return this;
    }

    function Food(name, price) {
        // new後this的指向就是這個實例物件
        console.log(this);
        Product.call(this, name, price);
        this.category = 'food';
    }
    Food.prototype = new Product();

    function Toy(name, price) {
        Product.call(this, name, price);
        this.category = 'toy';
    }
    Toy.prototype = new Product();

    let cheese = new Food('feta', 5);
    console.log(cheese); // Food {name: 'feta', price: 5, category: 'food'}
    let fun = new Toy('robot', 40);
    console.log(fun); // Toy {name: 'robot', price: 40, category: 'toy'}

new 綁定

function Person(name='haewon', age=6){
    this.name = name
    this.age = age
    console.log(this) // 這裡會打印 this
}
let me = new Person('pupu', 12) // 打印 Person {name: 'pupu', age: 12}, this 的指向為實例化的物件

Person() // 打印 window; 同時 window.name 的值為 'haewon'; window.age 的值為 6

總結

1. 默認綁定: console.log(this=== window)  ==> true 
2. 隱式綁定: (誰呼叫就指向誰) :
3. 顯式綁定: call, apply, bind
4. new 綁定規則;

優先級

1. 顯式綁定 > 隱式綁定

    function foo(){
            console.log(this.a);
        }
        let obj1 ={
            a:'obj1',
            foo
        }
        let obj2 ={
            a:'obj2',
            foo
        }
        obj1.foo() // obj1
        obj2.foo() // obj2

        obj1.foo.call(obj2) // obj2
        obj2.foo.call(obj1) // obj1
2. new 綁定 > 顯式綁定
function foo(b){
        this.a = b
    }
    let obj1 = {}
    let bar = foo.bind(obj1)
    bar(2)

    console.log(obj1.a) // 2

    let bar_child = new bar(99) // new 將 this 的指向更改為 bar_child
    console.log(obj1.a); // 2

    console.log(bar_child.a); // {a:99}

以上是我整理好的資料,希望有幫助到大家🙏


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
greenriver
iT邦研究生 4 級 ‧ 2023-02-14 09:01:27

感謝分享
很有用又詳細的資料
/images/emoticon/emoticon41.gif

我要留言

立即登入留言