手寫一個 call既们,apply濒析,bind 吧

這篇文章是手寫實現(xiàn)xxx功能部分的第一篇,后續(xù)會陸續(xù)更新其他的啥纸。

目錄傳送門 github地址

一号杏、call方法

考察知識點: call方法基礎用法,F(xiàn)unction原型對象斯棒,解構用法盾致, JS方法中this的指向, arguments等

function sayName(p) {
  console.log(this.name)
  console.log(p)
  return `${p}${this.name}`
}

// ES6版荣暮,比較簡單
Function.prototype.selfCall = function(ctx, ...args) {
  if (typeof this !== 'function') {
    throw new Error('you must use call with function')
  }
   // ctx = ctx || window || global // 這個寫法是為了在NodeJS中運行庭惜,不過這個并非這個問題的關鍵
  ctx = ctx || window
  // 強綁定上ctx為執(zhí)行上下文,這里為了為了防止方法名稱沖突穗酥,我定義這個方法名稱為_fn护赊,不過這個并不是這個問題的關鍵考察點
  ctx._fn = this
  const res = ctx._fn(...args)
  // 刪除這個,防止影響外部的對象正常運行
  delete ctx._fn
  // 返回值也要注意
  return res
}


// ES5版
Function.prototype.es5call = function(ctx) {
 if (typeof this !== 'function') {
    throw new Error('you must use call with function')
  }
  ctx = ctx || window
  var args = []
  // 收集參數(shù)
  for (var i = 1; i < arguments.length; i++) {
    args.push('arguments[' + i + ']')
  }
  var argstr = args.join(',')
  ctx._fn = this
  // ES5 由于無法通過解構的形式傳入?yún)?shù)砾跃,只能通過字符串拼接然后再通過eval來執(zhí)行
  var res =  eval('ctx._fn(' + argstr + ')')
  delete ctx._fn
  return res
}

var obj = {name: 'wyh'}
// 驗證結果
console.log(sayName.call(obj, 'xxx'))
console.log(sayName.selfCall(obj, 'xxx')) 
console.log(sayName.es5call(obj, 'xxx')) 

二骏啰、apply方法

考察知識點: apply方法用法


var obj = {
  name: 'wyh'
}

function say() {
  console.log(arguments[1] + arguments[2]) // expect 5
  return this.name
}

Function.prototype.es6apply = function(ctx, args) {
  if (typeof this !== 'function') {
    throw new Error('you must use apply with function')
  }
  ctx = ctx || window
  ctx._fn = this
  const res = ctx._fn(...args)
  delete ctx._fn
  return res
}

Function.prototype.es5apply = function(ctx, args) {
  if (typeof this !== 'function') {
    throw new Error('you must use apply with function')
  }
  ctx = ctx || window
  ctx._fn = this
  var res
  if (!args || !args.length) {
    res = ctx._fn()
  } else {
    var argStr = args.reduce(function(prev, current, currentIndex) {
      return prev + ",args[" + currentIndex + "]"
    }, '')
    argStr = argStr.substr(1)
    res = eval("ctx._fn(" + argStr + ")")
  }
  delete ctx._fn
  return res
}

console.log(say.apply(obj, [1,2,3,4])) // 5 , wyh
console.log(say.es6apply(obj, [1,2,3,4])) // 5 , wyh
console.log(say.es5apply(obj, [1,2,3,4])) // 5 , wyh

三、bind方法

考察知識點: bind方法的用法

bind方法是不同于call方法和apply方法的抽高,bind方法會返回一個新的函數(shù)判耕。這個方法的接收參數(shù)形式和意義與call方法一樣,不同的是翘骂,bind會將接收的第一個參數(shù)作為返回的函數(shù)的this對象壁熄。bind方法是函數(shù)珂里化的一個典型例子。

對于bind雏胃,我會特殊對待,由淺到深完成實現(xiàn)它的過程

// 首先定義一些基礎的數(shù)據(jù)方便后面使用
var person = {
  name: 'wyh',
  sayName(n) {
    return n + this.name
  },
  calc(a,b) {
    console.log(this.name)
    return a + b
  }
}

var person2 = {
  name: 'person2'
}

1志鞍、 bind 輔助函數(shù)形式

function _bind(fn, ctx, ...args) {
  return function() {
    return fn.apply(ctx, args)
  }
}

console.log(_bind(person.sayName, person2, 'nihao')()) // nihaoperson2

這個只是為了更方便大家去理解bind函數(shù)的工作模式

2瞭亮、使用apply/call 實現(xiàn)bind函數(shù) 這個也是很簡單的

Function.prototype.applybind = function(ctx, ...args) {
  if (typeof this !== 'function') {
    throw new Error('you must use bind with function')
  }
  ctx = ctx || window
  var self = this
  return function() {
    return self.apply(ctx, args)
  }
}

3、不使用強綁定形式

// ES6版
Function.prototype.es6bind = function(ctx, ...args) {
  if (typeof this !== 'function') {
    throw new Error('you must use bind with function')
  }
  ctx = ctx || window
  ctx._fn = this
  return function() {
    const res = ctx._fn(...args)
    delete ctx._fn
    return res
  }
}

// ES5 版
Function.prototype.es5bind = function(ctx) {
  if (typeof this !== 'function') {
    throw new Error('you must use bind with function')
  }
  ctx = ctx || window
  ctx._fn = this
  var args = []
  // 做一層緩存固棚,因為下面返回的function中的arguments會和這個arguments沖突
  var fArguments = arguments
  for (var i = 1; i < fArguments.length; i++) {
    args.push("fArguments[" + i + "]")
  }
  var argsStr = args.join(',')
  return function() {
    var res = eval("ctx._fn(" + argsStr + ")")
    delete ctx._fn
    return res
  }
}

console.log(person.calc.es6bind(person2, 1, 2)()) // person2 3
console.log(person.calc.es5bind(person2, 1, 2)()) // person2 3

踩坑

在寫call和apply的時候统翩,一時迷了寫了下面這種實現(xiàn)方法:

// 錯誤示范
Function.prototype.callError = function(ctx) {
  ctx = ctx || window
  ctx._fn = this
  var newArr = [];
  for(var i=1; i<args.length; i++) {
    newArr.push(args[i]);
  }
  // 因為這里newArr.join(',') 返回的是一個字符串,也就是不管這個call方法傳入幾個參數(shù)此洲,真正執(zhí)行的就只會有一個參數(shù)了
  var res = ctx._fn(newArr.join(','));
  delete ctx._fn;
  return res
}

結語

內置的call厂汗,apply,bind方法要比我寫的要復雜且健壯的多呜师。不過自己手動實現(xiàn)這三個方法主要是為了考察這三個函數(shù)是否掌握以及函數(shù)內部this的指向問題娶桦。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子衷畦,更是在濱河造成了極大的恐慌栗涂,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祈争,死亡現(xiàn)場離奇詭異斤程,居然都是意外死亡,警方通過查閱死者的電腦和手機菩混,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門忿墅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人沮峡,你說我怎么就攤上這事疚脐。” “怎么了帖烘?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵亮曹,是天一觀的道長。 經(jīng)常有香客問我秘症,道長照卦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任乡摹,我火速辦了婚禮役耕,結果婚禮上,老公的妹妹穿的比我還像新娘聪廉。我一直安慰自己瞬痘,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布板熊。 她就那樣靜靜地躺著框全,像睡著了一般。 火紅的嫁衣襯著肌膚如雪干签。 梳的紋絲不亂的頭發(fā)上津辩,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音容劳,去河邊找鬼喘沿。 笑死,一個胖子當著我的面吹牛竭贩,可吹牛的內容都是我干的蚜印。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼留量,長吁一口氣:“原來是場噩夢啊……” “哼窄赋!你這毒婦竟也來了哟冬?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤寝凌,失蹤者是張志新(化名)和其女友劉穎柒傻,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體较木,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡红符,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了伐债。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片预侯。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖峰锁,靈堂內的尸體忽然破棺而出萎馅,到底是詐尸還是另有隱情,我是刑警寧澤虹蒋,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布糜芳,位于F島的核電站,受9級特大地震影響魄衅,放射性物質發(fā)生泄漏峭竣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一晃虫、第九天 我趴在偏房一處隱蔽的房頂上張望皆撩。 院中可真熱鬧,春花似錦哲银、人聲如沸扛吞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽滥比。三九已至,卻和暖如春做院,著一層夾襖步出監(jiān)牢的瞬間盲泛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工山憨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留查乒,地道東北人弥喉。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓郁竟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親由境。 傳聞我的和親對象是個殘疾皇子棚亩,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344