成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

從一道面試題,到“我可能看了假源碼[2]

chanthuang / 2777人閱讀

摘要:函數(shù)是這樣子聲明的使用了系統(tǒng)自己的構(gòu)造函數(shù)來聲明,第一個參數(shù)是,函數(shù)體內(nèi)又。構(gòu)造函數(shù)調(diào)用情況正常方式調(diào)用無窮無盡當(dāng)然,里還歸納了幾項比較簡單,我就不再翻譯了。

上一篇從一道面試題,到“我可能看了假源碼”中,由淺入深介紹了關(guān)于一篇經(jīng)典面試題的解法。
最后在皆大歡喜的結(jié)尾中,突生變化,懸念又起。這一篇,就是為了解開這個懸念。

如果你還沒有看過前傳,可以參看前情回顧:

回顧1. 題目是模擬實現(xiàn)ES5中原生bind函數(shù);
回顧2. 我們通過4種遞進(jìn)實現(xiàn)達(dá)到了完美狀態(tài);
回顧3. 可是ES5-shim中的實現(xiàn),又讓我們大跌眼鏡...

ES5-shim的懸念

ES5-shim實現(xiàn)方式源碼貼在了最后,我們看看他做了什么奇怪的事情:
1)從結(jié)果上看,返回了bound函數(shù)。
2)bound函數(shù)是這樣子聲明的:

bound = Function("binder", "return function (" + boundArgs.join(",") + "){ return binder.apply(this, arguments); }")(binder);

3)bound使用了系統(tǒng)自己的構(gòu)造函數(shù)Function來聲明,第一個參數(shù)是binder,函數(shù)體內(nèi)又binder.apply(this, arguments)。
我們知道這種動態(tài)創(chuàng)建函數(shù)的方式,類似eval。最好不要使用它,因為用它定義函數(shù)比用傳統(tǒng)方式要慢得多。
4)那么ES5-shim抽風(fēng)了嗎?

追根問底

答案肯定是沒抽風(fēng),他這樣做是有理由的。

神秘的函數(shù)的length屬性

你可能不知道,每個函數(shù)都有l(wèi)ength屬性。對,就像數(shù)組和字符串那樣。函數(shù)的length屬性,用于表示函數(shù)的形參個數(shù)。更重要的是函數(shù)的length屬性值是不可重寫的。我寫了個測試代碼來證明:

function test (){}
test.length  // 輸出0
test.hasOwnProperty("length")  // 輸出true
Object.getOwnPropertyDescriptor("test", "length") 
// 輸出:
// configurable: false, 
// enumerable: false,
// value: 4, 
// writable: false 
撥云見日

說到這里,那就好解釋了。
ES5-shim是為了最大限度的進(jìn)行兼容,包括對返回函數(shù)length屬性的還原。如果按照我們之前實現(xiàn)的那種方式,length值始終為零。
所以:既然不能修改length的屬性值,那么在初始化時賦值總可以吧!
于是我們可通過eval和new Function的方式動態(tài)定義函數(shù)來。
同時,很有意思的是,源碼里有這樣的注釋:

// XXX Build a dynamic function with desired amount of arguments is the only
// way to set the length property of a function.
// In environments where Content Security Policies enabled (Chrome extensions,
// for ex.) all use of eval or Function costructor throws an exception.
// However in all of these environments Function.prototype.bind exists
// and so this code will never be executed.

他解釋了為什么要使用動態(tài)函數(shù),就如同我們上邊所講的那樣,是為了保證length屬性的合理值。但是在一些瀏覽器中出于安全考慮,使用eval或者Function構(gòu)造器都會被拋出異常。但是,巧合也就是這些瀏覽器基本上都實現(xiàn)了bind函數(shù),這些異常又不會被觸發(fā)。

So, What a coincidence!

嘆為觀止

我們明白了這些,再看他的進(jìn)一步實現(xiàn):

if (!isCallable(target)) {
    throw new TypeError("Function.prototype.bind called on incompatible " + target);
}

這是為了保證調(diào)用的正確性,他使用了isCallable做判斷,isCallable很好實現(xiàn):

isCallable = function isCallable(value) { 
    if (typeof value !== "function") { 
        return false; 
    }
}

重設(shè)綁定函數(shù)的length屬性:

var boundLength = max(0, target.length - args.length);

構(gòu)造函數(shù)調(diào)用情況,在binder中也有效兼容。如果你不明白什么是構(gòu)造函數(shù)調(diào)用情況,可以參考上一篇。

if (this instanceof bound) { 
    ... // 構(gòu)造函數(shù)調(diào)用情況
} else {
    ... // 正常方式調(diào)用
}

if (target.prototype) {
    Empty.prototype = target.prototype;
    bound.prototype = new Empty();
    // Clean up dangling references.
    Empty.prototype = null;
}
無窮無盡

當(dāng)然,ES5-shim里還歸納了幾項todo...

// TODO
// 18. Set the [[Extensible]] internal property of F to true.
// 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
// 20. Call the [[DefineOwnProperty]] internal method of F with
//   arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]:
//   thrower, [[Enumerable]]: false, [[Configurable]]: false}, and
//   false.
// 21. Call the [[DefineOwnProperty]] internal method of F with
//   arguments "arguments", PropertyDescriptor {[[Get]]: thrower,
//   [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false},
//   and false.
// 22. Return F.

比較簡單,我就不再翻譯了。

源碼回放
bind: function bind(that) {
    var target = this;
    if (!isCallable(target)) {
        throw new TypeError("Function.prototype.bind called on incompatible " + target);
    }
    var args = array_slice.call(arguments, 1);
    var bound;
    var binder = function () {
        if (this instanceof bound) {
            var result = target.apply(
                this,
                array_concat.call(args, array_slice.call(arguments))
            );
            if ($Object(result) === result) {
                return result;
            }
            return this;
        } else {
            return target.apply(
                that,
                array_concat.call(args, array_slice.call(arguments))
            );
        }
    };
    var boundLength = max(0, target.length - args.length);
    var boundArgs = [];
    for (var i = 0; i < boundLength; i++) {
        array_push.call(boundArgs, "$" + i);
    }
    bound = Function("binder", "return function (" + boundArgs.join(",") + "){ return binder.apply(this, arguments); }")(binder);

    if (target.prototype) {
        Empty.prototype = target.prototype;
        bound.prototype = new Empty();
        Empty.prototype = null;
    }
    return bound;
}
總結(jié)

通過學(xué)習(xí)ES5-shim的源碼實現(xiàn)bind方法,結(jié)合前一篇,希望讀者能對bind和JS包括閉包,原型原型鏈,this等一系列知識點(diǎn)能有更深刻的理解。
同時在程序設(shè)計上,尤其是邏輯的嚴(yán)密性上,有所積累。

PS:百度知識搜索部大前端繼續(xù)招兵買馬,有意向者火速聯(lián)系。。。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/81630.html

相關(guān)文章

  • 深入理解js

    摘要:詳解十大常用設(shè)計模式力薦深度好文深入理解大設(shè)計模式收集各種疑難雜癥的問題集錦關(guān)于,工作和學(xué)習(xí)過程中遇到過許多問題,也解答過許多別人的問題。介紹了的內(nèi)存管理。 延遲加載 (Lazyload) 三種實現(xiàn)方式 延遲加載也稱為惰性加載,即在長網(wǎng)頁中延遲加載圖像。用戶滾動到它們之前,視口外的圖像不會加載。本文詳細(xì)介紹了三種延遲加載的實現(xiàn)方式。 詳解 Javascript十大常用設(shè)計模式 力薦~ ...

    caikeal 評論0 收藏0
  • 一道面試引發(fā)的思考 --- Event Loop

    摘要:想必面試題刷的多的同學(xué)對下面這道題目不陌生,能夠立即回答出輸出個,可是你真的懂為什么嗎為什么是輸出為什么是輸出個這兩個問題在我腦邊縈繞。同步任務(wù)都好理解,一個執(zhí)行完執(zhí)行下一個。本文只是我對這道面試題的一點(diǎn)思考,有誤的地方望批評指正。 想必面試題刷的多的同學(xué)對下面這道題目不陌生,能夠立即回答出輸出10個10,可是你真的懂為什么嗎?為什么是輸出10?為什么是輸出10個10?這兩個問題在我腦...

    betacat 評論0 收藏0
  • 一道面試,可能了假源碼

    摘要:返回的綁定函數(shù)也能使用操作符創(chuàng)建對象這種行為就像把原函數(shù)當(dāng)成構(gòu)造器。同時,將第一個參數(shù)以外的其他參數(shù),作為提供給原函數(shù)的預(yù)設(shè)參數(shù),這也是基本的顆粒化基礎(chǔ)。 今天想談?wù)勔坏狼岸嗣嬖囶},我做面試官的時候經(jīng)常喜歡用它來考察面試者的基礎(chǔ)是否扎實,以及邏輯、思維能力和臨場表現(xiàn),題目是:模擬實現(xiàn)ES5中原生bind函數(shù)。也許這道題目已經(jīng)不再新鮮,部分讀者也會有思路來解答。社區(qū)上關(guān)于原生bind的研...

    Carson 評論0 收藏0
  • 一道面試,可能了假源碼

    摘要:返回的綁定函數(shù)也能使用操作符創(chuàng)建對象這種行為就像把原函數(shù)當(dāng)成構(gòu)造器。同時,將第一個參數(shù)以外的其他參數(shù),作為提供給原函數(shù)的預(yù)設(shè)參數(shù),這也是基本的顆粒化基礎(chǔ)。 今天想談?wù)勔坏狼岸嗣嬖囶},我做面試官的時候經(jīng)常喜歡用它來考察面試者的基礎(chǔ)是否扎實,以及邏輯、思維能力和臨場表現(xiàn),題目是:模擬實現(xiàn)ES5中原生bind函數(shù)。也許這道題目已經(jīng)不再新鮮,部分讀者也會有思路來解答。社區(qū)上關(guān)于原生bind的研...

    rockswang 評論0 收藏0
  • 一道面試可能了假源碼

    摘要:返回的綁定函數(shù)也能使用操作符創(chuàng)建對象這種行為就像把原函數(shù)當(dāng)成構(gòu)造器。同時,將第一個參數(shù)以外的其他參數(shù),作為提供給原函數(shù)的預(yù)設(shè)參數(shù),這也是基本的顆?;A(chǔ)。 今天想談?wù)勔坏狼岸嗣嬖囶},我做面試官的時候經(jīng)常喜歡用它來考察面試者的基礎(chǔ)是否扎實,以及邏輯、思維能力和臨場表現(xiàn),題目是:模擬實現(xiàn)ES5中原生bind函數(shù)。也許這道題目已經(jīng)不再新鮮,部分讀者也會有思路來解答。社區(qū)上關(guān)于原生bind的研...

    jlanglang 評論0 收藏0

發(fā)表評論

0條評論

chanthuang

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<