call 和 apply 和 bind
-
首先会问call 和 apply 和 bind 三者有什么区别?
这个区别应该很容易,如果回答不好,就不会继续下去了,如果回答正确,就会要求手写call函数
如果实现了call函数,apply和bind基本都没啥问题了,所谓首先来实现call函数
call的难点在哪里呢?
就是如何确定this的指向,其实这里很简单,就是判断一下,如果没有传入,则设置为window,如果有传入,则设置为传入的代码
Function.prototype.myCall = function(context) {
// 关键代码在这里,如果传入的context为空,则设置为window
if (typeof context === undefined || typeof context === null) {
context = window
}
const symbol = Symbol()
context[symbol] = this
const args = [...arguments].slice(1)
const result = context[symbol](...args)
delete context[symbol]
return result
}
bind的实现有点类似call,但是这里因为bind会返回一个函数,假设返回的函数是A,需要考虑new A()的情况。
Function.prototype.myBind = function (context) {
if (typeof context === undefined || typeof context === null) {
context = window
}
const _this = this
const args = [...arguments].slice(1)
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以这样使用 new F(),所以需要判断是否是按照new F()使用了。
if (this instanceof F) {
return new _this(...args, ...arguments)
}
// 这边的 apply 严谨点可以自己实现
return _this.apply(context, args.concat(...arguments))
}
}
new
new 的过程发生了什么?
-
新建一个对象 -
然后把设置的属性,挂载到对象上面。 -
设置对象的_proto_ 指向构造函数的原型链 -
构造函数调用apply,传入新建的对象,查看执行结果,如果结果是一个对象就返回,如果不是一个对象,就把第一步新建的对象返回。
既然分析清楚了实现方式,那么使用代码实现就比较清楚了
function create() {
// 新建一个对象
const obj = {}
let Con = [].shift.call(arguments)
// 设置对象的_proto_ 指向构造函数的原型链
obj.__proto__ = Con.prototype
// 构造函数调用apply,传入新建的对象,查看执行结果,如果结果是一个对象就返回,如果不是一个对象,就把第一步新建的对象返回。
let result = Con.apply(obj, arguments)
return result instanceof Object ? result : obj
}
本文由 mdnice 多平台发布