实现 new
先用文字描述一下 new 的实现过程
- 新定义一个 json 对象
- 对象 继承 构造函数的原型链
- 将构造函数的 this 指向这个对象
- 根据构造函数的返回值类型返回结果,
function myNew(fn) {
let obj = {
}
obj = Object.create(fn.prototype)
let args = Array.prototype.slice.call(arguments, 1) // 获取除去fn之外的参数
let result = fn.call(obj, ...args)
return typeof result === 'object' ? result : obj;
}
function foo() {
this.name = 'ciel'
this.arg = arguments[0]
}
foo.prototype.callName = function() {
console.log(this.name)
}
// 测试
let test = myNew(foo, 'hhh', '123', 'saf')
test.callName()
console.log(test.arg)
这里解释一下 return typeof result === ‘object’ ? result : obj; 这句代码:
在JavaScript构造函数中:如果return值类型,那么对构造函数没有影响,实例化对象返回空对象;如果return引用类型(数组,函数,对象),那么实例化对象就会返回该引用类型; 可以测试以下两个构造函数在 new 之后返回的值就可以理解这句话的意思了
function foo() {
this.name = 'ciel'
return function() {
}
}
new foo() // fn(){}
function bar() {
this.name = 'ciel'
return 1
}
new bar() // {name: ciel}
实现 call
先看看伪代码是如何使用 myCall 的 fn.myCall(obj, args) 分析下代码应该怎么实现
myCall 应该挂在 Function.prototype 上
fn 的 this 指向 为 obj
myCall 的 args 透传给 fn
Function.prototype.myCall = function(target, ...args) {
// this 指向调用 myCall函数的对象
if (typeof this !== "function") {
throw new TypeError("not a function")
}
target = target || window
target.fn = this // 隐式绑定,改变构造函数的调用者间接改变 this 指向
let result = target.fn(...args)
return result
};
// 测试
let obj = {
name: 123 }
function foo(...args) {
console.log(this.name, args)
}
let s = foo.myCall(obj, '111', '222')
实现 apply
回忆一下 apply 与 call 的区别: apply 参数要为数组。 其他和 call 实现一样
Function.prototype.myApply = function(target) {
if (typeof this !== "function") {
throw new TypeError("not a function");
}
if (!Array.isArray(arguments[1])) {
throw new Error('arg not a array')
}
target = target || window
target.fn = this
let args = arguments[1]
let result = target.fn(args)
return result
};
const obj = {
name: 123 };
function foo(...args) {
console.log(this.name, args);
}
foo.prototype.name = 123;
const s1 = [1, 2,