手动实现 new
创建一个新对象,这个对象的proto要指向构造函数的原型对象 执行构造函数 返回值为 object 类型则作为 new 方法的返回值返回,否则返回上述全新对象
function _new() {
let obj = {};
let [constructor, ...args] = [...arguments];
obj.__proto__ = constructor.prototype;
let result = constructor.apply(obj, args);
if ((result && typeof result === "function") || typeof result === "object") {
return result;
}
return obj;
}
实现 instandof
instanceof 运算符主要是用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
function _instanceof(leftValue, rightValue) {
let rightValue = rightValue.prototype; // 取得当前类的原型
let proto = Object.getPrototypeOf(leftValue); // 取得当前实例对象的原型链上的属性
while (true) {
if (proto === null) {
// 找到了 Object的基类 Object.prototype.__proto__
return false;
}
if (proto === rightValue) {
// 在当前实例对象的原型链上,找到了当前类
return true;
}
proto = Object.getPrototypeOf(proto); // 沿着原型链__ptoto__一层一层向上查找
}
}
实现 call 和 apply
改变 this 指向,唯一区别就是传递参数不同
// 实现call
Function.prototype.call = function (context, ...args) {
// null,undefined,和不传时,context为 window
context = context == null ? window : context;
// 必须保证 context 是一个对象类型
let contextType = typeof context;
if (!/^(object|function)$/i.test(contextType)) {
// context = new context.constructor(context); // 不适用于 Symbol/BigInt
context = Object(context);
}
let result;
context["fn"] = this; // 把函数作为对象的某个成员值
result = context["fn"](...args); // 把函数执行,此时函数中的this就是
delete context["fn"]; // 设置完成员属性后,删除
return result;
};
// 实现apply
Function.prototype.apply = function (context, args) {
context = context == null ? window : context;
let contextType = typeof context;
if (!/^(object|function)$/i.test(contextType)) {
context = Object(context);
}
let result;
context["fn"] = this;
result = context["fn"](...args);
delete context["fn"];
return result;
};
实现 bind
bind 它并不是立马执行函数,而是有一个延迟执行的操作,就是生成了一个新的函数,需要你去执行它
Function.prototype.mybind = function (context, ...args) {
return (...newArgs) => {
return this.call(context, ...args, ...newArgs);
};
};
实现数组的扁平化 flat
- ES6 flat 方法 arr.flat()
- reduce 实现
function flatten(arr) {
return arr.reduce((result, item) => {
return result.concat(Array.isArray(item) ? flatten(item) : item);
}, []);
}
- 扩展运算符
function flatten(arr) {
while (arr.some((item) => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
实现数据去重
-
ES6 set 方法
js const newArr = [...new Set(arr)] -
filter 方法
function unique1(arr) { const newArr = arr.filter((item, index, arr) => { return arr.indexOf(item) === index; }); return newArr; }- reduce 方法
function unique2(arr) { const newArr = arr.reduce((pre, cur) => { pre.includes(cur) ? pre : [...pre, cur]; }, []); return newArr; }-
利用对象的键唯一性
function unique3(arr) { let obj = {}; const newArr = arr.filter((item) => { return obj.hasOwnProperty(item) ? false : (item] = true); }); return newArr }
实现函数函数柯里化
函数柯里化就是把接受「多个参数」的函数变换成接受一个「单一参数」的函数,并且返回接受「余下参数」返回结果的一种应用。 所以我们可以首先判断传递的参数是否达到执行函数的 fn 个数如果没有达到的话 继续返回新的函数 并返回 curry 函数传递剩余参数
function curry(fn, ...args) {
fn.length > args.length
? (...arguments) => curry(fn, ...args, ...arguments)
: fn(...args);
}
实现 sleep 函数
某个时间过后,就去执行某个函数,基于 Promise 封装异步任务
function sleep(fn, wait) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(fn);
});
});
}
实现一个简单的发布订阅
class EventEmitter {
constructor() {
// 事件对象,存放订阅的名字和事件
this.events = {};
}
// 订阅事件的方法
on(eventName, callback) {
if (!this.events[eventName]) {
// 注意数据,一个名字可以订阅多个事件函数
this.events[eventName] = [callback];
} else {
// 存在则push到指定数组的尾部保存
this.events[eventName].push(callback);
}
}
// 触发事件的方法
emit(eventName) {
// 遍历执行所有订阅的事件
this.events[eventName] && this.events[eventName].forEach((cb) => cb());
}
}