當前位置:
首頁 > 最新 > This 帶來的困惑

This 帶來的困惑

1 引言

javascript 的 this 是個頭痛的話題,本期精讀的文章更是引出了一個觀點,避免使用 this。我們來看看是否有道理。

本期精讀的文章是:classes-complexity-and-functional-programming

2 內容概要

javascript 語言的 this 是個複雜的設計,相比純對象與純函數,this 帶來了如下問題:

const person = new Person( Jane Doe ) const getGreeting = person.getGreeting // later... getGreeting() // Uncaught TypeError: Cannot read property greeting of undefined at getGreeting

初學者可能突然將 this 弄丟導致程序出錯,甚至在 react 中也要使用 bind 的方式,使回調可以訪問到 setState 等函數。

this 也不利於測試,如果使用純函數,可以通過入參出參做測試,而不需要預先初始化環境。

所以我們可以避免使用 this,看如下的例子:

function setName(person, strName) { return Object.assign({}, person, ) } // bonus function! function setGreeting(person, newGreeting) { return Object.assign({}, person, ) } function getName(person) { return getPrefixedName( Name , person.name) } function getPrefixedName(prefix, name) { return `$: $` } function getGreetingCallback(person) { const = person return (subject) => `$ $, I m $` } const person = const person2 = setName(person, Sarah Doe ) const person3 = setGreeting(person2, Hello ) getName(person3) // Name: Sarah Doe getGreetingCallback(person3)( Jeff ) // Hello Jeff, I m Sarah Doe

這樣 person 實例是個純對象,沒有將方法掛載到原型鏈上,簡單易懂。

或者可以將屬性放在上級作用於,避免使用 this,就避免了 this 丟失帶來的隱患:

function getPerson(initialName) { let name = initialName const person = { setName(strName) { name = strName }, greeting: Hey there! , getName() { return getPrefixedName( Name ) }, getGreetingCallback() { const = person return (subject) => `$ $, I m $` }, } function getPrefixedName(prefix) { return `$: $` } return person }

以上代碼沒有用到 this,也不會因為 this 產生的問題所困擾。

3 精讀

本文作者認為,class 帶來的困惑主要在於 this,這主要因為成員函數會掛到 prototype 下,雖然多個實例共享了引用,但因此帶來的隱患就是 this 的不確定性。js 有許多種 this 丟失情況,比如 隱式綁定 別名丟失隱式綁定 回調丟失隱式綁定 顯式綁定new綁定 箭頭函數改變this作用範圍 等等。

由於在 prototype 中的對象依賴 this,如果 this 丟了,就訪問不到原型鏈,不但會引發報錯,在寫代碼時還需要注意 this 的作用範圍是很頭疼的事。因此作者有如下解決方案:

function getPerson(initialName) { let name = initialName const person = { setName(strName) { name = strName } } return person }

由此生成的 person 對象不但是個簡單 object,由於沒有調用 this,也不存在 this 丟失的情況。

這個觀點我是不認可的。當然做法沒有問題,代碼邏輯也正確,也解決了 this 存在的原型鏈訪問丟失問題,但這並不妨礙使用 this。我們看以下代碼:

class Person { setName = (name) => { this.name = name } } const person = new Person() const setName = person.setName setName("Jane Doe") console.log(person)

這裡用到了 this,也產生了別名丟失隱式綁定,但 this 還能正確訪問的原因在於,沒有將 setName 的方法放在原型鏈上,而是放在了每個實例中,因此無論怎麼丟失 this,也僅僅丟失了原型鏈上的方法,但 this 無論如何會首先查找其所在對象的方法,只要方法不放在原型鏈上,就不用擔心丟失的問題。

至於放在原型鏈上會節約多個實例內存開銷問題,函數式也無法避免,如果希望擺脫 this 帶來的困擾,class 的方式也可以解決問題。

3.1 this 丟失的情況3.1.1 默認綁定

在嚴格模式與非嚴格模式下,默認綁定有所區別,非嚴格模式 this 會綁定到上級作用域,而 use strict 時,不會綁定到 window。

function foo(){ "use strict" console.log(this.count) // TypeError: count undefined } var count = 1 foo()3.1.2 隱式綁定

當函數被對象引用起來調用時,this 會綁定到其依附的對象上。

3.1.3 別名丟失隱式綁定

調用函數引用時,this 會根據調用者環境而定。

3.1.4 回調丟失隱式綁定

這種情況類似 react 默認的情況,將函數傳遞給子組件,其調用時,this 會丟失。

3.2 this 綁定修復3.2.1 bind 顯式綁定

使用 bind 屬於顯示綁定。

3.2.2 es6綁定

這種情況類似使用箭頭函數創建成員變數,以下方式等於創建了沒有掛載到原型鏈的匿名函數,因此 this 不會丟失。

3.2.3 函數 bind

除此之外,我們還可以指定回調函數的作用域,達到 this 指向正確原型鏈的效果。

function foo(){ setTimeout(function() { console.log(this.count) // 2 }.bind(this)) } var obj = { count: 2 } foo.call(obj)

關於塊級作用域也是 this 相關的知識點,由於現在大量使用 let const 語法,甚至在 if 塊下也存在塊級作用域:

if (true) { var a = 1 let b = 2 const c = 3 } console.log(a) // 1 console.log(b) // ReferenceError console.log(c) // ReferenceError4 總結

要正視 this 帶來的問題,不能因為綁定丟失,引發非預期的報錯而避免使用,其根本原因在於 javascript 的原型鏈機制。這種機制是非常好的,將對象保存在原型鏈上,可以方便多個實例之間共享,但因此不可避免帶來了原型鏈查找過程,如果對象運行環境發生了變化,其原型鏈也會發生變化,此時無法享受到共享內存的好處,我們有兩種選擇:一種是使用 bind 將原型鏈找到,一種是比較偷懶的將函數放在對象上,而不是原型鏈上。

自動 bind 的方式 react 之前在框架層面做過,後來由於過於黑盒而取消了。如果為開發者隱藏 this 細節,框架層面自動綁定,看似方便了開發者,但過分提高開發者對 this 的期望,一旦去掉黑魔法,就會有許多開發者不適應 this 帶來的困惑,所以不如一開始就將 this 問題透傳給開發者,使用自動綁定的裝飾器,或者回調處手動 bind(this),或將函數直接放在對象中都可以解決問題。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 推酷 的精彩文章:

親手教的AI總是放心些
淺析Node與Element
重回中國市場的挪威三文魚,想要這樣拉攏吃貨們
讀Zepto源碼之屬性操作
新加坡企業網路營銷競爭性高於香港3倍

TAG:推酷 |

您可能感興趣

Consensus大會對QuarkChain的深度十問,為你帶來項目的最新進展
With Flowers 植物能給我們帶來很多美好
Raf Simons帶來的希望曙光
《The Bonfire:Forsaken Lands》帶來激動人心的生存冒險
除夕大驚喜!adidas Stan Smith也將帶來Holi Pack中國限定配色?!
超酷金屬質感!adidas Originals Ozweego 帶來四款新配色!
可能是你玩過最燒腦的戰棋!FTL製作者帶來的Into the Breach
Dad Shoes 熱潮—adidas 帶來更多 Temper Run 的全新配色
Chester Bennington與Linkin Park不僅給這個世界帶來了音樂,更是故事、回憶與勇氣
最新版Android P可能帶來iPhone X的手勢操作?
Hedi Slimane將會給我們帶來怎樣的Céline?
有點意思!Matthew M Williams x Nike 帶來 「雙層襪子」
Jordan 聯手Will Smith帶來無鞋帶的AJ 5「白葡萄」!
Vans x californiadept.帶來獨特色調的棋盤設計,經典與變奏你更喜歡哪一款?
Dad Shoe你只知道Balenciaga? adidas Twinstrike ADV新配色帶來驚喜!
Dad Shoe你只知道Balenciaga?adidas Twinstrike ADV新配色帶來驚喜!
The Joy of Giving 給予帶來喜樂
世界盃接近了,Louis Vuitton 帶來了官方授權的特別系列
Herschel Supply為Trail系列帶來高端Sailcloth新款
給大家帶來的是一款偽球鞋Balenciaga TripleS來自時裝品牌的老爹鞋