模擬js中的call、apply和bind的實現

1、call和apply的特色

  • 能夠改變咱們當前函數的this指向
  • 還會讓當前函數執行

var person = {
    value : 1
}
function bar() {
    console.log(this.value)
}
// 若是不對this進行綁定執行bar() 會返回undefined,this指向window
bar.call(person) // 1複製代碼

試想一下當調用call的時候也就相似於數組

var person = {    value: 1,
    bar: function() {
        console.log(this.value)
    }
}
person.bar() // 1複製代碼

這樣就把 this 指向到了 person上,可是這樣給 person 對象加了一個屬性,不太合適,不過沒關係,執行完刪除這個屬性就能夠實現了。
bash

也就是說步驟實際上是這樣的app

  1. 將函數設爲對象的屬性
  2. 執行這個函數
  3. 刪除這個函數

一、call模擬實現

Function.prototype.call = function(context){
    context = context ? Object(context) : window;//不傳遞context默認爲window
    context.fn = this;//this也就是調用call的函數
    let args = [...arguments].slice(1);
    let r = context.fn(...args);
    delete context.fn;
    return r;
}複製代碼

二、apply模擬實現

apply的方法和 call 方法的實現相似,只不過是若是有參數,以數組形式進行傳。
函數

Function.prototype.apply= function(context,args){
    context = context ? Object(context) : window;//不傳遞context默認爲window
    context.fn = this;
    if(!args){
        return context.fn();
    }
    let r = context.fn(...args);
    delete context.fn;
    return r;
}複製代碼

2、bind的特色

  • bind方法能夠綁定this指向  綁定參數
  • bind方法返回一個綁定後的函數(高階函數)
  • 調用綁定後的方法,會讓原方法執行
  • 若是綁定的函數被new了,當前函數的this就是當前的實例
  • new出來的結果,能夠找到原有類的原型

一、bind方法模擬實現第一步

用法:ui

let obj = {
    name:'gjf'
}
function fn(){
    console.log(this.name)
}
let bindFn = fn.bind(obj); //返因一個綁定後的方法
findFn() //用綁定後的方法,讓原方法執行複製代碼

實現:this

Function.prototype.bind = function(context){
    let that = this;
    return  function(){
        return that.apply(context);    
    }
}複製代碼

這樣實現了最簡單的改變this指向的bind,可是這樣還遠遠不夠,由於bind還能夠綁定參數;spa

二、bind方法模擬實現第二步

方法傳參能夠分兩批傳,一批能夠先在bind方法裏面先綁定好,另外一批在調用的時候傳參,例如如下示例;prototype

用法:code

let obj = {
    name:'gjf'
}
function fn(name,age){
    console.log(this.name+'養了一隻'+name+age+'歲了')
}
let bindFn = fn.bind(obj,'貓'); //返因一個綁定後的方法
findFn(8) //用綁定後的方法,讓原方法執行複製代碼

實現:對象

Function.prototype.bind = function(context){
    let that = this;
    let bindArgs = Array.prototype.slice.call(argument,1)//['貓']
    return  function(){
        let args = Array.prototype.slice.call(argument);
        return that.apply(context,bindArgs.concat(args));    
    }
}複製代碼

三、bind方法模擬實現第三步

調用bind返回的函數除了能夠直接調用,還能夠把函數當成一個類來調用;原函數上綁定了屬性,new出來的實例上可否訪問。

用法:

fn.prototype.flag = '哺乳類'; //原函數上綁定了屬性
let findFn = fn.bind(obj,'貓');
let instance = new findFn(8);//若是綁定的函數被new了,當前函數的this就是當前的實例
console.log(instance.flag) //undefined複製代碼

實現:

Function.prototype.bind = function(context){
    let that = this;
    let bindArgs = Array.prototype.slice.call(argument,1)//['貓']
    function Fn(){} //Object.create的原理
    function fBound(){
        let args = Array.prototype.slice.call(argument);
        return that.apply(this instanceof fBound ? this:context,bindArgs.concat(args));    
    }
    Fn.prototype = this.prototype;
    fBound.prototype = new Fn();
    return fBound;
}複製代碼

這裏,咱們js裏的三種改變this指向的方法就實現啦。。。。。

相關文章
相關標籤/搜索