国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JavaScript進(jìn)階之模擬call,apply和bind

CoderBear / 1871人閱讀

摘要:模擬和模擬一樣,現(xiàn)摘抄下面的代碼添加一個(gè)返回值對(duì)象然后我們定義一個(gè)函數(shù),如果執(zhí)行下面的代碼能夠返回和函數(shù)一樣的值,就達(dá)到我們的目的。

原文:https://zhehuaxuan.github.io/...  
作者:zhehuaxuan
目的

本文主要用于理解和掌握callapplybind的使用和原理,本文適用于對(duì)它們的用法不是很熟悉,或者想搞清楚它們?cè)淼耐?
好,那我們開始!
在JavaScript中有三種方式來改變this的作用域callapplybind。我們先來看看它們是怎么用的,只有知道怎么用的,我們才能來模擬它。

Function.prototype.call()

首先是Function.prototype.call(),不熟的童鞋請(qǐng)猛戳MDN,它是這么說的:call()允許為不同的對(duì)象分配和調(diào)用屬于一個(gè)對(duì)象的函數(shù)/方法。也就是說:一個(gè)函數(shù),只要調(diào)用call()方法,就可以把它分配給不同的對(duì)象。

如果還是不明白,不急!跟我往下看,我們先來寫一個(gè)call()函數(shù)最簡(jiǎn)單的用法:

function source(){
    console.log(this.name); //打印 xuan
}
let destination = {
    name:"xuan"
};
console.log(source.call(destination));

上述代碼會(huì)打印出destinationname屬性,也就是說source()函數(shù)通過調(diào)用call()source()函數(shù)中的this對(duì)象可以分配到destination對(duì)象中。類似于實(shí)現(xiàn)destination.source()的效果,當(dāng)然前提是destination要有一個(gè)source屬性

好,現(xiàn)在大家應(yīng)該明白call()的基本用法,我們?cè)賮砜聪旅娴睦樱?/p>

function source(age,gender){
    console.log(this.name);
    console.log(age);
    console.log(gender);
}
let destination = {
    name:"xuan"
};
console.log(source.call(destination,18,"male"));

打印效果如下:

我們可以看到可以call()也可以傳參,而且是以參數(shù),參數(shù),...的形式傳入。

上述我們知道call()的兩個(gè)作用:

1.改變this的指向

2.支持對(duì)函數(shù)傳參

我們看到最后還還輸出一個(gè)undefined,說明現(xiàn)在調(diào)用source.call(…args)沒有返回值。

我們給source函數(shù)添加一個(gè)返回值試一下:

function source(age,gender){
    console.log(this.name);
    console.log(age);
    console.log(gender);
    //添加一個(gè)返回值對(duì)象
    return {
        age:age,
        gender:gender,
        name:this.name
    }
}
let destination = {
    name:"xuan"
};
console.log(source.call(destination,18,"male"));

打印結(jié)果:

果不其然!call()函數(shù)的返回值就是source函數(shù)的返回值,那么call()函數(shù)的作用已經(jīng)很明顯了。

這邊再總結(jié)一下:

改變this的指向

支持對(duì)函數(shù)傳參

函數(shù)返回什么,call就返回什么。

模擬Function.prototype.call()

根據(jù)call()函數(shù)的作用,我們下面一步一步的進(jìn)行模擬。我們先把上面的部分代碼摘抄下來:

function source(age,gender){
    console.log(this.name);
    console.log(age);
    console.log(gender);
    //添加一個(gè)返回值對(duì)象
    return {
        age:age,
        gender:gender,
        name:this.name
    }
}
let destination = {
    name:"xuan"
};

上面的這部分代碼我們先不變。現(xiàn)在只要實(shí)現(xiàn)一個(gè)函數(shù)call1()并使用下面方式

console.log(source.call1(destination));

如果得出的結(jié)果和call()函數(shù)一樣,那就沒問題了。

現(xiàn)在我們來模擬第一步:改變this的指向

假設(shè)我們destination的結(jié)構(gòu)是這樣的:

let destination = {
    name:"xuan",
    source:function(age,gender){
        console.log(this.name);
        console.log(age);
        console.log(gender);
        //添加一個(gè)返回值對(duì)象
        return {
            age:age,
            gender:gender,
            name:this.name
        }
    }
}

我們執(zhí)行destination.source(18,"male");就可以在source()函數(shù)中把正確的結(jié)果打印出來并且返回我們想要的值。

現(xiàn)在我們的目的更明確了:給destination對(duì)象添加一個(gè)source屬性,然后添加參數(shù)執(zhí)行它

所以我們定義如下:

Function.prototype.call1 = function(ctx){
    ctx.fn = this;   //ctx為destination   this指向source   那么就是destination.fn = source;
    ctx.fn(); // 執(zhí)行函數(shù)
    delete ctx.fn;  //在刪除這個(gè)屬性
}
console.log(source.call1(destination,18,"male"));

打印效果如下:

我們發(fā)現(xiàn)this的指向已經(jīng)改變了,但是我們傳入的參數(shù)還沒有處理。

第二步:支持對(duì)函數(shù)傳參
我們使用ES6語法修改如下:

Function.prototype.call1 =function(ctx,...args){
    ctx.fn = this;
    ctx.fn(...args);
    delete ctx.fn;
}
console.log(source.call1(destination,18,"male"));

打印效果如下:

參數(shù)出現(xiàn)了,現(xiàn)在就剩下返回值了,很簡(jiǎn)單,我們?cè)傩薷囊幌拢?/p>

Function.prototype.call1 =function(ctx,...args){
    ctx.fn = this || window; //防止ctx為null的情況
    let res = ctx.fn(...args);
    delete ctx.fn;
    return res;
}
console.log(source.call1(destination,18,"male"));

打印效果如下:

現(xiàn)在我們實(shí)現(xiàn)了call的效果!

模擬Function.prototype.apply()

apply()函數(shù)的作用和call()函數(shù)一樣,只是傳參的方式不一樣。apply的用法可以查看MDN,MDN這么說的:apply() 方法調(diào)用一個(gè)具有給定this值的函數(shù),以及作為一個(gè)數(shù)組(或類似數(shù)組對(duì)象)提供的參數(shù)。

apply()函數(shù)的第二個(gè)參數(shù)是一個(gè)數(shù)組,數(shù)組是調(diào)用apply()的函數(shù)的參數(shù)。

function source(age,gender){
    console.log(this.name);
    console.log(age);
    console.log(gender);
    return {
        age:age,
        gender:gender,
        name:this.name
    }
}
let destination = {
    name:"xuan"
};
console.log(source.apply(destination,[18,"male"]));

效果和call()是一樣的。既然只是傳參不一樣,我們把模擬call()函數(shù)的代碼稍微改改:

Function.prototype.apply1 =function(ctx,args=[]){
    ctx.fn = this || window;
    let res = ctx.fn(...args);
    delete ctx.fn;
    return res;
}
console.log(source.apply1(destination,[18,"male"]));

執(zhí)行效果如下:

apply()函數(shù)的模擬完成。

Function.prototype.bind()

對(duì)于bind()函數(shù)的作用,我們引用MDN,bind()方法會(huì)創(chuàng)建一個(gè)新函數(shù)。當(dāng)這個(gè)新函數(shù)被調(diào)用時(shí),bind() 的第一個(gè)參數(shù)將作為它運(yùn)行時(shí)的 this對(duì)象,之后的一序列參數(shù)將會(huì)在傳遞的實(shí)參前傳入作為它的參數(shù)。我們看一下代碼:

function source(age,gender){
    console.log(this.name);
    console.log(age);
    console.log(gender);
    return {
        age:age,
        gender:gender,
        name:this.name
    }
}
let destination = {
    name:"xuan"
};
var res = source.bind(destination,18,"male");
console.log(res());
console.log("==========================")
var res1 = source.bind(destination,18);
console.log(res1("male"));
console.log("==========================")
var res2 = source.bind(destination);
console.log(res2(18,"male"));

打印效果如下:

我們發(fā)現(xiàn)bind函數(shù)跟applycall有兩個(gè)區(qū)別:

1.bind返回的是函數(shù),雖然也有call和apply的作用,但是需要在調(diào)用bind()時(shí)生效

2.bind中也可以添加參數(shù)

明白了區(qū)別,下面我們來模擬bind函數(shù)。

模擬Function.prototype.bind()

和模擬call一樣,現(xiàn)摘抄下面的代碼:

function source(age,gender){
    console.log(this.name);
    console.log(age);
    console.log(gender);
    //添加一個(gè)返回值對(duì)象
    return {
        age:age,
        gender:gender,
        name:this.name
    }
}
let destination = {
    name:"xuan"
};

然后我們定義一個(gè)函數(shù)bind1,如果執(zhí)行下面的代碼能夠返回和bind函數(shù)一樣的值,就達(dá)到我們的目的。

var res = source.bind1(destination,18);
console.log(res("male"));

首先我們定義一個(gè)bind1函數(shù),因?yàn)榉祷刂凳且粋€(gè)函數(shù),所以我們可以這么寫:

Function.prototype.bind1 = function(ctx,...args){
    var that = this;//外層的this指向通過變量傳進(jìn)去
    return function(){
        //將外層函數(shù)的參數(shù)和內(nèi)層函數(shù)的參數(shù)合并
        var all_args = [...args].concat([...arguments]);
        //因?yàn)閏tx是外層的this指針,在外層我們使用一個(gè)變量that引用進(jìn)來
        return that.apply(ctx,all_args);
    }
}

打印效果如下:

這里我們利用閉包,把外層函數(shù)的ctx和參數(shù)args傳到內(nèi)層函數(shù),再將內(nèi)外傳遞的參數(shù)合并,然后使用apply()call()函數(shù),將其返回。

當(dāng)我們調(diào)用res("male")時(shí),因?yàn)橥鈱?b>ctx和args還是會(huì)存在內(nèi)存當(dāng)中,所以調(diào)用時(shí),前面的ctx也就是sourceargs也就是18,再將傳入的"male"跟18合并[18,"male"],執(zhí)行source.apply(destination,[18,"male"]);返回函數(shù)結(jié)果即可。bind()的模擬完成!

但是bind除了上述用法,還可以有如下用法:

function source(age,gender){
    console.log(this.name);
    console.log(age);
    console.log(gender);
    //添加一個(gè)返回值對(duì)象
    return {
        age:age,
        gender:gender,
        name:this.name
    }
}
let destination = {
    name:"xuan"
};
var res = source.bind1(destination,18);
var person = new res("male");
console.log(person);

打印效果如下:


我們發(fā)現(xiàn)bind函數(shù)支持new關(guān)鍵字,調(diào)用的時(shí)候this的綁定失效了,那么new之后,this指向哪里呢?我們來試一下,代碼如下:

function source(age,gender){
  console.log(this);
}
let destination = {
    name:"xuan"
};
var res = source.bind(destination,18);
console.log(new res("male"));
console.log(res("male"));

執(zhí)行new的時(shí)候,我們發(fā)現(xiàn)雖然bind的第一個(gè)參數(shù)是destination,但是this是指向source的。

不用new的話,this指向destination

好,現(xiàn)在再來回顧一下我們的bind1實(shí)現(xiàn):

Function.prototype.bind1 = function(ctx,...args){
    var that = this;
    return function(){
        //將外層函數(shù)的參數(shù)和內(nèi)層函數(shù)的參數(shù)合并
        var all_args = [...args].concat([...arguments]);
        //因?yàn)閏tx是外層的this指針,在外層我們使用一個(gè)變量that引用進(jìn)來
        return that.apply(ctx,all_args);
    }
}

如果我們使用:

var res = source.bind(destination,18);
console.log(new res("male"));

如果執(zhí)行上述代碼,我們的ctx還是destination,也就是說這個(gè)時(shí)候下面的source函數(shù)中的ctx還是指向destination。而根據(jù)Function.prototype.bind的用法,這時(shí)this應(yīng)該是指向source自身。

我們先把部分代碼抄下來:

function source(age,gender){
    console.log(this.name);
    console.log(age);
    console.log(gender);
    //添加一個(gè)返回值對(duì)象
    return {
        age:age,
        gender:gender,
        name:this.name
    }
}
let destination = {
    name:"xuan"
};

我們改一下bind1函數(shù):

Function.prototype.bind1 = function (ctx, ...args) {
    var that = this;//that肯定是source
    //定義了一個(gè)函數(shù)
    let f = function () {
        //將外層函數(shù)的參數(shù)和內(nèi)層函數(shù)的參數(shù)合并
        var all_args = [...args].concat([...arguments]);
        //因?yàn)閏tx是外層的this指針,在外層我們使用一個(gè)變量that引用進(jìn)來
        var real_ctx = this instanceof f ? this : ctx;
        return that.apply(real_ctx, all_args);
    }
    //函數(shù)的原型指向source的原型,這樣執(zhí)行new f()的時(shí)候this就會(huì)通過原型鏈指向source
    f.prototype = this.prototype;
    //返回函數(shù)
    return f;
}

我們執(zhí)行

var res = source.bind1(destination,18);
console.log(new res("male"));

效果如下:

已經(jīng)達(dá)到我們的效果!

現(xiàn)在分析一下上述實(shí)現(xiàn)的代碼:

//調(diào)用var res = source.bind1(destination,18)時(shí)的代碼分析
Function.prototype.bind1 = function (ctx, ...args) {
    var that = this;//that肯定是source
    //定義了一個(gè)函數(shù)
    let f = function () {
       ... //內(nèi)部先不管
    }
    //函數(shù)的原型指向source的原型,這樣執(zhí)行new f()的時(shí)候this就會(huì)指向一個(gè)新家的對(duì)象,這個(gè)對(duì)象通過原型鏈指向source,這正是我們上面執(zhí)行apply的時(shí)候需要傳入的參數(shù)
     //f.prototype==>source.prototype
    f.prototype = this.prototype;
    //返回函數(shù)
    return f;
}

f()函數(shù)的內(nèi)部實(shí)現(xiàn)分析:

//new res("male")相當(dāng)于運(yùn)行new f("male");下面進(jìn)行函數(shù)的運(yùn)行態(tài)分析
let f = function () {
     console.log(this);//這個(gè)時(shí)候打印this就是一個(gè)_proto_指向f.prototype的對(duì)象,因?yàn)閒.prototype==>source.prototype,所以this._proto_==>source.prototype
     //將外層函數(shù)的參數(shù)和內(nèi)層函數(shù)的參數(shù)合并
     var all_args = [...args].concat([...arguments]);
     //正常不用new的時(shí)候this指向當(dāng)前調(diào)用處的this指針(在全局環(huán)境中執(zhí)行,this就是window對(duì)象);使用new的話這個(gè)this對(duì)象的原型鏈上有一個(gè)類型是f的原型對(duì)象。
    //那么判斷一下,如果this instanceof f,那么real_ctx=this,否則real_ctx=ctx;
     var real_ctx = this instanceof f ? this : ctx;
    //現(xiàn)在把真正分配給source函數(shù)的對(duì)象傳入
     return that.apply(real_ctx, all_args);
}

至此bind()函數(shù)的模擬實(shí)現(xiàn)完畢!如有不對(duì)之處,歡迎拍磚!您的寶貴意見是我寫作的動(dòng)力,謝謝大家。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/102537.html

相關(guān)文章

  • 進(jìn)階3-3期】深度解析 call apply 原理、使用場(chǎng)景及實(shí)現(xiàn)

    摘要:之前文章詳細(xì)介紹了的使用,不了解的查看進(jìn)階期。不同的引擎有不同的限制,核心限制在,有些引擎會(huì)拋出異常,有些不拋出異常但丟失多余參數(shù)。存儲(chǔ)的對(duì)象能動(dòng)態(tài)增多和減少,并且可以存儲(chǔ)任何值。這邊采用方法來實(shí)現(xiàn),拼成一個(gè)函數(shù)。 之前文章詳細(xì)介紹了 this 的使用,不了解的查看【進(jìn)階3-1期】。 call() 和 apply() call() 方法調(diào)用一個(gè)函數(shù), 其具有一個(gè)指定的 this 值和分...

    godlong_X 評(píng)論0 收藏0
  • 進(jìn)階3-4期】深度解析bind原理、使用場(chǎng)景及模擬實(shí)現(xiàn)

    摘要:返回的綁定函數(shù)也能使用操作符創(chuàng)建對(duì)象這種行為就像把原函數(shù)當(dāng)成構(gòu)造器,提供的值被忽略,同時(shí)調(diào)用時(shí)的參數(shù)被提供給模擬函數(shù)。 bind() bind() 方法會(huì)創(chuàng)建一個(gè)新函數(shù),當(dāng)這個(gè)新函數(shù)被調(diào)用時(shí),它的 this 值是傳遞給 bind() 的第一個(gè)參數(shù),傳入bind方法的第二個(gè)以及以后的參數(shù)加上綁定函數(shù)運(yùn)行時(shí)本身的參數(shù)按照順序作為原函數(shù)的參數(shù)來調(diào)用原函數(shù)。bind返回的綁定函數(shù)也能使用 n...

    guyan0319 評(píng)論0 收藏0
  • javasscript - 收藏集 - 掘金

    摘要:跨域請(qǐng)求詳解從繁至簡(jiǎn)前端掘金什么是為什么要用是的一種使用模式,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問的問題。異步編程入門道典型的面試題前端掘金在界中,開發(fā)人員的需求量一直居高不下。 jsonp 跨域請(qǐng)求詳解——從繁至簡(jiǎn) - 前端 - 掘金什么是jsonp?為什么要用jsonp?JSONP(JSON with Padding)是JSON的一種使用模式,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問的問題...

    Rango 評(píng)論0 收藏0
  • 進(jìn)階 6-2 期】深入高階函數(shù)應(yīng)用柯里化

    摘要:引言上一節(jié)介紹了高階函數(shù)的定義,并結(jié)合實(shí)例說明了使用高階函數(shù)和不使用高階函數(shù)的情況。我們期望函數(shù)輸出,但是實(shí)際上調(diào)用柯里化函數(shù)時(shí),所以調(diào)用時(shí)就已經(jīng)執(zhí)行并輸出了,而不是理想中的返回閉包函數(shù),所以后續(xù)調(diào)用將會(huì)報(bào)錯(cuò)。引言 上一節(jié)介紹了高階函數(shù)的定義,并結(jié)合實(shí)例說明了使用高階函數(shù)和不使用高階函數(shù)的情況。后面幾部分將結(jié)合實(shí)際應(yīng)用場(chǎng)景介紹高階函數(shù)的應(yīng)用,本節(jié)先來聊聊函數(shù)柯里化,通過介紹其定義、比較常見的...

    stackvoid 評(píng)論0 收藏0
  • JavaScript深入bind模擬實(shí)現(xiàn)

    摘要:也就是說當(dāng)返回的函數(shù)作為構(gòu)造函數(shù)的時(shí)候,時(shí)指定的值會(huì)失效,但傳入的參數(shù)依然生效。構(gòu)造函數(shù)效果的優(yōu)化實(shí)現(xiàn)但是在這個(gè)寫法中,我們直接將,我們直接修改的時(shí)候,也會(huì)直接修改函數(shù)的。 JavaScript深入系列第十一篇,通過bind函數(shù)的模擬實(shí)現(xiàn),帶大家真正了解bind的特性 bind 一句話介紹 bind: bind() 方法會(huì)創(chuàng)建一個(gè)新函數(shù)。當(dāng)這個(gè)新函數(shù)被調(diào)用時(shí),bind() 的第一個(gè)參數(shù)...

    FingerLiu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

CoderBear

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<