手寫源碼系列(一):call、apply和bind

最近工做不是十分緊張,閒來無事,決定好好鞏固一下基礎知識。最近的風氣是,各個公司都喜歡拿源碼說事兒,那咱們就藉此機會,關注一下你們平常開發中常常用到的方法、屬性的源碼。javascript

概念

在開始以前,咱們很是有必要去了解、鞏固一下call、apply和bind在開發中的具體做用。前端

// call
    var a = {
        name : "Cherry",

        func1: function () {
            console.log(this.name)
        },

        func2: function () {
            setTimeout(  function () {
                this.func1()
            }.call(a),100);
        }

    };

    a.func2() // "Cherry"
    
// apply 
    var a = {
        name : "Cherry",

        func1: function () {
            console.log(this.name)
        },

        func2: function () {
            setTimeout(  function () {
                this.func1()
            }.apply(a),100);
        }

    };

    a.func2()   // Cherry
    
// bind
  var a = {
        name : "Cherry",

        func1: function () {
            console.log(this.name)
        },

        func2: function () {
            setTimeout(  function () {
                this.func1()
            }.bind(a)(),100);
        }

    };

    a.func2()  // Cherry

複製代碼

相同點:均可以改變this指向。
不一樣點:call方法接受的是一個參數列表,apply的第二個參數爲數組;bind方法返回的不是具體數值,而是函數。
若是想獲取bind方法的返回值,可主動執行java

function sayHelloTo (to) {
    console.log(`${this.name} say hello to ${to}`)
}

var Jerry = {
  name: 'Jerry'
}
sayHelloTo.call(Jerry, 'Tom')
//Jerry say hello to Tom.

var Foo = {
  name: 'Foo'
}
sayHelloTo.apply(Foo, ['Bar'])
//Foo say hello to Bar.

var XYZ = {
  name: 'XYZ'
}
var say = sayHelloTo.bind(XYZ)
say('ABC')
//XYZ say hello to ABC.
複製代碼

手寫call方法

Function.prototype.myCall = function(context, ...args) {
  // 判斷是不是undefined和null
  if (typeof context === 'undefined' || context === null) { //1
    context = window
  }
  let fnSymbol = Symbol()      //2
  context[fnSymbol] = this    // 3
  let fn = context[fnSymbol] (...args) //4
  delete context[fnSymbol] //5
  return fn  //6
}
複製代碼

咱們利用上面的myCall方法,替換call來執行上面的例子,能夠獲得相同的結果es6

咱們分別來解讀一下上面備註的數字:
一、判斷context的值,若是沒有或者爲undefined,則this指向window。(與call方法中,若是第一個參數行爲保持一致
二、爲傳入的context擴展一個屬性。
三、將原函數指向這個屬性。(this在調用時,指向call前面的函數
四、執行原函數。
五、刪除傳入的對象的無用屬性。
六、返回執行後的值。數組

這裏須要注意的地方有兩點:
一、要熟練使用es6相關知識(...args,Symbol)
二、之因此能獲取到this.name,是由於此時的this指向的是contextapp

手寫apply方法

Function.prototype.myApply = function(context, args) { // 1
  // 判斷是不是undefined和null
  if (typeof context === 'undefined' || context === null) {
    context = window
  }
  let fnSymbol = Symbol()
  context[fnSymbol] = this
  let fn = context[fnSymbol] (...args)
  return fn
}
複製代碼

注意傳參不一樣。
告訴你個小祕密,如何記住傳的是列表類仍是數組呢?Array與apply都是A開頭,那就傳數組嘍!call不是A開頭,那就是列表!函數

手寫bind方法

Function.prototype.myBind = function(context) {
// 判斷是不是undefined和null
    if (typeof context === "undefined" || context === null) {
    	context = window;
    }
    self = this;
    return function(...args) {
    	return self.apply(context, args);
    }
}
複製代碼

能夠借用apply或者call,間接實現bind,而且,注意開頭提到的區別!
bind返回的是函數哦~post

若是文章對的前端學習有幫助,別忘了點贊關注哦~學習


摘錄自
較真的前端
this、apply、call、bind
MDNui

相關文章
相關標籤/搜索