【01-18】call apply bind

apply 接收一個(gè)數(shù)組參數(shù)

Function.prototype.my_apply=function(target){

    const cxt = target||window;
    
    //將當(dāng)前被調(diào)用的方法定義再ctx.fun上
    //為了能以對(duì)象調(diào)用的形式綁定this
    cxt.func = this;

    const res = arguments[1]?cxt.func(...arguments[1]):cxt.func();

    //否則會(huì)對(duì)傳入對(duì)象造成污染(添加該方法)
    delete cxt.func;

    return res;
}

call 接收的是一個(gè)列表

function call(target){
    const cxt = target||window;

    //將當(dāng)前被調(diào)用的方法定義再ctx.fun上
    //為了能以對(duì)象調(diào)用的形式綁定this
    cxt.func = this;

    const args = Array.from(arguments).slice(1);

    const res = args.length>0?cxt.func(...args):cxt.func();

    delete cxt.func;

    return res;

}

apply 不會(huì)立即執(zhí)行而是返回一個(gè)函數(shù)

function baz (fn,obj) {

return function(){

return fn.apply(obj,arguments);

}

}

詳解bind

var obj = {};
console.log(obj);
console.log(typeof Function.prototype.bind); // function
console.log(typeof Function.prototype.bind());  // function
console.log(Function.prototype.bind.name);  // bind
console.log(Function.prototype.bind().name);  // bound

1龄捡、bind是Functoin原型鏈中Function.prototype的一個(gè)屬性,每個(gè)函數(shù)都可以調(diào)用它慷暂。

2聘殖、bind本身是一個(gè)函數(shù)名為bind的函數(shù),返回值也是函數(shù)行瑞,函數(shù)名是bound 奸腺。(打出來(lái)就是bound加上一個(gè)空格)。
知道了bind是函數(shù)血久,就可以傳參突照,而且返回值'bound '也是函數(shù),也可以傳參氧吐,就很容易寫(xiě)出例子2:

后文統(tǒng)一 bound 指原函數(shù)original bind之后返回的函數(shù)讹蘑,便于說(shuō)明。

var obj = {
    name: '軒轅Rowboat',
};
function original(a, b){
    console.log(this.name);
    console.log([a, b]);
    return false;
}
var bound = original.bind(obj, 1);
var boundResult = bound(2); // '軒轅Rowboat', [1, 2]
console.log(boundResult); // false
console.log(original.bind.name); // 'bind'
console.log(original.bind.length); // 1
console.log(original.bind().length); // 2 返回original函數(shù)的形參個(gè)數(shù)
console.log(bound.name); // 'bound original'
console.log((function(){}).bind().name); // 'bound '
console.log((function(){}).bind().length); // 0

由此可以得出結(jié)論2:

  • 1筑舅、調(diào)用bind的函數(shù)中的this指向bind()函數(shù)的第一個(gè)參數(shù)座慰。

  • 2、傳給bind()的其他參數(shù)接收處理了翠拣,bind()之后返回的函數(shù)的參數(shù)也接收處理了版仔,也就是說(shuō)合并處理了。

  • 3误墓、并且bind()后的name為bound + 空格 + 調(diào)用bind的函數(shù)名邦尊。如果是匿名函數(shù)則是bound + 空格。

  • 4优烧、bind后的返回值函數(shù)蝉揍,執(zhí)行后返回值是原函數(shù)(original)的返回值。

  • 5畦娄、bind函數(shù)形參(即函數(shù)的length)是1又沾。bind后返回的bound函數(shù)形參不定弊仪,根據(jù)綁定的函數(shù)原函數(shù)(original)形參個(gè)數(shù)確定。

根據(jù)結(jié)論2:我們就可以簡(jiǎn)單模擬實(shí)現(xiàn)一個(gè)簡(jiǎn)版bindFn

// 第一版 修改this指向杖刷,合并參數(shù)
Function.prototype.bindFn = function bind(thisArg){
    if(typeof this !== 'function'){
        throw new TypeError(this + 'must be a function');
    }
    // 存儲(chǔ)函數(shù)本身
    var self = this;
    // 去除thisArg的其他參數(shù) 轉(zhuǎn)成數(shù)組
    var args = [].slice.call(arguments, 1);
    var bound = function(){
        // bind返回的函數(shù) 的參數(shù)轉(zhuǎn)成數(shù)組
        var boundArgs = [].slice.call(arguments);
        // apply修改this指向励饵,把兩個(gè)函數(shù)的參數(shù)合并傳給self函數(shù),并執(zhí)行self函數(shù)滑燃,返回執(zhí)行結(jié)果
        return self.apply(thisArg, args.concat(boundArgs));
    }
    return bound;
}
// 測(cè)試
var obj = {
    name: '軒轅Rowboat',
};
function original(a, b){
    console.log(this.name);
    console.log([a, b]);
}
var bound = original.bindFn(obj, 1);
bound(2); // '軒轅Rowboat', [1, 2]

但我們知道函數(shù)是可以用new來(lái)實(shí)例化的役听。那么bind()返回值函數(shù)會(huì)是什么表現(xiàn)呢。

參照mdn實(shí)現(xiàn)了一個(gè)簡(jiǎn)易版的

Function.prototype.bindfun=function(){
const thatFun = this;
const thatTarget=arguments[0]
let args = [].slice.call(arguments,1);
const bound = function(){
    let boundArgs=[].slice.call(arguments);
    return thatFun.apply(thatTarget,args.concat(boundArgs))
}
return bound;
}
let f=fun.bindfun(o1,1);
f(2)

接下來(lái)看例子3

var obj = {
    name: '軒轅Rowboat',
};
function original(a, b){
    console.log('this', this); // original {}
    console.log('typeof this', typeof this); // object
    this.name = b;
    console.log('name', this.name); // 2
    console.log('this', this);  // original {name: 2}
    console.log([a, b]); // 1, 2
}
var bound = original.bind(obj, 1);
var newBoundResult = new bound(2);
console.log(newBoundResult, 'newBoundResult'); // original {name: 2}

例子3種可以看出this指向了new bound()生成的新對(duì)象表窘。

可以分析得出結(jié)論3:

1典予、bind原先指向obj的失效了,其他參數(shù)有效乐严。

2瘤袖、new bound的返回值是以original原函數(shù)構(gòu)造器生成的新對(duì)象。original原函數(shù)的this指向的就是這個(gè)新對(duì)象昂验。
另外前不久寫(xiě)過(guò)一篇文章:面試官問(wèn):能否模擬實(shí)現(xiàn)JS的new操作符捂敌。簡(jiǎn)單摘要:
new做了什么:

1.創(chuàng)建了一個(gè)全新的對(duì)象。

2.這個(gè)對(duì)象會(huì)被執(zhí)行[[Prototype]](也就是__proto__)鏈接既琴。

3.生成的新對(duì)象會(huì)綁定到函數(shù)調(diào)用的this占婉。

4.通過(guò)new創(chuàng)建的每個(gè)對(duì)象將最終被[[Prototype]]鏈接到這個(gè)函數(shù)的prototype對(duì)象上。

5.如果函數(shù)沒(méi)有返回對(duì)象類型Object(包含Functoin, Array, Date, RegExg, Error)甫恩,那么new表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新的對(duì)象逆济。

所以相當(dāng)于new調(diào)用時(shí),bind的返回值函數(shù)bound內(nèi)部要模擬實(shí)現(xiàn)new實(shí)現(xiàn)的操作填物。
話不多說(shuō)啄骇,直接上代碼脆粥。

// 第三版 實(shí)現(xiàn)new調(diào)用
Function.prototype.bindFn = function bind(thisArg){
    if(typeof this !== 'function'){
        throw new TypeError(this + ' must be a function');
    }
    // 存儲(chǔ)調(diào)用bind的函數(shù)本身
    var self = this;
    // 去除thisArg的其他參數(shù) 轉(zhuǎn)成數(shù)組
    var args = [].slice.call(arguments, 1);
    var bound = function(){
        // bind返回的函數(shù) 的參數(shù)轉(zhuǎn)成數(shù)組
        var boundArgs = [].slice.call(arguments);
        var finalArgs = args.concat(boundArgs);
        // new 調(diào)用時(shí),其實(shí)this instanceof bound判斷也不是很準(zhǔn)確涵卵。es6 new.target就是解決這一問(wèn)題的莱褒。
        if(this instanceof bound){
            // 這里是實(shí)現(xiàn)上文描述的 new 的第 1, 2, 4 步
            // 1.創(chuàng)建一個(gè)全新的對(duì)象
            // 2.并且執(zhí)行[[Prototype]]鏈接
            // 4.通過(guò)`new`創(chuàng)建的每個(gè)對(duì)象將最終被`[[Prototype]]`鏈接到這個(gè)函數(shù)的`prototype`對(duì)象上击困。
            // self可能是ES6的箭頭函數(shù),沒(méi)有prototype广凸,所以就沒(méi)必要再指向做prototype操作阅茶。
            if(self.prototype){
                // ES5 提供的方案 Object.create()
                // bound.prototype = Object.create(self.prototype);
                // 但 既然是模擬ES5的bind,那瀏覽器也基本沒(méi)有實(shí)現(xiàn)Object.create()
                // 所以采用 MDN ployfill方案 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
                function Empty(){}
                Empty.prototype = self.prototype;
                bound.prototype = new Empty();
            }
            // 這里是實(shí)現(xiàn)上文描述的 new 的第 3 步
            // 3.生成的新對(duì)象會(huì)綁定到函數(shù)調(diào)用的`this`谅海。
            var result = self.apply(this, finalArgs);
            // 這里是實(shí)現(xiàn)上文描述的 new 的第 5 步
            // 5.如果函數(shù)沒(méi)有返回對(duì)象類型`Object`(包含`Functoin`, `Array`, `Date`, `RegExg`, `Error`)脸哀,
            // 那么`new`表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新的對(duì)象。
            var isObject = typeof result === 'object' && result !== null;
            var isFunction = typeof result === 'function';
            if(isObject || isFunction){
                return result;
            }
            return this;
        }
        else{
            // apply修改this指向扭吁,把兩個(gè)函數(shù)的參數(shù)合并傳給self函數(shù)撞蜂,并執(zhí)行self函數(shù)盲镶,返回執(zhí)行結(jié)果
            return self.apply(thisArg, finalArgs);
        }
    };
    return bound;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蝌诡,隨后出現(xiàn)的幾起案子溉贿,更是在濱河造成了極大的恐慌,老刑警劉巖浦旱,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宇色,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡颁湖,警方通過(guò)查閱死者的電腦和手機(jī)宣蠕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)爷狈,“玉大人植影,你說(shuō)我怎么就攤上這事∠延溃” “怎么了思币?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)羡微。 經(jīng)常有香客問(wèn)我谷饿,道長(zhǎng),這世上最難降的妖魔是什么妈倔? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任博投,我火速辦了婚禮,結(jié)果婚禮上盯蝴,老公的妹妹穿的比我還像新娘毅哗。我一直安慰自己,他們只是感情好捧挺,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布虑绵。 她就那樣靜靜地躺著,像睡著了一般闽烙。 火紅的嫁衣襯著肌膚如雪翅睛。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天黑竞,我揣著相機(jī)與錄音捕发,去河邊找鬼。 笑死很魂,一個(gè)胖子當(dāng)著我的面吹牛扎酷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播遏匆,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼法挨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼骤铃!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起坷剧,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤惰爬,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后惫企,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體撕瞧,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年狞尔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了丛版。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡偏序,死狀恐怖页畦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情研儒,我是刑警寧澤豫缨,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站端朵,受9級(jí)特大地震影響好芭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜冲呢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一舍败、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧敬拓,春花似錦邻薯、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至翰意,卻和暖如春木人,著一層夾襖步出監(jiān)牢的瞬間信柿,已是汗流浹背冀偶。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留渔嚷,地道東北人进鸠。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像形病,于是被迫代替她去往敵國(guó)和親客年。 傳聞我的和親對(duì)象是個(gè)殘疾皇子霞幅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容