摘要:而模擬的方法返回的函數(shù)用作構(gòu)造函數(shù)時,生成的對象為。同樣,使用運算符時,綁定構(gòu)造函數(shù)和未綁定構(gòu)造函數(shù)并無兩樣。標(biāo)準的方法創(chuàng)建一個新函數(shù)稱為綁定函數(shù),新函數(shù)與被調(diào)函數(shù)綁定函數(shù)的目標(biāo)函數(shù)具有相同的函數(shù)體在規(guī)范中內(nèi)置的屬性。
這是一道面試題,題目給出了使用bind方法的樣例,要求用javascript實現(xiàn)這個方法,面試官還很善意的提醒我函數(shù)柯里化,然而,我還是不會這道題目,所以回來這會《javacript權(quán)威指南》和《javacript 高級教程》開始學(xué)習(xí)相關(guān)知識。
一、javacript實現(xiàn)bind方法bind()是在ECMAScript5中新增的方法,但是在ECMAScript3中可以輕易的模擬bind()。
版本一這部分參考了《javacript權(quán)威指南》權(quán)威指南的p191,ECMAScript3版本的Function.bind()方法的實現(xiàn)。
if(!Function.prototype.bind){ Function.prototype.bind = function(o){ // 將`this`和`arguments`的值保存在變量中,以便在后面嵌套的函數(shù)中可以使用它們 var self = this, boundArgs = arguments; //bind方法的返回值是一個函數(shù) return function(){ var args = [],//創(chuàng)建一個實參列表,將傳入的bind()的第二個及后續(xù)的實參都傳入這個函數(shù)。 i; for(i=1;i版本一存在的問題 上述ECMAScript3版本的Function.bind()方法和ECMAScript5中定義的bind()有些出入,主要有以下三個方面。
真正的bind()方法(ECMAScript5中定義的bind())返回一個函數(shù)對象,這個函數(shù)對象的length屬性是綁定函數(shù)的形參個數(shù)減去綁定實參的個數(shù)。而模擬的bind()方法返回的函數(shù)對象的length屬性的值為0.
真正的bind()方法可以順帶用作構(gòu)造函數(shù),此時將忽略傳入bind()的this,原始函數(shù)就會以構(gòu)造函數(shù)的形式調(diào)用,其實參也已經(jīng)綁定。而模擬的bind()方法返回的函數(shù)用作構(gòu)造函數(shù)時,生成的對象為Object()。
真正的bind()方法所返回的函數(shù)并不包含prototype屬性(普通函數(shù)固有的prototype屬性是不能刪除的),并且將這些綁定的函數(shù)用作構(gòu)造函數(shù)時所創(chuàng)建的對象從原始的未綁定的構(gòu)造函數(shù)中繼承prototype。同樣,使用instanceof運算符時,綁定構(gòu)造函數(shù)和未綁定構(gòu)造函數(shù)并無兩樣。
版本二針對上述ECMAScript3版本的Function.bind()方法存在的問題,《JavaScript Web Application》一書中給出的版本有針對性的修復(fù)了這些問題。
Function.prototype.bind = function(context){ var args = Array.prototype.slice.call(arguments,1),//要點3 self = this, F = function(){};//要點1 bound = function(){ var innerArgs = Array.prototype.slice.call(arguments); var finalArgs = args.concat(innerArgs); return self.apply((this instanceof F ? this : context),finalArgs);//要點2 }; F.prototype = self.prototype; bound.prototype = new F(); return bound; }要點1,解釋
如下這段代碼,實際上用到了原型式繼承。這跟ECMAscript5中的Object.creat()方法只接受一個參數(shù)時是一樣的。
F = function(){};//要點1 ... F.prototype = self.prototype; bound.prototype = new F();要點2,解釋
如下這段代碼,是要判斷通過bind方法綁定得到的函數(shù),是直接調(diào)用還是用作構(gòu)造函數(shù)通過new來調(diào)用的。
this instanceof F ? this : context為了分析這段代碼的具體含義,需要知道通過構(gòu)造函數(shù)生成對象時,new操作符都干了啥。比如如下代碼:
var a = new B()(1).首先創(chuàng)建一個空對象,var a = {};
(2).將構(gòu)造函數(shù)的作用域賦給新對象(因此,this就指向了這個新對象);
(3).執(zhí)行構(gòu)造函數(shù)中的代碼(為這個新對象添加屬性), B.call(a);
(4).繼承被構(gòu)造函數(shù)的原型,a._proto_ = B.prototype;
(5).返回這個新對象。標(biāo)準的bind方法:
創(chuàng)建一個新函數(shù)(稱為綁定函數(shù)),新函數(shù)與被調(diào)函數(shù)(綁定函數(shù)的目標(biāo)函數(shù))具有相同的函數(shù)體(在 ECMAScript 5 規(guī)范中內(nèi)置的call屬性)。當(dāng)目標(biāo)函數(shù)被調(diào)用時this值綁定到bind()的第一個參數(shù),該參數(shù)不能被重寫。綁定函數(shù)被調(diào)用時,bind() 也接受預(yù)設(shè)的參數(shù)提供給原函數(shù)。一個綁定函數(shù)也能使用new操作符創(chuàng)建對象:這種行為就像把原函數(shù)當(dāng)成構(gòu)造器。提供的this值被忽略,同時調(diào)用時的參數(shù)被提供給模擬函數(shù)。
通過原型鏈的繼承可以判斷綁定函數(shù)是否用作了構(gòu)造函數(shù),通過new操作符來調(diào)用。假設(shè)目標(biāo)函數(shù)為funObj,綁定函數(shù)為funBind.即
var funBind = funObj.bind(context); var obj = new funBind();上面代碼具有如下繼承關(guān)系(這里畫出繼承關(guān)系圖更容易理解):
obj instanceof funBind // true funBind.prototype instanceof F //true F.prototype = self.prototyepa instanceof B原理,是判斷B.prototype是否存在于a的原型鏈中。因此有
obj instanceof F // true此外,要點2這里還用到了借用構(gòu)造函數(shù)來實現(xiàn)繼承,如下代碼
self.apply(this,finalArgs)要點3,解釋
版本二測試這里實際上是將類數(shù)組對象轉(zhuǎn)化為數(shù)組,因為類數(shù)組對象,比如arguments、nodelist;雖然很像數(shù)組,比如具有length屬性,但是不是數(shù)組,比如,沒有concat、slice這些方法.
常用的將類數(shù)組對象轉(zhuǎn)為數(shù)組的方法有
(1).Array.prototype.slice.call
(2).擴展運算符...,比如[...arguments]
(3). Array.from();測試1
可見,版本二并沒有解決版本一的問題1和3
測試2
版本二的精簡版可見版本二解決了版本一的問題2
版本二中要點1和要點2看著很不爽,于是,我給精簡了一下,測試結(jié)果與版本二相同。
Function.prototype.bind = function(context){ var args = Array.prototype.slice.call(arguments,1),//要點3 self = this, //F = function(){};//要點1 bound = function(){ var innerArgs = Array.prototype.slice.call(arguments); var finalArgs = args.concat(innerArgs); //return self.apply((this instanceof F ? this : context),finalArgs);//要點2 return self.apply((this instanceof self ? this : context),finalArgs);//要點2 }; //F.prototype = self.prototype; //bound.prototype = new F(); bound.prototype = self.prototype; return bound; }測試結(jié)果如下:
二、bind函數(shù)應(yīng)用關(guān)于bind函數(shù)的應(yīng)用這里只提兩點在我使用這個方法的時候,遇到的讓我剛開始比較懵逼仔細一想還真是這么回事的問題。
一段神奇的代碼var unBindSlice = Array.prototype.slice; var bindSlice = Function.prototype.call.bind(unBindSlice); ... bindSlice(arguments);測試一下
這段代碼的作用就是將一個類數(shù)組對象轉(zhuǎn)化為真正的數(shù)組,是下面這段代碼的另一種寫法而已
Array.prototype.slice.call(arguments);bind函數(shù)只創(chuàng)建一個新函數(shù)而不執(zhí)行將一個函數(shù)對象作為bind的context,這種寫法的作用是,為需要特定this值的函數(shù)創(chuàng)造捷徑。
私以為這是bind和call與apply方法的一個重要差別,call和apply這兩個方法都會立即執(zhí)行函數(shù),返回的是函數(shù)執(zhí)行后的結(jié)果。而bind函數(shù)只創(chuàng)建一個新函數(shù)而不執(zhí)行。
之前看過一段錯誤的代碼,就是用apply改變一個構(gòu)造函數(shù)的this,緊接著又用這個構(gòu)造函數(shù)創(chuàng)建新對象,毫無疑問這是錯誤的,遺憾的是找不到這段錯誤代碼的出處了。
三、函數(shù)柯里化函數(shù)柯里化是與函數(shù)綁定緊密相關(guān)的一個主題,它用于創(chuàng)建已經(jīng)設(shè)置好了一個或者多個參數(shù)的函數(shù)。函數(shù)柯里化的基本方法與函數(shù)綁定是一樣的:使用一個閉包返回一個函數(shù)。
柯里化函數(shù)通常創(chuàng)建步驟如下:調(diào)用另一個函數(shù)并為它傳入要柯里化的函數(shù)和必要參數(shù)。同樣方式如下:
function curry(fn){ var args = Array.prototype.slice.call(arguments,1); return function(){ var innerArgs = Array.prototype.slice.call(arguments); var finalArgs = args.concat(innerArgs); return fn.apply(null, finalArgs); }; }有沒有感到很熟悉,其實上面bind方法的兩個實現(xiàn)版本都用到了函數(shù)柯里化,區(qū)別在于,這里的通用函數(shù)沒用考慮到執(zhí)行環(huán)境。
曾經(jīng)看過一段類似函數(shù)柯里化的代碼,私以為很巧妙,如下:
假如有一個對象數(shù)組,想要根據(jù)對象的某個屬性來對其進行排序。而傳遞給sort方法的比較函數(shù)只能接受兩個參數(shù),即比較的值,這樣就無法指定排序的對象屬性了。如何將需要三個參數(shù)的函數(shù)轉(zhuǎn)化為滿足要求的僅需要兩個參數(shù)?要解決這個問題,可以定義一個函數(shù),它接收一個屬性名,然后根據(jù)這個屬性名創(chuàng)建并返回一個比較函數(shù),如下:
function createComparisionFunction(property){ return function(obj1,obj2){ return obj1[property]-obj2[property]; }; }四、參考文獻1.Javascript中bind()方法的使用與實現(xiàn).
2.javascript原生一步步實現(xiàn)bind分析.
3.JS中的bind方法與函數(shù)柯里化.
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/86648.html
摘要:引言上一節(jié)介紹了高階函數(shù)的定義,并結(jié)合實例說明了使用高階函數(shù)和不使用高階函數(shù)的情況。我們期望函數(shù)輸出,但是實際上調(diào)用柯里化函數(shù)時,所以調(diào)用時就已經(jīng)執(zhí)行并輸出了,而不是理想中的返回閉包函數(shù),所以后續(xù)調(diào)用將會報錯。引言 上一節(jié)介紹了高階函數(shù)的定義,并結(jié)合實例說明了使用高階函數(shù)和不使用高階函數(shù)的情況。后面幾部分將結(jié)合實際應(yīng)用場景介紹高階函數(shù)的應(yīng)用,本節(jié)先來聊聊函數(shù)柯里化,通過介紹其定義、比較常見的...
摘要:柯里化通用式上面的柯里化函數(shù)沒涉及到高階函數(shù),也不具備通用性,無法轉(zhuǎn)換形參個數(shù)任意或未知的函數(shù),我們接下來封裝一個通用的柯里化轉(zhuǎn)換函數(shù),可以將任意函數(shù)轉(zhuǎn)換成柯里化。 showImg(https://segmentfault.com/img/remote/1460000018998373); 閱讀原文 前言 在 JavaScript 中,柯里化和反柯里化是高階函數(shù)的一種應(yīng)用,在這之前...
摘要:作為函數(shù)式編程語言,帶來了很多語言上的有趣特性,比如柯里化和反柯里化。在一些函數(shù)式編程語言中,會定義一個特殊的占位變量。個人理解不知道對不對延遲執(zhí)行柯里化的另一個應(yīng)用場景是延遲執(zhí)行。不斷的柯里化,累積傳入的參數(shù),最后執(zhí)行。作為函數(shù)式編程語言,JS帶來了很多語言上的有趣特性,比如柯里化和反柯里化。 這里可以對照另外一篇介紹 JS 反柯里化 的文章一起看~ 1. 簡介 柯里化(Currying)...
摘要:簡介柯里化,又稱部分求值,是把接收多個參數(shù)的函數(shù)變成接受一個單一參數(shù)最初函數(shù)的第一個參數(shù)的函數(shù),并且返回接受剩余的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。按照作者的說法,所謂柯里化就是使函數(shù)理解并處理部分應(yīng)用。的思想極大地助于提升函數(shù)的復(fù)用性。 簡介 柯里化(Currying),又稱部分求值(Partial Evaluation),是把接收多個參數(shù)的函數(shù)變成接受一個單一參數(shù)(最初函數(shù)的第一個...
摘要:面試題實現(xiàn)結(jié)果,題的核心就是問的的柯里化先說說什么是柯里化,看過許多關(guān)于柯里化的文章,始終搞不太清楚,例如柯里化是把接受多個參數(shù)的函數(shù)變換成接受一個單一參數(shù)最初函數(shù)的第一個參數(shù)的函數(shù),并且返回接受余下的參數(shù)且返回結(jié)果的新函數(shù)的技術(shù)。 面試題:實現(xiàn)add(1)(2)(3) //結(jié)果 = 6,題的核心就是問的js的柯里化 先說說什么是柯里化,看過許多關(guān)于柯里化的文章,始終搞不太清楚,例如...
閱讀 666·2021-11-15 11:37
閱讀 4105·2021-09-09 09:34
閱讀 3559·2019-08-30 15:52
閱讀 2602·2019-08-29 14:03
閱讀 2842·2019-08-26 13:36
閱讀 1587·2019-08-26 12:16
閱讀 1592·2019-08-26 11:45
閱讀 3488·2019-08-23 18:41