手写 JS
instanceof
instanceof 用于检测构造函数的 prototype 是否出现在被检测对象的原型链上。
- 基础数据类型都返回 false
- null 返回 false
- 校验右侧数据类型,如果是基础数据类型则报错:
Uncaught TypeError: Right-hand side of 'instanceof' is not an object
- 右侧如果是
{}
,则报错:Uncaught TypeError: Right-hand side of 'instanceof' is not callable
- 右侧要有 prototype 属性
function myInstanceof(left, right) {
if ((typeof left !== 'object' && typeof left !== 'function') || left === null) return false;
let proto = Object.getPrototypeOf(left);
while (true) {
if (proto === null) return false;
if (proto === right.prototype) return true;
proto = Object.getPrototypeOf(proto);
}
}
console.log(myInstanceof(1, Number)); // false
console.log(myInstanceof(new Boolean(), Boolean)); // true
console.log(myInstanceof(() => {}, Function)); // true
console.log({} instanceof {}); // Uncaught TypeError: Right-hand side of 'instanceof' is not callable
console.log({} instanceof 1); // Uncaught TypeError: Right-hand side of 'instanceof' is not an object
new
function myNew(constructor, ...args) {
if (typeof constructor !== 'function') {
throw new TypeError('Constructor must be a function');
}
const obj = Object.create(constructor.prototype);
const result = constructor.apply(obj, args);
return result !== null && result instanceof Object ? result : obj;
}
call
示例:
let foo = { value: 1 };
function bar() {
console.log(this.value);
}
bar.call(foo);
通过 call 将 this 指向了 foo,可以理解成foo.bar()
,如下:
let foo = {
value: 1,
bar: function () {
console.log(this.value);
}
};
还有其他的情况:接收 参数、参数为 null 或 undefined、有返回值
let foo = { value: 1 };
function bar(a, b) {
console.log(a, b);
console.log(this.value);
return { a: 1 };
}
bar.call(foo, 1, 2);
bar.call(null); // this 指向 window
let res = bar.call(foo, 1, 2);
console.log(res); // { a: 1 }
综上,总结出以下步骤:
- 给 foo 增加一个临时的函数 fn,指向 bar:
foo.fn = bar
- 执行 fn:
foo.fn()
- 删除 fn:
delete foo.fn
- 如果传入 call 的参数是
null
或者undefined
,那么 this 就指向window
- 如果 bar 有返回值,需要将结果返回
最终结果:
Function.prototype.myCall = function (context, ...args) {
context = context || window;
context.fn = this;
let result = context.fn(...args);
delete context.fn;
return result;
};