<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                [toc] this 到底指向誰?有一種廣泛流傳的說法是:*誰調用它,this 就指向誰。* 也就是說,this 的指向實在調用時確定的。這么說沒有太大的問題,可是并不全面。面試官要求我們用更加規范的語言進行總結,那么他到底再等什么樣的回答呢? 事實上,調用函數會創建新的屬于函數自身的執行上下文。執行上下文的調用創建階段會決定 this 的指向。到此,我們可以得出一個結論: > this 的指向,是在調用函數時根據執行上下文所動態確定的。 具體環節和規則,可以先“死記硬背”以下幾條規律,后面來慢慢一一分析: * 在函數體中,簡單調用函數時(非顯示/隱式綁定下),嚴格模式下 this 綁定到undefined,否則綁定到全局對象 window/global; * 一般構造函數new調用,綁定到新創建的對象上; * 一般由 bind/call/apply 方法顯示調用,綁定到指定參數的對象上; * 一般由上下文對象調用,綁定再該對象上; * 箭頭函數中,根據外層上下文綁定的this決定this的指向。 當然,真實環境多樣,我們來逐一梳理。 ## 全局環境下的 this 在這種情況相對簡單直接,函數在瀏覽器全局環境中被簡單調用,非嚴格模式下 this 指向window;在 use strict 指明嚴格模式的情況下就是 undefined。我們來看例題,請描述打印結果: ```javascript function f1() { console.log(this) } function f2() { 'use strict' console.log(this) } f1() // window f2() // undefined ``` 這樣的題目比較基礎,但是需要候選人格外注意其變種,請再看一題: ```javascript const foo = { bar: 10, fn: function() { console.log(this) console.log(this.bar) } } var fn1 = foo.fn fn1() ``` 這里的 this 任然指向的是 window。雖然 fn 函數在 foo 對象中作為方法被引用,但是在賦值給 fn1 之后,fn1 的執行任然是在 window 的全局環境中。因此輸出 window 和 undefined,他們相當于: ```javascript console.log(window) console.log(window.bar) ``` 還是上面這道題目,如果調用改變為: ```javascript const foo = { bar: 10, fn: function() { console.log(this) console.log(this.bar) } } foo.fn() ``` 將會輸出: ```javascript { bar: 10, fn: f } 10 ``` 因為這個時候 this 指向的時最后調用它的對象,在 foo.fn() 語句中 this 指向 foo 對象。請記住: 在執行函數時,如果函數中的 this 是被上一級的對象所調用,那么 this 指向的就是上一級對象;否則指向全局環境。 ## 上下文對象調用中的this 如上結論,面對下面的題目的時候我們就不會在困惑了: ```javascript const student = { name: 'zhangsan', fn: function() { return this } } console.log(student.fn() === student) // true ``` 最終結果將返回 true。 當存在更復雜的調用關系時,請看例題: ```javascript const person = { name: 'zhangsan', brother: { name: 'lisi', fn: function() { return this.name } } } console.log(person.brother.fn()) ``` 在這種嵌套關系中,this 指向最后調用它的對象,因此輸出將會時:lisi。 到此,this 的上下文對象調用已經理解得比較清楚了。當我們再看一道更高階的題目: ```javascript const o1 = { text: 'o1', fn: function() { return this.text } } const o2 = { text: 'o2', fn: function() { return o1.fn() } } const o3 = { text: 'o3', fn: function() { var fn = o1.fn return fn() } } console.log(o1.fn()) // o1 console.log(o2.fn()) // o1 console.log(o3.fn()) // undefined ``` 答案是:o1、o1、undefined,你對對了嗎? 我們來一一分析: * 第一個 console 最簡單,o1 沒有問題。難點在第二個和第三個上面,關鍵還是看調用this的哪個函數。 * 第二個 console 的 o2.fn(),最終還是調用 o1.fn(),因此答案當然時 undefined。 * 最后一個,在進行 var fn = o1.fn 賦值之后,時“裸奔”調用,因此這里的 this 指向 window,答案當然時undefined。 如果面試者回答順利,可以緊接著追問,如果我們需要讓: ```javascript console.log(o2.fn()) ``` 輸出 o2 該怎么做? 一般開發者可能會想到使用 bind/call/apply 來對 this的指向進行干預,這確實時一種思路。但是我接著問,如果不能使用 bind/call/apply,有別的辦法嗎? 這樣可以考察候選人基礎掌握的深度及隨機應變的能力。答案為: ```javascript const o1 = { text: 'o1', fn: function() { return this.text } } const o2 = { text: 'o2', fn: o1.fn } console.log(o2.fn()) // o2 ``` 還是應用那個最重要的結論:this 指向最后調用它的對象,在 fn 執行時,掛到 o2 對象上即可,我們提前進行了賦值操作。 ## bind/call/apply 改變 this 指向 上文提到 bind/call/apply,在這個概念上,比較常見的基礎考察點時:bind/call/apply 三個方法的區別。 這樣的問題相對基礎,我們直接上答案:一句話總結,**他們都是用來改變相關函數的this指向的,但是 直接 call/apply 是直接進行相關函數調用;bind 不會執行相關函數,而是返回一個新的函數,這個新的函數已經自動綁定了新的 this指向,開發者需要手動調用即可。** 再具體的 call/apply 之間的區別主要體現在參數設定上。 用代碼來總結: ```javascript const target = {} fn.call(target, 'arg1', 'arg2') ``` 相當于 ```javascript const target = {} fn.apply(target, ['arg1', 'arg2']) ``` 相當于 ```javascript const target = {} fn.bind(target, 'arg1', 'arg2')() ``` 具體基礎用法這里不再科普,如果讀者尚不清楚,需要自己補充一下知識點。 我們來看一道例題: ```javascript const foo = { name: 'zhangsan', logName: function() { console.log(this.name) } } const bar = { name: 'lisi', } console.log(foo.logName.call(bar)) // lisi ``` 將會輸出 lisi,這不難理解。但是對 bind/call/apply 的高級考察往往會結合構造函數以及組合式實現繼承。構造函數的使用案例,我們結合下面的例題進行分析。 ## 構造函數和this 這方面最直接的例題為: ```javascript function Foo() { this.bar = 'zhangsan' } const instance = new Foo() console.log(instance.bar) // zhangsan ``` 答案將是zhangsan。但是這樣的場景往往伴隨這下一個問題:new 操作符調用構造函數,具體做了什么?以下供參考: - 創建一個對象; - 將構造函數的this指向這個新對象; - 為這個對象添加屬性、方法等; - 最終返回新對象。 以上過程,也可以用代碼表述: ```javascript var obj = {} obj.__proto__ = Foo.prototype Foo.call(obj) ``` 當然,這里對 new 的模擬是一個簡單基本版本。 需要指出的是,如果在構造函數中出現了顯示 return 的情況,那么需要注意分為兩種場景: ```javascript function Foo() { this.user = 'zhangsan' const o = {} return o } const instance = new Foo() console.log(instance.user) // undefined ``` 將會輸出undefined,此時 instance 是返回的空對象 o。 ```javascript function Foo() { this.user = 'zhangsan' return 1 } const instance = new Foo() console.log(instance.user) //zhangsan ``` 將會輸出zhangsan,也就是說此時 instance 是返回目標對象示例 this。 **結論:如果構造函數中顯示返回一個對象,那么 this 就指向這個返回的對象;如果返回的不是一個對象,那么 this仍然指向實例。** ## 箭頭函數中的this指向 **箭頭函數使用 `this`?不適應以上標準規則,而是根據外層(函數或者全局)上下文來決定**。 來看題目: ```javascript const foo = { ? ? fn: function() { ? ? ? ? setTimeout(function(){ ? ? ? ? ? ? console.log(this) ? ??? ??}) ? ??} } console.log(foo.fn()) // window ``` 這道題中,`this` 出現在 `setTimeout()` 中的匿名函數里,因此 `this` 指向 window 對象。如果需要 `this` 指向 foo 這個 object 對象,可以巧用箭頭函數解決: ```javascript const foo = { ? ? fn: function() { ? ? ? ? setTimeout(() => { ? ? ? ? ? ? console.log(this) ? ??? ??}) ? ??} } console.log(foo.fn()) // { fn: f } ``` 單純箭頭函數中的 `this` 非常簡單,但是綜合所有情況,結合 `this` 的優先級考察,這是哈哈 `this` 指向并不好確定。 ## this 優先級相關 我們常常把通過 call、apply、bind、new 對 `this` 綁定的情況稱為顯示綁定;根據調用關系確定的 `this` 指向稱為隱式綁定。 那么顯示綁定和隱式綁定誰的優先級更高呢? 情況例題: ```javascript function foo(a) { ? ? console.log(this.a) } const obj1 = { ? ? a: 1, ? ? foo: foo } const obj2 = { ? ? a: 2, ? ? foo: foo } obj1.foo.call(obj2)? ? // 2 obj2.foo.call(obj1)? ? // 1 ``` 輸出分別為2、1,也就是說 call、apply 的顯示綁定一般來說優先級更高。 ```javascript function foo(a) { ? ? this.a = a } const obj1 = {} var bar = foo.bind(obj1) bar(2) console.log(obj1.a)? ? // 2 ``` 上述代碼通過 `bin` 將 bar 函數中的 `this` 綁定為 obj1 對象。執行 `bar(2) `后 `obj1.a` 的值為`2`。即經過 `bar(2)` 執行后,`obj1` 對象為: `{ a:2 }`。 當再使用 bar 作為構造函數時: ```javascript var baz = new bar(3) console.log(baz.a)? ? // 3 ``` 將會輸出3。我們看到 bar 函數本身式通過 `bind` 方法構造的函數,其內部已經對將 `this` 綁定為 obj1,它再作為構造函數,通過 `new` 調用時,返回的實例已經與 obj1 解綁。也就是說: > `new` 綁定修改了 bind 綁定的 `this`,因此 `new` 綁定的優先級比顯示 bind綁定更高 我們再看看: ```javascript function foo() { ? ? return a => { ? ? ? ? console.log(this.a) ? ??} } const obj1 = { ? ? a: 2 } const obj2 = { ? ? a: 3 } const bar = foo.call(obj1) console.log(bar.call(obj2))? ? // 2 ``` 將會輸出2。由于 `foo()` 的 this 綁定到 obj1,bar(引用箭頭函數)的 `this` 也會綁定到 obj1,箭頭函數的綁定無法被修改。 如果將 foo 完全寫成箭頭函數的形式: ```javascript var a = 123 const foo = () => a => { ? ? console.log(this.a) } const obj1 = { ? ? a: 2 } const obj2 = { ? ? a: 3 } var bar = foo.call(obj1) console.log(bar.call(obj2))? ? // 123 ``` 將會輸出123。 這里再“抖個機靈”,僅僅將上述代碼的第一處變量 a 的賦值改為: ```javascript const a = 123 /* 同上 */ ``` 答案將會輸出 `undefined`,原因時因為使用 `const` 聲明的變量不會掛載到 window 全局對象當中。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看