call的模擬實現

call:

call()方法在使用一個指定的 this 值和若干個指定的參數值的前提下調用某個函數或方法

舉例:git

var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}
bar.call(foo); // 1

根據上面的案例,咱們能夠注意到兩點github

  • call改變了this指向,指向了foo
  • bar函數執行了

模擬實現第一步

看到上面的效果,咱們該如何實現呢?
首先咱們能夠將代碼改寫成如下形式數組

var foo = {
    value: 1,
    bar:function(){
        console.log(this.value)
    }
}
foo.bar()

改造事後咱們能夠看到此時的this也是指向foo的,可是這樣倒是給foo對象添加了一個屬性bar的函數,這樣是不合適的,不過咱們能夠使用delete方法將其刪除,所以步驟能夠分爲如下三步:函數

  • 將函數設爲對象的屬性
  • 執行該函數
  • 刪除該函數

以上三個步驟就能夠寫作:this

注意:由於是在對象中添加屬性,fn屬性名字能夠隨便寫,反正也要刪除
foo.fn = bar
  foo.fn()
  delete foo.fn

根據以上思路實現初版:prototype

Function.prototype.call2 = function (context) {
    // this誰調用指向誰,此時是函數bar調用call2,由於this指向bar
    context.fn = this
    context.fn()
    delete context.fn
}
var foo = {
    value: 1
}
function bar() {
    console.log(this.value) // 1
}
bar.call2(foo)

模擬實現第二步

咱們知道call方法還能夠給定參數,能夠看以下案例:code

var foo = {
    value: 1
}
function bar(name, age) {
    console.log(name) // 張三
    console.log(age)  // 18 
    console.log(this.value) // 1
}
bar.call(foo, '張三', 18)

可是咱們要知道參數傳遞是不固定的,所以咱們能夠使用在argument中取值,取出咱們傳遞的第二個參數到最後一個參數,任何添加到一個數組中,實現代碼以下對象

var arg = []
for(var i = 1; i < argument.length; i++){
    args.push('arguments[' + i + ']');
}
注意:arguments是一個僞數組,所以要用for循環

解決了傳遞參數不定長的問題,那麼參數放到須要執行的函數參數中應該如何實現呢?get

context.fn(arg.join(''))
這種方法是確定不行的

咱們能夠經過ES3中的eval來實現,這樣就能夠實現函數參數傳遞以及調用了it

eval('context.fn(' + args +')')

因此第二版就實現以下

Function.prototype.call2 = function (context) {
    context.fn = this
    var args = []
    for (var i = 1; i < arguments.length; i++) {
        args.push('arguments[' + i + ']')
    }
    eval('context.fn(' + args + ')')
    delete context.fn
}

模擬實現第三步

  • this參數能夠傳null,當爲null的時候,視爲指向window
var value = 1
function bar() {
    console.log(this.value)
}
  • 函數是有返回值的
var obj = {
    value: 1
}
function bar(name, age) {
    return {
      value: this.value,
      name,
      age
    }
}
console.log(bar.call(obj, '張三', 18))

不過這個很好實現,直接上最終版

Function.prototype.call2 = function (context) {
    var context = context || window
    context.fn = this
    var args = []
    for (var i = 1; i < arguments.length; i++) {
        args.push('arguments[' + i + ']')
    }
    var result = eval('context.fn(' + args + ')')
    delete fn
    return result
}
var obj = {
    value: 1
}
function bar(name, age) {
    return {
      value: this.value,
      name,
      age
    }
}
console.log(bar.call2(obj, '張三', 18))

也能夠使用ES6的語法實現

Function.prototype.call2 = function (context, ...args) {
    context = context || window
    // Symbol爲了防止有相同的函數名
    let fn = Symbol()
    context.fn = this
    const result = context.fn(...args)
    delete context.fn
    return result
}
var foo = {
    value: 1
}
function bar(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value)
}

主要參考:https://github.com/mqyqingfen...

相關文章
相關標籤/搜索