手写 apply
function apply(func, context, argsArray) {
// 检查 func 是否是函数
if (typeof func !== 'function') {
throw new TypeError('First argument must be a function');
}
// 如果没有传递上下文,则默认为全局对象
context = context || globalThis;
// 如果未提供参数数组,则默认为空数组
argsArray = argsArray || [];
// 创建一个唯一的键以避免与现有上下文属性冲突
const uniqueKey = Symbol();
// 在上下文中创建一个临时函数,以便我们可以调用 apply 方法
context[uniqueKey] = func;
// 调用函数并返回结果
const result = context[uniqueKey](...argsArray);
// 清理上下文中的临时函数
delete context[uniqueKey];
return result;
}
// 示例用法:
function add(a, b) {
return a + b;
}
const numbers = [2, 3];
const sum = apply(add, null, numbers);
console.log(sum); // 输出:5
手写 call
function call(func, context, ...args) {
// 检查 func 是否是函数
if (typeof func !== 'function') {
throw new TypeError('First argument must be a function');
}
// 如果没有传递上下文,则默认为全局对象
context = context || globalThis;
// 创建一个唯一的键以避免与现有上下文属性冲突
const uniqueKey = Symbol();
// 在上下文中创建一个临时函数,以便我们可以调用 apply 方法
context[uniqueKey] = func;
// 调用函数并返回结果
const result = context[uniqueKey](...args);
// 清理上下文中的临时函数
delete context[uniqueKey];
return result;
}
// 示例用法:
function greet(name) {
return `Hello, ${name}!`;
}
const greeting = call(greet, null, 'John');
console.log(greeting); // 输出:Hello, John!
手写 bind
Function.prototype.myBind = function (context, ...args) {
// 将当前函数保存在变量 fn 中
const fn = this;
// 如果未提供 args 参数,则将其设置为空数组
args = args ? args : [];
// 返回一个新的函数
return function newFn(...newFnArgs) {
// 如果新函数被用作构造函数(通过 new 关键字调用),则将 this 绑定到新创建的对象上,并调用原始函数 fn
if (this instanceof newFn) {
return new fn(...args, ...newFnArgs);
}
// 否则,使用 apply 方法将函数 fn 绑定到指定的上下文 context,并传入参数数组
return fn.apply(context, [...args, ...newFnArgs]);
};
};
寄生式组合继承
function Person(obj) {
this.name = obj.name
this.age = obj.age
}
Person.prototype.add = function(value){
console.log(value)
}
var p1 = new Person({name:"番茄", age: 18})
function Person1(obj) {
Person.call(this, obj)
this.sex = obj.sex
}
// 这一步是继承的关键
Person1.prototype = Object.create(Person.prototype);
Person1.prototype.constructor = Person1;
Person1.prototype.play = function(value){
console.log(value)
}
var p2 = new Person1({name:"鸡蛋", age: 118, sex: "男"})
手写 new
function myNew(constructor, ...args) {
// 创建一个新对象,并将其原型指向构造函数的原型
const instance = Object.create(constructor.prototype);
// 调用构造函数,并将新对象作为上下文(this)
const result = constructor.apply(instance, args);
// 如果构造函数返回了一个对象,则返回该对象,否则返回新对象
return Object(result) === result ? result : instance;
}
手写 instanceof的实现
function myInstanceof(obj, constructor) {
// 如果 obj 或 constructor 不是对象,则返回 false
if (typeof obj !== 'object' || obj === null || typeof constructor !== 'function') {
return false;
}
// 获取 constructor 的原型对象
let proto = Object.getPrototypeOf(obj);
// 循环查找原型链
while (proto !== null) {
// 如果 constructor 的原型对象出现在原型链中,则返回 true
if (proto === constructor.prototype) {
return true;
}
// 继续向上查找原型链
proto = Object.getPrototypeOf(proto);
}
// 如果未找到 constructor 的原型对象,则返回 false
return false;
}
Object.create()的实现
function myCreate(proto, propertiesObject) {
// 如果 proto 不是对象或者为 null,则抛出 TypeError
if (typeof proto !== 'object' && proto !== null) {
throw new TypeError('Object prototype may only be an Object or null');
}
// 创建一个新的空对象
const obj = {};
// 将新对象的原型设置为 proto
Object.setPrototypeOf(obj, proto);
// 如果提供了属性描述符对象,则定义属性到新对象上
if (propertiesObject !== undefined) {
Object.defineProperties(obj, propertiesObject);
}
// 返回新创建的对象
return obj;
}
Object.assign
function myAssign(target, ...sources) {
// 如果目标对象为 null 或 undefined,则抛出错误
if (target == null) {
throw new TypeError('Cannot convert undefined or null to object');
}
// 将目标对象转换为对象
const to = Object(target);
// 遍历每个源对象
sources.forEach(source => {
// 如果源对象为 null 或 undefined,则跳过
if (source != null) {
// 遍历源对象的所有可枚举属性
for (const key in source) {
// 检查源对象是否具有该属性
if (Object.prototype.hasOwnProperty.call(source, key)) {
// 将源对象的属性复制到目标对象中
to[key] = source[key];
}
}
}
});
// 返回目标对象
return to;
}
Promise的实现
Promise 是链条式的,一个接一个,所以 _handle 就会先处理,即便没有处理函数,也会执行自身的 resolve 传递到下一个上,而 then 就是保持一个接一个主因,return 以后,下一个 then 会挂在自己身上
class Promise {
callbacks = [];
state = 'pending';//增加状态
value = null;//保存结果
constructor(fn) {
fn(this._resolve.bind(this), this._reject.bind(this));
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
this._handle({
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject
});
});
}
_handle(callback) {
if (this.state === 'pending') {
this.callbacks.push(callback);
return;
}
let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;
if (!cb) {//如果then中没有传递任何东西
cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
cb(this.value);
return;
}
let ret = cb(this.value);
cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
cb(ret);
}
_resolve(value) {
if (value && (typeof value === 'object' || typeof value === 'function')) {
var then = value.then;
if (typeof then === 'function') {
then.call(value, this._resolve.bind(this), this._reject.bind(this));
return;
}
}
this.state = 'fulfilled';//改变状态
this.value = value;//保存结果
this.callbacks.forEach(callback => this._handle(callback));
}
_reject(error) {
this.state = 'rejected';
this.value = error;
this.callbacks.forEach(callback => this._handle(callback));
}
}
Promise.resolve
class Promise {
// 其他代码...
static resolve(value) {
if (value instanceof Promise) {
// 如果传入的值是一个 Promise 对象,则直接返回该 Promise 对象
return value;
}
// 如果传入的值不是一个 Promise 对象,则创建一个新的 Promise 对象
return new Promise((resolve) => {
resolve(value);
});
}
// 其他代码...
}
Promise.reject
Promise.reject = function(reason) {
return new Promise((resolve, reject) => reject(reason))
}
Promise.all
Promise.all = function(promiseArr) {
let index = 0, result = []
return new Promise((resolve, reject) => {
promiseArr.forEach((p, i) => {
Promise.resolve(p).then(val => {
index++
result[i] = val
if (index === promiseArr.length) {
resolve(result)
}
}, err => {
reject(err)
})
})
})
}
Promise.race
Promise.race = function(promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach(p => {
Promise.resolve(p).then(val => {
resolve(val)
}, err => {
rejecte(err)
})
})
})
}
Promise.allSettled
Promise.allSettled = function(promises) {
return new Promise(resolve => {
const results = [];
let settledCount = 0;
for (let i = 0; i < promises.length; i++) {
const promise = promises[i];
Promise.resolve(promise)
.then(value => {
results[i] = { status: "fulfilled", value };
})
.catch(reason => {
results[i] = { status: "rejected", reason };
})
.finally(() => {
settledCount++;
if (settledCount === promises.length) {
resolve(results);
}
});
}
});
};
Promise.any
Promise.any = function(promises) {
return new Promise((resolve, reject) => {
const errors = [];
let settledCount = 0;
for (let i = 0; i < promises.length; i++) {
const promise = promises[i];
Promise.resolve(promise)
.then(value => {
resolve(value);
})
.catch(reason => {
errors.push(reason);
settledCount++;
if (settledCount === promises.length) {
reject(new AggregateError("All promises were rejected", errors));
}
});
}
});
};
防抖
let debounce = (fn,time = 1000) => {
let timeLock = null
return function (...args){
clearTimeout(timeLock)
timeLock = setTimeout(()=>{
fn(...args)
},time)
}
}
实现节流函数(throttle)
function throttle(func, delay) {
let lastCall = 0;
return function (...args) {
const now = new Date().getTime();
if (now - lastCall < delay) return;
lastCall = now;
func(...args);
};
}
深拷贝(deepclone)
function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
// 处理循环引用的情况
if (hash.has(obj)) {
return hash.get(obj);
}
// 处理 RegExp 类型
if (obj instanceof RegExp) {
return new RegExp(obj);
}
// 处理 Date 类型
if (obj instanceof Date) {
return new Date(obj);
}
// 创建一个新的对象或数组来存储克隆后的数据
let clone = Array.isArray(obj) ? [] : new obj.constructor();
// 存储到 WeakMap,避免循环引用
hash.set(obj, clone);
// 遍历原始对象的所有属性或者数组的所有元素,进行递归克隆
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], hash);
}
}
// 考虑 Symbol 的情况
let symbolObj = Object.getOwnPropertySymbols(obj);
for (let i = 0; i < symbolObj.length; i++) {
if (obj.hasOwnProperty(symbolObj[i])) {
clone[symbolObj[i]] = deepClone(obj[symbolObj[i]], hash);
}
}
return clone;
}
数组扁平化的实现(flat)
let arr = [1,2,[3,4,[5,[6]]]]
console.log(arr.flat(Infinity))//flat参数为指定要提取嵌套数组的结构深度,默认值为 1
//用reduce实现
function fn(arr){
return arr.reduce((prev,cur)=>{
return prev.concat(Array.isArray(cur)?fn(cur):cur)
},[])
}
函数柯里化
fn.length 是 JavaScript 函数对象的一个属性,它返回了函数声明时的形参个数。
function sumFn(a,b,c){return a+ b + c};
let sum = curry(sumFn);
sum(2)(3)(5)//10
sum(2,3)(5)//10
function curry(fn,...args){
let fnLen = fn.length,
argsLen = args.length;
//对比函数的参数和当前传入参数
//若参数不够就继续递归返回curry
//若参数够就调用函数返回相应的值
if(fnLen > argsLen){
return function(...arg2s){
return curry(fn,...args,...arg2s)
}
}else{
return fn(...args)
}
}
手写一个 jsonp
function jsonp(url, callbackName, callback) {
// 使用 new URL 构建 URL 对象
const urlObject = new URL(url);
// 设置查询参数 callback
urlObject.searchParams.set('callback', callbackName);
// 创建 script 标签
const script = document.createElement('script');
// 设置 script 标签的 src 属性为构建好的 URL
script.src = urlObject.toString();
// 将 script 标签添加到页面中
document.body.appendChild(script);
// 定义全局回调函数
window[callbackName] = function(data) {
// 执行回调函数,并传入从服务器返回的数据
callback(data);
// 移除 script 标签
document.body.removeChild(script);
// 删除全局回调函数
delete window[callbackName];
};
}
20.EventEmitter 实现
class EventEmitter {
constructor() {
// 存储事件及其对应的回调函数
this.events = {};
}
// 订阅事件
on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(callback);
}
// 发布事件
emit(eventName, ...args) {
if (this.events[eventName]) {
this.events[eventName].forEach(callback => {
callback.apply(this, args);
});
}
}
// 移除事件的指定回调函数
off(eventName, callback) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(cb => cb !== callback);
}
}
// 移除指定事件的所有回调函数
removeAllListeners(eventName) {
delete this.events[eventName];
}
}
模板引擎实现
let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let data = {
name: '姓名',
age: 18
}
render(template, data); // 我是姓名,年龄18,性别undefined
function render(template, data) {
const reg = /\{\{(\w+)\}\}/; // 模板字符串正则
if (reg.test(template)) { // 判断模板里是否有模板字符串
const name = reg.exec(template)[1]; // 查找当前模板里第一个模板字符串的字段
template = template.replace(reg, data[name]); // 将第一个模板字符串渲染
return render(template, data); // 递归的渲染并返回渲染后的结构
}
return template; // 如果模板没有模板字符串直接返回
}