call、apply、bind、new、instanceof 實現原理javascript
更改函數的 this 指向並執行函數。java
Function.prototype.myCall = function(context) {
var context = context || window
context.fn = this
var args = []
for(var i = 1; i < arguments.length; i++) {
args.push('arguments[' + i + ']')
}
var result = eval('context.fn(' + args + ')')
delete context.fn
return result
}
複製代碼
分析:git
context
做爲可選參數,若是不傳或傳 null
,默認上下文爲 window
context
添加方法 fn
,fn
就是要執行的函數,誰調用了 call() , fn
就是誰。 因此 context.fn = this
fn
,將結果返回。核心就是,經過將函數添加到對象上,而後經過對象調用這個函數,從而使函數中的 this 指向這個對象。github
更簡潔的寫法:數組
Function.prototype.myCall = function(context, ...rest) {
var context = context || window
context.fn = this
var result = context.fn(...rest)
delete context.fn
return result
}
複製代碼
apply 實現與 call 相似,區別在於對參數的處理app
Function.prototype.myApply = function(context, arr) {
var context = context || window
context.fn = this
var result
if(!arr) {
result = context.fn()
} else {
var args = []
for(var i = 0; i < arr.length; i++) {
args.push('arr[' + i + ']')
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result
}
複製代碼
簡潔:函數
Function.prototype.myApply = function(context, arr) {
var context = context || window
context.fn = this
var result
if(arr) {
result = context.fn(...arr)
}else {
result = context.fn()
}
delete context.fn
return result
}
複製代碼
bind 方法返回一個改變 this 指向的函數,bind 第一個參數做爲這個函數的 this,後續參數會在函數調用時,做爲函數的參數傳入,而後纔會傳入函數的實參。ui
舉個例子:this
function foo(name,age) {
console.log('name:' + name + 'age:' + age)
}
var f = foo.bind(null,'張三')
f(20) // name:張三age:20
複製代碼
實現:spa
Function.prototype.myBind = function(context) {
var self = this
var args = [].slice.call(arguments, 1)
function F() {
var args2 = [].slice.call(arguments)
var arglist = args.concat(args2)
return self.apply(this instanceof F ? this: context, arglist)
}
F.prototype = Object.create(self.prototype)
return F
}
複製代碼
簡潔版:
Function.prototype.myBind = function(context) {
var self = this
var args = [...arguments].slice(1)
function F() {
return self.apply(this instanceof F ? this : context,args.concat(...arguments))
}
F.prototype = Object.create(self.prototype)
return F
}
複製代碼
原函數.apply()
實現改變 this 。context
,this 應該是聲明的變量。context
的後續參數,先存起來,在調用由 bind 返回的新函數時 ,向 apply() 傳入由原來存的參數和新傳入的參數組成的數組(注意順序)。new 運算符建立了一個用戶自定義類型的實例
在執行 new 構造函數()
時,發生了:
__proto__
)指向構造函數的原型function myNew() {
var constructor = [].shift.call(arguments)
var obj = Object.create(constructor.prototype)
var res = constructor.apply(obj, arguments)
return res instanceof Object ? res: obj
}
複製代碼
ES6 簡潔版:
function myNew(constructor, ...rest) {
let obj = Object.create(constructor.prototype)
let res = constructor.apply(obj, rest)
return res instanceof Object ? res : obj
}
複製代碼
能正確判斷對象的類型,原理是判斷構造函數的原型對象是否能在對象的原型鏈上
function myInstanceof(obj, fn) {
let prototype = fn.prototype
let objProto = obj.__proto__
while(true) {
if(objProto == null)
return false
if(objProto === prototype)
return true
objProto = objProto.__proto__
}
}
複製代碼
參考資料: