apply、call、bind的區別與精簡實現

apply() 方法接收一個指定的this值和一個包含多個參數的數組來調用一個函數。javascript

call() 方法接收一個指定的 this 值和一個參數列表來調用一個函數。前端

bind() 方法建立一個新的函數,在 bind() 被調用時,這個新函數的 this 被指定爲 bind() 的第一個參數,而其他參數將做爲新函數的參數,供調用時使用java

使用 callapply 函數的時候要注意,若是傳遞給 this 的值不是一個對象,JavaScript 會嘗試使用內部 ToObject 操做將其轉換爲對象。所以,若是傳遞的值是一個原始值好比 7'foo',那麼就會使用相關構造函數將它轉換爲對象,因此原始值 7 會被轉換爲對象,像 new Number(7) 這樣,而字符串 'foo' 轉化成 new String('foo') 這樣,例如:數組

function bar() {
  console.log(Object.prototype.toString.call(this));
}

//原始值 7 被隱式轉換爲對象
bar.call(7); // [object Number]
bar.call('foo'); // [object String]
複製代碼

apply

func.apply(thisArg, [argsArray])閉包

  • thisArg必選的。在 func 函數運行時使用的 this 值。若是這個函數處於非嚴格模式下,則指定爲 nullundefined 時會自動替換爲指向全局對象,原始值會被包裝。
  • argsArray 可選的。一個數組或者類數組對象,其中的數組元素將做爲單獨的參數傳給 func 函數。
Function.prototype.Apply = function (thisArg, args = Symbol.for('args')) {
  //Apply 函數老是被咱們想改變this的函數調用,所以本函數內this老是指代調用函數
  
  //生成一個Symbol類型的惟一符號,用於將調用apply的函數掛載到指定對象上
  const fn = Symbol('fn')      
  thisArg[fn] = this  

  //經過對象調用函數,並傳參
  args === Symbol.for('args') ? thisArg[fn]() : thisArg[fn](...args)

  //刪除掛載到指定對象上的方法
  delete thisArg[fn]           
}

// 聲明全局變量
var position = 'global'
var name = 'window'

function func(name) {
  console.log(this.position)
  console.log(name) 
}

const obj = {
  name: 'object',
  position: 'obj',
}

func.Apply(obj,[obj.name,null]) // obj object
// 其中,Apply內this指向func

func.Apply(obj,[name,null]) // obj window
// func中 name 只受傳參影響
複製代碼

Symbol.for(key) 會根據給定的鍵 key,來從運行時的 symbol 註冊表中找到對應的 symbol,若是找到了,則返回它,不然,新建一個與該鍵關聯的 symbol,並放入全局 symbol 註冊表中。app

call

Function.prototype.Call = function (thisArg, ...args) {
  //這裏this爲這個方法的調用者
  const fn = Symbol('fn')
  thisArg[fn] = this || globalThis

  //經過對象調用函數,並傳參
  args.length ? thisArg[fn](...args) : thisArg[fn]() 

  //刪除掛載到指定對象上的方法
  delete thisArg[fn]      
}

func.Call(obj)  // obj undefined
func.Call(obj, 'test') // obj test
複製代碼

這裏不使用arguments對象獲取傳入參數。arguments不是一個 Array ,除了length屬性和索引元素以外沒有任何Array屬性。函數

使用剩餘參數...args能夠精簡代碼post

bind

bind() 函數會建立一個新的綁定函數,它包裝了原函數對象。調用綁定函數一般會致使執行包裝函數。ui

簡單版本this

Function.prototype.Bind = function(thisArg, ...args){
  let self = this;
  let fBound = function(...args1){
    return self.apply(thisArg, [...args, ...args1]);
  }
  return fBound;
}
複製代碼

使用閉包保存了第一次綁定時的this值,同時使後續的綁定無效

Function.prototype.Bind = function(thisArg, ...args){
  let self = this;
  
  let fBound = function(...args1){
    //若是當前this爲fBound的實例,表示是執行了new,指向this,不然指向bind對象
    return self.apply(this instanceof fBound ? this : thisArg, [...args, ...args1]);
  }
  //修改返回函數的 prototype 爲綁定函數的 prototype,new出實例對象就能夠繼承綁定函數的原型中的成員
  fBound.prototype = this.prototype;
  return fBound;
}
複製代碼

這裏補充了綁定的函數爲構造函數時的狀況

function foo(age, height) {
  console.log(this.name)       // obj
  console.log(age)             // 3
  console.log(height)          // 2
}
const obj = {
  name: 'obj',
  age: 3
}
foo.Bind(obj, obj.age)(2) // 綁定bind時同時傳遞一個參數
複製代碼

相關文章:
從零開始的前端築基之旅(超級精細,持續更新~)
看完就能搞懂的this指向及箭頭函數的講解~

若是你收穫了新知識,請給做者點個贊吧~

相關文章
相關標籤/搜索