apply以及call實(shí)現(xiàn)bind的過程

理解

  1. call
    改變函數(shù)的this指向,且為執(zhí)行時(shí)的this指向,第二個(gè)參數(shù)逐次傳入即可,在綁定之后立即執(zhí)行
  2. apply
    改變函數(shù)的this指向,且為執(zhí)行時(shí)的this指向,第二個(gè)參數(shù)需要以數(shù)組的形式傳入即可,在綁定之后立即執(zhí)行
  3. bind
    改變函數(shù)的this指向,但是同時(shí)生成一個(gè)綁定函數(shù),綁定函數(shù)作為返回值返回,綁定之后并不立即執(zhí)行,可以給返回的綁定函數(shù)繼續(xù)傳入?yún)?shù),并且bind的第二個(gè)參數(shù)傳入的格式類似于call。

嘗試

bind的特點(diǎn)

  1. 返回一個(gè)函數(shù)
  2. 可以繼續(xù)給返回的函數(shù)傳入?yún)?shù)

嘗試

version1

//考慮到返回的是一個(gè)函數(shù)

Function.prototype.bind = function (fn) {
    return function () {      
          return this.call(fn);
    };  
};

var foo = function () {
    console.log(this.color);
};

var bar = {
    color: 'red'
};

var func = foo.bind(bar);
func();

執(zhí)行結(jié)果

image.png

version2

//上一步中犯了一個(gè)錯(cuò)誤導(dǎo)致執(zhí)行出錯(cuò),關(guān)于this,需要在返回函數(shù)之前做this的緩存
Function.prototype.bind = function (fn) {
    var _this = this;
    return function () {
        return _this.apply(fn);
    };
};


var foo = function () {
    console.log(this.color);
};

var bar = {
    color: 'red'
};

var func = foo.bind(bar);
func();

執(zhí)行結(jié)果

image.png

version3

//版本2可以正確執(zhí)行,但是不能傳入?yún)?shù)

Function.prototype.bind = function (fn) {
    var _this = this,
        args  = Array.prototype.slice.call(arguments, 1);//截取第一個(gè)參數(shù)以后的所有參數(shù),且將它們轉(zhuǎn)為真正的數(shù)組
    return function () {
        return _this.apply(fn,args);
    };
};


var foo = function () {
    console.log(this.color);
    console.log(arguments);
};

var bar = {
    color: 'red'
};

var func = foo.bind(bar,1,2,3,4);
func();

執(zhí)行結(jié)果

image.png

version4

Function.prototype.bind = function (fn) {
    var _this = this,
        args  = Array.prototype.slice.call(arguments, 1);
    return function () {
        var finalArgs = args.concat(Array.prototype.slice.call(arguments));//保證在返回的參數(shù)里還能傳入?yún)?shù)
        return _this.apply(fn,finalArgs);
    };
};


var foo = function () {
    console.log(this.color);
    console.log(arguments);
};

var bar = {
    color: 'red'
};

var func = foo.bind(bar,1,2,3,4);
func(11,12,13,14);

延伸參考

以上是我自己寫出來的版本,但是在寫完之后去查閱了一些資料,進(jìn)行了進(jìn)一步的理解和學(xué)習(xí)

當(dāng)把bind綁定之后的函數(shù)作為構(gòu)造函數(shù)返回,則會(huì)產(chǎn)生一些和當(dāng)前編寫的版本4有差異的地方,如下。
bind

var value = 2;

var foo = {
    value: 1
};

function bar(name, age) {
    this.habit = 'shopping';
    console.log(this.value);
    console.log(name);
    console.log(age);
}

bar.prototype.friend = 'kevin';

var bindFoo = bar.bind(foo, 'daisy');

var obj = new bindFoo('18');
// undefined
// daisy
// 18
console.log(obj.habit);
console.log(obj.friend);
// shopping
// kevin

執(zhí)行結(jié)果

image.png

版本4加入后


//定義bind
Function.prototype.bind = function (fn) {
    var _this = this,
        args  = Array.prototype.slice.call(arguments, 1);//截取第一個(gè)參數(shù)以后的所有參數(shù),且將它們轉(zhuǎn)為真正的數(shù)組
    return function () {
        var finalArgs = args.concat(Array.prototype.slice.call(arguments));
        return _this.apply(fn,finalArgs);
    };
};
var value = 2;

var foo = {
    value: 1
};

function bar(name, age) {
    this.habit = 'shopping';
    console.log(this.value);
    console.log(name);
    console.log(age);
}

bar.prototype.friend = 'kevin';

var bindFoo = bar.bind(foo, 'daisy');

var obj = new bindFoo('18');
// undefined
// daisy
// 18
console.log(obj.habit);
console.log(obj.friend);
// shopping
// kevin

執(zhí)行結(jié)果

image.png

分析:首先需要明確的是new一個(gè)對(duì)象的過程,先創(chuàng)建一個(gè)空對(duì)象,繼承構(gòu)造函數(shù)的原型,然后將this指向這個(gè)新對(duì)象,然后將該對(duì)象賦給當(dāng)前的變量即可。那么首先就要明確兩點(diǎn):1.原型繼承 2.this的指向

首先,修改this

Function.prototype.bind = function (fn) {
    var _this = this,
        args = Array.prototype.slice.call(arguments,1);
    var boundFn = function () {
        //判斷this
        var finalArgs = args.concat(Array.prototype.slice.call(arguments));
        return _this.apply(this instanceof boundFn ? this : fn, finalArgs);//在判斷之后替換掉原來的this
    };
    boundFn.prototype = this.prototype;//原型繼承
    return boundFn;
};

執(zhí)行結(jié)果

image.png

結(jié)論:
可以通過判斷最終的返回函數(shù)的this的真正指向,去判斷真正的fn是哪一個(gè)函數(shù),究竟是原來的bar還是后來通過構(gòu)造函數(shù)實(shí)例化修改了this的obj。這一點(diǎn)對(duì)于最終的value判斷很重要,因?yàn)閷?shí)例化之后的obj沒有value,但是obj通過原型繼承可以訪問到bar的原型中的friend。
參考材料

總結(jié)

  • 重新復(fù)習(xí)了this相關(guān)的知識(shí)點(diǎn),以及鞏固了對(duì)于apply和call的使用
  • 計(jì)劃下一次整理關(guān)于原型鏈的知識(shí)點(diǎn),同時(shí)回顧原型繼承
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容