- 小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
因为现在大环境的原因,前端对底层原理需要有一定的掌握,所以特地学习并记录一下前端涉及到的各种手写及算法。 书接上文前端手写各种(一)
5.手写 new 操作符的实现
function myNew(fn, ...args) {
// 创建一个新对象(若该函数不是 JS 内置的,则创建一个新的 Object 对象)
let obj = Object.create(fn.prototype);
// 将 this 绑定到这个对象;执行构造函数中的代码(为这个新对象添加属性)
let res = fn.call(obj, ...args);
//若函数没有返回其他对象,则自动返回这个新对象;若函数有 return 返回的是非对象,则还是自动返回这个新对象,即覆盖那个非对象。
if (res && (typeof res === "object" || typeof res === "function")) {
return res;
}
return obj;
}
6.手写 call、apply、bind
Function.prototype.myCall = function (context, ...args) {
if (!context || context === null) {
context = window;
}
// 创造唯一的key值,作为我们构造的context内部方法名
let fn = Symbol();
context[fn] = this; // this指向调用call的函数
// 执行函数并返回结果,相当于把自身作为传入的context的方法进行调用了
return context[fn](...args);
};
// apply原理一致,只是第二个参数是传入的数组
Function.prototype.myApply = function (context, args) {
if (!context || context === null) {
context = window;
}
// 创造唯一的key值,作为我们构造的context内部方法名
let fn = Symbol();
context[fn] = this;
// 执行函数并返回结果
return context[fn](...args);
};
// bind实现较为复杂
Function.prototype.myBind = function (context, ...args) {
if (!context || context === null) {
context = window;
}
// 创造唯一的key值,作为我们构造的context内部方法名
let fn = Symbol();
context[fn] = this;
let _this = this;
// bind情况较为复杂
const result = function (...innerArgs) {
if (this instanceof _this === true) {
// 此时this指向指向result的实例 这时候不需要改变this指向
this[fn] = _this;
this[fn](...[...args, ...innerArgs]); //这里使用es6的方法让bind支持参数合并
} else {
// 如果只是作为普通函数调用 那就很简单了 直接改变this指向为传入的context
context[fn](...[...args, ...innerArgs]);
}
// 如果绑定的是构造函数 那么需要继承构造函数原型属性和方法
// 实现继承的方式: 使用Object.create
result.prototype = Object.create(this.prototype);
return result;
};
};
感兴趣的童鞋,可以继续关注后续文章。