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,...)
thisArg為this所指向的對(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雖然改變了function的this指向胆筒,但不是馬上執(zhí)行
- 而call & apply 是在改變function的this指向后立即執(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
}