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__
}
}
复制代码
参考资料: