如何實(shí)現(xiàn) new 潮孽、apply 、call 庐镐、bind 的底層邏輯

new 原理介紹

new 關(guān)鍵詞的主要作用是執(zhí)行一個(gè)構(gòu)造函數(shù)恩商、返回一個(gè)實(shí)例對(duì)象,根據(jù)構(gòu)造函數(shù)的懷況中必逆,來(lái)確定是否可以接受參數(shù)的傳遞怠堪。

function Person() {
  this.name = 'Jack'
}
let p = new Person()
console.log(p.name) // Jack

new 在生成實(shí)例的過(guò)程中進(jìn)行的步驟:

  • 創(chuàng)建一個(gè)新對(duì)象
  • 將構(gòu)造函數(shù)的作用域賦給新對(duì)象(this指向新對(duì)象)
  • 執(zhí)行構(gòu)造函數(shù)中的代碼(為這個(gè)新對(duì)象添加屬性)
  • 返回新對(duì)象

當(dāng)構(gòu)造函數(shù)中有return一個(gè)對(duì)象的操作時(shí):

function Person() {
  this.name = 'Jack'
  return{age:18}
}
let p = new Person()
console.log(p) // {age:18}
console.log(p.name) // undefined
console.log(p.age) // 18

上面代碼可以看出,當(dāng)構(gòu)造函數(shù)return一個(gè)和this無(wú)關(guān)的對(duì)象時(shí)名眉,new會(huì)直接返回這個(gè)新對(duì)象 粟矿,而不是通過(guò)new執(zhí)行步驟生成的this對(duì)象 。
注:構(gòu)造函數(shù)必須返回是一個(gè)對(duì)象损拢,如果返回不是對(duì)象陌粹,那么還是按照new的實(shí)現(xiàn)步驟返回新生成的對(duì)象。

function Person() {
  this.name = 'Jack'
  return 'tom'
}
let p = new Person()
console.log(p) // {name:'Jack'}
console.log(p.name) // Jack

new 被調(diào)用后大致做了哪幾件事情

  • 讓實(shí)例可以訪問(wèn)到私有屬性
  • 讓實(shí)例可以訪問(wèn)構(gòu)造函數(shù)原型(constructor.prototype)所在原型鏈上的屬性
  • 構(gòu)造函數(shù)返回的最后結(jié)果是引用數(shù)據(jù)類(lèi)型

總結(jié):new關(guān)鍵詞執(zhí)行之后總是會(huì)返回一個(gè)對(duì)象福压,要么是實(shí)例對(duì)象掏秩,要么是return語(yǔ)句指定的對(duì)象

apply & call & bind 原理介紹

call 、apply 和 bind 是掛在 Function 對(duì)象上的三個(gè)方法荆姆,調(diào)用這三個(gè)方法必須是一個(gè)函數(shù)

語(yǔ)法:

func.call(thisArg,param1,param2,...)
func.apply(thisArg,param1,param2,...)
func.bind(thisArg,param1,param2,...)

thisArgthis所指向的對(duì)象蒙幻,后面的param1,param2為函數(shù)的function的多個(gè)參數(shù),如果不需要參數(shù)可不寫(xiě)

相同點(diǎn):都可改變 function 的 this 指向

call & apply 區(qū)別:
傳參的寫(xiě)法不同

  • apply第二個(gè)參數(shù)為數(shù)組
  • call則是第二個(gè)至第N個(gè)都是給fcuntion的傳參

bind 與 call & apply區(qū)別:

  • bind雖然改變了functionthis指向胆筒,但不是馬上執(zhí)行
  • call & apply 是在改變functionthis指向后立即執(zhí)行
借用

A對(duì)象有個(gè)getName的方法邮破,B 對(duì)象也需要臨時(shí)使用同樣的方法,那么這個(gè)時(shí)候可以借用A 對(duì)象的getName方法
例:

let a = {
  name: 'jack',
  getName: function(msg) {
    return msg + this.name
  }
}
let b = {
  name: 'lily'
}
console.log(a.getName('hello~')) // hello~jack
console.log(a.getName.call(b, 'hello~')) // hello~lily
console.log(a.getName.apply(b, ['hello~'])) // hello~lily
let name = a.getName.bind(b, 'hello~')
console.log(name()) // hello~lily

apply & call & bind 的使用場(chǎng)景

1、判斷數(shù)據(jù)類(lèi)型

Object.prototype.toString幾乎可以判斷所有類(lèi)型的數(shù)據(jù)

function getType(obj) {
  let type = typeof obj
  if (type !== 'object') {
    return type
  }
  return Object.prototype.toString.call(obj).replace(/^$/, '$1')
}

判斷數(shù)據(jù)類(lèi)型就是借用了Object.prototype.toString方法抒和,最后返回用來(lái)判斷傳入的object字符串來(lái)確定最后的數(shù)據(jù)類(lèi)型

2矫渔、類(lèi)數(shù)組的借用方法

類(lèi)數(shù)組因?yàn)椴皇钦嬲臄?shù)組,所以沒(méi)有數(shù)組類(lèi)型上自帶的種種方法摧莽,可以利用一些方法云借用數(shù)組的方法庙洼。

var arrayLike = {
  0: 'java',
  1: 'script',
  length: 2
}
Array.prototype.push.call(arrayLike, 'jack', 'lily')
console.log(typeof arrayLike) // object
console.log(arrayLike) // {0: "java", 1: "script", 2: "jack", 3: "lily", length: 4}
3、獲取數(shù)組的最大 / 最小值

apply來(lái)實(shí)現(xiàn)數(shù)組中判斷最大 / 最小值范嘱,apply直接傳遞數(shù)組作為調(diào)用方法的參數(shù)送膳,也可以減少一步展開(kāi)數(shù)組

let arr = [13, 6, 10, 11, 16]
const max = Math.max.apply(Math, arr)
const min = Math.min.apply(Math, arr)
console.log(max) // 16
console.log(min) // 6
繼承
function Parent3() {
  this.name = 'parent3'
  this.play = [1, 2, 3]
}
Parent3.prototype.getName = function() {
  return this.name
}
function Child3() {
  // 第二次調(diào)用 Parent3()
  Parent3.call(this)
  this.type = 'child3'
}
Child3.prototype = new Parent3()
// 手動(dòng)掛上構(gòu)造器,指向自己的構(gòu)造函數(shù)
Child3.prototype.constructor = Child3
var s3 = new Child3()
s3.play.push(4)
console.log(s3.getName()) //parent3
apply 和 call 的實(shí)現(xiàn)
Function.prototype.call = function(context, ...args) {
  var context = context || window
  context.fn = this
  var result = eval('context.fn(...args)')
  delete context.fn
  return result
}
Function.prototype.apply = function(context, args) {
  let context = context || window
  context.fn = this
  let result = eval('context.fn(...args)')
  delete context.fn
  return result
}

這兩個(gè)方法是直接返回執(zhí)行結(jié)果 丑蛤,而 bind 方法是返回一個(gè)函數(shù)叠聋,因此這里直接用 eval 執(zhí)行得到結(jié)果

bind 的實(shí)現(xiàn)

bind 的實(shí)現(xiàn)思路基本和 apply 一樣,但是在最后實(shí)現(xiàn)返回結(jié)果這里 bind 不需要直接執(zhí)行受裹,因此不再需要用 eval 而是需要通過(guò)返回一個(gè)函數(shù)的方式將結(jié)果返回之后再通過(guò)執(zhí)行這個(gè)結(jié)果碌补,得到想要的執(zhí)行效果

Function.prototype.bind = function(context, ...args) {
  if (typeof this !== 'function') {
    throw new Error('this must be a function')
  }
  var self = this
  var fbound = function() {
    self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments)))
  }
  if (this.prototype) {
    fbound.prototype = Object.create(this.prototype)
  }
  return fbound
}
總結(jié)
總結(jié)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市棉饶,隨后出現(xiàn)的幾起案子厦章,更是在濱河造成了極大的恐慌,老刑警劉巖照藻,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件袜啃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡幸缕,警方通過(guò)查閱死者的電腦和手機(jī)群发,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)发乔,“玉大人熟妓,你說(shuō)我怎么就攤上這事±干校” “怎么了起愈?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)译仗。 經(jīng)常有香客問(wèn)我抬虽,道長(zhǎng),這世上最難降的妖魔是什么纵菌? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任斥赋,我火速辦了婚禮,結(jié)果婚禮上产艾,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好闷堡,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布隘膘。 她就那樣靜靜地躺著,像睡著了一般杠览。 火紅的嫁衣襯著肌膚如雪弯菊。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,821評(píng)論 1 290
  • 那天踱阿,我揣著相機(jī)與錄音管钳,去河邊找鬼。 笑死软舌,一個(gè)胖子當(dāng)著我的面吹牛才漆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播佛点,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼醇滥,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了超营?” 一聲冷哼從身側(cè)響起鸳玩,我...
    開(kāi)封第一講書(shū)人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎演闭,沒(méi)想到半個(gè)月后不跟,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡米碰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年窝革,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片见间。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡聊闯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出米诉,到底是詐尸還是另有隱情菱蔬,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布史侣,位于F島的核電站拴泌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏惊橱。R本人自食惡果不足惜蚪腐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望税朴。 院中可真熱鬧回季,春花似錦家制、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至鼻忠,卻和暖如春涵但,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背帖蔓。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工矮瘟, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人塑娇。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓澈侠,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親钝吮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子埋涧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容