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

資訊專欄INFORMATION COLUMN

javascript原生一步步實(shí)現(xiàn)bind分析

coordinate35 / 686人閱讀

摘要:綁定函數(shù)被調(diào)用時(shí),也接受預(yù)設(shè)的參數(shù)提供給原函數(shù)。原型鏈官方文檔上有一句話說明綁定過后的函數(shù)被實(shí)例化之后,需要繼承原函數(shù)的原型鏈方法,且綁定過程中提供的被忽略繼承原函數(shù)的對(duì)象,但是參數(shù)還是會(huì)使用。

bind 官方描述

bind() 函數(shù)會(huì)創(chuàng)建一個(gè)新函數(shù)(稱為綁定函數(shù)),新函數(shù)與被調(diào)函數(shù)(綁定函數(shù)的目標(biāo)函數(shù))具有相同的函數(shù)體(在 ECMAScript 5 規(guī)范中內(nèi)置的call屬性)。當(dāng)目標(biāo)函數(shù)被調(diào)用時(shí) this 值綁定到 bind() 的第一個(gè)參數(shù),該參數(shù)不能被重寫。綁定函數(shù)被調(diào)用時(shí),bind() 也接受預(yù)設(shè)的參數(shù)提供給原函數(shù)。一個(gè)綁定函數(shù)也能使用new操作符創(chuàng)建對(duì)象:這種行為就像把原函數(shù)當(dāng)成構(gòu)造器。提供的 this 值被忽略,同時(shí)調(diào)用時(shí)的參數(shù)被提供給模擬函數(shù)。

使用介紹

由于javascript中作用域是由其運(yùn)行時(shí)候所處的環(huán)境決定的,所以往往函數(shù)定義和實(shí)際運(yùn)行的時(shí)候所處環(huán)境不一樣,那么作用域也會(huì)發(fā)生相應(yīng)的變化。
例如下面這個(gè)情況:

var id = "window";
//定義一個(gè)函數(shù),但是不立即執(zhí)行
var test = function(){
    console.log(this.id)
}
test() // window
//把test作為參數(shù)傳遞
var obj = {
    id:"obj",
    hehe:test
}
//此時(shí)test函數(shù)運(yùn)行環(huán)境發(fā)生了改變
obj.hehe() // "obj"
//為了避免這種情況,javascript里面有一個(gè)bind方法可以在函數(shù)運(yùn)行之前就綁定其作用域,修改如下

var id = "window";
var test = function(){
    console.log(this.id)
}.bind(window)
var obj = {
    id:"obj",
    hehe:test
}
test() // window
obj.hehe() // window

上面介紹了bind方法的一個(gè)重要作用就是為一個(gè)函數(shù)綁定作用域,但是bind方法在低版本瀏覽器不兼容,這里我們可以手動(dòng)實(shí)現(xiàn)一下。

拆分一下關(guān)鍵思路

因?yàn)閎ind方法不會(huì)立即執(zhí)行函數(shù),需要返回一個(gè)待執(zhí)行的函數(shù)(這里用到閉包,可以返回一個(gè)函數(shù))return function(){}

作用域綁定,這里可以使用apply或者call方法來實(shí)現(xiàn) xx.call(yy)/xx.apply(yy)

參數(shù)傳遞,由于參數(shù)的不確定性,需要用apply傳遞數(shù)組(實(shí)例更明了xx.apply(yy,[...Array...]),如果用call就不太方便了,因?yàn)閏all后面的參數(shù)需要一個(gè)個(gè)列出來

實(shí)現(xiàn)

有了上述的思路,大致的雛形已經(jīng)明了了,代碼應(yīng)該也很容易實(shí)現(xiàn)

綁定作用域,綁定傳參
Function.prototype.testBind = function(that){
    var _this = this,
        /*
        *由于參數(shù)的不確定性,統(tǒng)一用arguments來處理,這里的arguments只是一個(gè)類數(shù)組對(duì)象,有l(wèi)ength屬性
        *可以用數(shù)組的slice方法轉(zhuǎn)化成標(biāo)準(zhǔn)格式數(shù)組,除了作用域?qū)ο髏hat以外,
        *后面的所有參數(shù)都需要作為數(shù)組參數(shù)傳遞
        *Array.prototype.slice.apply(arguments,[1])/Array.prototype.slice.call(arguments,1)
        */
        slice = Array.prototype.slice,
        args = slice.apply(arguments,[1]);
    //返回函數(shù)    
    return function(){
        //apply綁定作用域,進(jìn)行參數(shù)傳遞
        return _this.apply(that,args)
    }    
}

測(cè)試

var test = function(a,b){
    console.log("作用域綁定 "+ this.value)
    console.log("testBind參數(shù)傳遞 "+ a.value2)
    console.log("調(diào)用參數(shù)傳遞 " + b)
}
var obj = {
    value:"ok"
}
var fun_new = test.testBind(obj,{value2:"also ok"})

fun_new ("hello bind")
// 作用域綁定 ok
// testBind參數(shù)傳遞 also ok
// 調(diào)用參數(shù)傳遞  undefined
動(dòng)態(tài)參數(shù)

上面已經(jīng)實(shí)現(xiàn)了bind方法的作用域綁定,但是美中不足的是,既然我們返回的是一個(gè)函數(shù),調(diào)用的時(shí)候應(yīng)該支持傳遞參數(shù),很顯然,上面的 fun_new 調(diào)用的時(shí)候并不支持傳參,只能在 testBind 綁定的時(shí)候傳遞參數(shù),因?yàn)槲覀冏罱K調(diào)用的是這個(gè)返回函數(shù)

function(){
        return _this.apply(that,args)
    }    

這里面的args在綁定的時(shí)候就已經(jīng)確定了,調(diào)用的時(shí)候值已經(jīng)固定,
我們并沒有處理這個(gè)function傳遞的參數(shù)。

我們對(duì)其進(jìn)行改造

return function(){
        return _this.apply(that,
            args.concat(Array.prototype.slice.apply(arguments,[0]))
        )
    }    

這里的 Array.prototype.slice.apply(arguments,[0]) 指的是這個(gè)返回函數(shù)執(zhí)行的時(shí)候傳遞的一系列參數(shù),所以是從第一個(gè)參數(shù)開始 [0] ,之前的args = slice.apply(arguments,[1])指的是 testBind方法執(zhí)行時(shí)候傳遞的參數(shù),所以從第二個(gè)開始 [1],兩則有本質(zhì)區(qū)別,不能搞混,只有兩者合并了之后才是返回函數(shù)的完整參數(shù)

所以有如下實(shí)現(xiàn)

Function.prototype.testBind = function(that){
    var _this = this,
        slice = Array.prototype.slice,
        args = slice.apply(arguments,[1]);
    return function(){
        return _this.apply(that,
                    args.concat(Array.prototype.slice.apply(arguments,[0]))
                )
    }    
}

測(cè)試

var test = function(a,b){
    console.log("作用域綁定 "+ this.value)
    console.log("testBind參數(shù)傳遞 "+ a.value2)
    console.log("調(diào)用參數(shù)傳遞 " + b)
}
var obj = {
    value:"ok"
}
var fun_new = test.testBind(obj,{value2:"also ok"})

fun_new ("hello bind")
// 作用域綁定 ok
// testBind參數(shù)傳遞 also ok
// 調(diào)用參數(shù)傳遞  hello bind

在以上2種傳參方式中,bind的優(yōu)先級(jí)高,從 args.concat(Array.prototype.slice.apply(arguments,[0])) 也可以看出來,bind的參數(shù)在數(shù)組前面。

原型鏈

官方文檔上有一句話:

A bound function may also be constructed using the new operator: doing
so acts as though the target function had instead been constructed.
The provided this value is ignored, while prepended arguments are
provided to the emulated function.

說明綁定過后的函數(shù)被new實(shí)例化之后,需要繼承原函數(shù)的原型鏈方法,且綁定過程中提供的this被忽略(繼承原函數(shù)的this對(duì)象),但是參數(shù)還是會(huì)使用。
這里就需要一個(gè)中轉(zhuǎn)函數(shù)把原型鏈傳遞下去

fNOP = function () {} //創(chuàng)建一個(gè)中轉(zhuǎn)函數(shù)
fNOP.prototype = this.prototype;
xx.prototype = new fNOP() 
修改如下
Function.prototype.testBind = function(that){
    var _this = this,
        slice = Array.prototype.slice,
        args = slice.apply(arguments,[1]),
        fNOP = function () {},
        //所以調(diào)用官方bind方法之后 有一個(gè)name屬性值為 "bound "
        bound = function(){
            return _this.apply(that,
                args.concat(Array.prototype.slice.apply(arguments,[0]))
            )
        }    

    fNOP.prototype = _this.prototype;

    bound.prototype = new fNOP();

    return bound;
}

而且bind方法的第一個(gè)參數(shù)this是可以不傳的,需要分2種情況

直接調(diào)用bind之后的方法

var f = function () { console.log("不傳默認(rèn)為"+this)  };f.bind()()
// 不傳默認(rèn)為 Window 

所以直接調(diào)用綁定方法時(shí)候 apply(that, 建議改為 apply(that||window,,其實(shí)不改也可以,因?yàn)椴粋髂J(rèn)指向window

使用new實(shí)例化被綁定的方法

容易糊涂,重點(diǎn)在于弄清楚標(biāo)準(zhǔn)的bind方法在new的時(shí)候做的事情,然后就可以清晰的實(shí)現(xiàn)

這里我們需要看看 new 這個(gè)方法做了哪些操作 比如說 var a = new b()

創(chuàng)建一個(gè)空對(duì)象 a = {},并且this變量引用指向到這個(gè)空對(duì)象a

繼承被實(shí)例化函數(shù)的原型 :a.__proto__ = b.prototype

被實(shí)例化方法bthis對(duì)象的屬性和方法將被加入到這個(gè)新的 this 引用的對(duì)象中: b的屬性和方法被加入的 a里面

新創(chuàng)建的對(duì)象由 this 所引用 :b.call(a)

通過以上可以得知,如果是var after_new = new bindFun(); 由于這種行為是把原函數(shù)當(dāng)成構(gòu)造器,那么那么最終實(shí)例化之后的對(duì)象 this需要繼承自原函數(shù), 而這里的 bindFun 目前是

function(){
            return _this.apply(that || window,
                args.concat(Array.prototype.slice.apply(arguments,[0]))
            )
        }    

這里apply的作用域是綁定的that || window,在執(zhí)行 testBind()的時(shí)候就已經(jīng)固定,并沒有把原函數(shù)的this對(duì)象繼承過來,不符合我們的要求,我們需要根據(jù)apply的特性解決這個(gè)問題:

在一個(gè)子構(gòu)造函數(shù)中,你可以通過調(diào)用父構(gòu)造函數(shù)的 `apply/call` 方法來實(shí)現(xiàn)繼承

例如

function Product(name, price) {
  this.name = name;
  this.price = price;

  if (price < 0) {
    throw RangeError("Cannot create product " +
                      this.name + " with a negative price");
  }
}

function Food(name, price) {
  Product.call(this, name, price); 
  this.category = "food";
}

//等同于(其實(shí)就是把Product放在Food內(nèi)部執(zhí)行了一次)
function Food(name, price) { 
    this.name = name;
    this.price = price;
    if (price < 0) {
        throw RangeError("Cannot create product " +
                this.name + " with a negative price");
    }

    this.category = "food"; 
}

所以在new新的實(shí)例的時(shí)候?qū)崟r(shí)將這個(gè)新的this對(duì)象 進(jìn)行 apply 繼承原函數(shù)的 this 對(duì)象,就可以達(dá)到 new 方法里面的第 3 步的結(jié)果

apply(that||window,
//修改為 如果是new的情況,需要綁定new之后的作用域,this指向新的實(shí)例對(duì)象
apply(isNew ? this : that||window,  ==>

Function.prototype.testBind = function(that){
    var _this = this,
        slice = Array.prototype.slice,
        args = slice.apply(arguments,[1]),
        fNOP = function () {},
        //所以調(diào)用官方bind方法之后 有一個(gè)name屬性值為 "bound "
        bound = function(){
            return _this.apply(isNew ? this : that||window,
                args.concat(Array.prototype.slice.apply(arguments,[0]))
            )
        }    

    fNOP.prototype = _this.prototype;

    bound.prototype = new fNOP();

    return bound;
}

這里的 isNew 是區(qū)分 bindFun 是直接調(diào)用還是被 new 之后再調(diào)用,通過原型鏈的繼承關(guān)系可以知道,
bindFun 屬于 after_new的父類,所以 after_new instanceof bindFun 為 true,同時(shí)
bindFun.prototype = new fNOP() 原型繼承; 所以 fNOP 也是 after_new的父類, after_new instanceof fNOP 為 true

最終結(jié)果
Function.prototype.testBind = function(that){
        var _this = this,
            slice = Array.prototype.slice,
            args = slice.apply(arguments,[1]),
            fNOP = function () {},
            bound = function(){
                //這里的this指的是調(diào)用時(shí)候的環(huán)境
                return _this.apply(this instanceof  fNOP ? this : that||window,
                    args.concat(Array.prototype.slice.apply(arguments,[0]))
                )
            }    
        fNOP.prototype = _this.prototype;
    
        bound.prototype = new fNOP();
    
        return bound;
    }

我看到有些地方寫的是

this instanceof fNOP && that ? this : that || window,

我個(gè)人覺得這里有點(diǎn)不正確,如果綁定時(shí)候不傳參數(shù),那么that就為空,那無論怎樣就只能綁定 window作用域了。

以上是個(gè)人見解,不對(duì)的地方望指導(dǎo),謝謝!

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

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

相關(guān)文章

  • 從源碼步步學(xué)習(xí),Ryan Dahl的Deno實(shí)現(xiàn)原理

    摘要:之父在中的設(shè)計(jì)錯(cuò)誤演講中表示不允許將任意本地函數(shù)綁定至當(dāng)中。所有系統(tǒng)調(diào)用都將通過消息傳遞完成序列化。兩項(xiàng)原生函數(shù)與。這既簡(jiǎn)化了設(shè)計(jì)流程,又使得系統(tǒng)更易于審計(jì)。 Node之父ry:在Node中的設(shè)計(jì)錯(cuò)誤演講中表示: 不允許將任意本地函數(shù)綁定至 V8 當(dāng)中。 所有系統(tǒng)調(diào)用都將通過消息傳遞完成(protobuf 序列化)。 兩項(xiàng)原生函數(shù):send 與 recv。 這既簡(jiǎn)化了設(shè)計(jì)流程,又使得...

    goji 評(píng)論0 收藏0
  • JS原生步步實(shí)現(xiàn)前端路由和單頁面應(yīng)用

    摘要:這里借鑒了一下的處理方式,我們把單獨(dú)模塊的包裝成一個(gè)函數(shù),提供一個(gè)全局的回調(diào)方法,加載完成時(shí)候再調(diào)用回調(diào)函數(shù)。 前端路由實(shí)現(xiàn)之 #hash 先上github項(xiàng)目地址: spa-routers運(yùn)行效果圖showImg(https://segmentfault.com/img/bVFi7l?w=581&h=312); 背景介紹 用了許多前端框架來做spa應(yīng)用,比如說backbone,ang...

    idealcn 評(píng)論0 收藏0
  • 學(xué)習(xí) apply 和 call

    摘要:官方描述方法在指定值和參數(shù)參數(shù)以數(shù)組或類數(shù)組對(duì)象的形式存在的情況下調(diào)用某個(gè)函數(shù)。兩者基本一致,只有一個(gè)區(qū)別,就是方法接受的是若干個(gè)參數(shù)的列表,而方法接受的是一個(gè)包含多個(gè)參數(shù)的數(shù)組。 Function.prototype.apply() & Function.prototype.call() 官方描述 apply() 方法在指定 this 值和參數(shù)(參數(shù)以數(shù)組或類數(shù)組對(duì)象的形式存在)的...

    gclove 評(píng)論0 收藏0
  • javascript知識(shí)點(diǎn)

    摘要:模塊化是隨著前端技術(shù)的發(fā)展,前端代碼爆炸式增長(zhǎng)后,工程化所采取的必然措施。目前模塊化的思想分為和。特別指出,事件不等同于異步,回調(diào)也不等同于異步。將會(huì)討論安全的類型檢測(cè)惰性載入函數(shù)凍結(jié)對(duì)象定時(shí)器等話題。 Vue.js 前后端同構(gòu)方案之準(zhǔn)備篇——代碼優(yōu)化 目前 Vue.js 的火爆不亞于當(dāng)初的 React,本人對(duì)寫代碼有潔癖,代碼也是藝術(shù)。此篇是準(zhǔn)備篇,工欲善其事,必先利其器。我們先在代...

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

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

0條評(píng)論

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