加深对 JavaScript
的了解,学习并手写常见高频题目,了解实现原理
欢送大家指导,有不足之处会及时改良
点击此处传送至代码仓库
this
1. bind
/**
* 1. bind() 办法创立一个新的函数
* 2. 在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数
* 3. new 状况下疏忽第一个参数
* 4. 其余参数将作为新函数的参数,供调用时应用
* @param {object} ctx
* @param {...any} args
* @returns {function} 返回一个原函数的拷贝,并领有指定 this 值和初始参数
*/
Function.prototype.__bind = function (ctx, ...args) {
// 判断 this 是否为 function 类型
if (typeof this !== 'function') throw new TypeError('Error');
// 保留以后 this
const __this = this;
return function F() {
return this instanceof F
? new __this(...args, ...arguments) // new
: __this.apply(ctx, [...args, ...arguments]); // 间接调用时绑定 this
};
};
// ------------------------------ 测试 ------------------------------
function print() {console.log(this.name, ...arguments);
}
const obj = {name: 'mxin',};
// Function.prototype.__bind()
console.log('Function.prototype.__bind()');
// 间接调用,返回原函数拷贝,this 指向 obj
const F = print.__bind(obj, 26);
F(178); // mxin, 26, 178
// new 状况
const _obj = new F(145); // undefined, 26, 145
console.log(_obj); // print {}
// Function.prototype.bind()
console.log('Function.prototype.bind()');
const Fn = print.bind(obj, 26);
Fn(178); // mxin, 26, 178
const __obj = new Fn(145); // undefined, 26, 145
console.log(__obj); // print {}
2. call
/**
* 模仿 call
* 应用一个指定的 this 值和独自给出的一个或多个参数来调用一个函数
* @param {object} ctx
* @param {...any} args
* @returns {any} 调用 this 的返回值,若无有返回值,则返回 undefined
*/
Function.prototype.__call = function (ctx, ...args) {if (typeof this !== 'function') throw new TypeError('Error');
// 思考 null 状况,参数默认赋值会有效
if (!ctx) ctx = window;
// 将 this 函数保留在 ctx 上
ctx.fn = this;
// 传参执行并保留返回值
const res = ctx.fn(...args);
// 删除 ctx 上的 fn
delete ctx.fn;
return res;
};
// ------------------------------ 测试 ------------------------------
function Product(name, price) {
this.name = name;
this.price = price;
}
// Function.prototype.__call()
console.log('Function.prototype.__call()');
function Food(name, price) {Product.__call(this, name, price);
this.category = 'food';
}
const food = new Food('cheese', 5);
console.log(food);
// Food {name: "cheese", price: 5, category: "food"}
// category: "food"
// name: "cheese"
// price: 5
// __proto__:
// constructor: ƒ Food(name, price)
// __proto__: Object
// Function.prototype.call()
console.log('Function.prototype.call()');
function Toy(name, price) {Product.call(this, name, price);
this.category = 'toy';
}
const toy = new Toy('car', 10);
console.log(toy);
// Toy {name: "car", price: 10, category: "toy"}
// category: "toy"
// name: "car"
// price: 10
// __proto__:
// constructor: ƒ Toy(name, price)
// __proto__: Object
3. apply
/**
* 模仿 apply
* 调用一个具备给定 this 值的函数,以及以一个数组(或类数组对象)的模式提供的参数
* @param {object} ctx
* @param {} args
*/
Function.prototype.__apply = function (ctx, args) {if (typeof this !== 'function') throw new TypeError('Error');
// 思考 null 状况,参数默认赋值会有效
if (!ctx) ctx = window;
// 将 this 函数保留在 ctx 上
ctx.fn = this;
// 传参执行并保留返回值
const result = ctx.fn(...args);
// 删除 ctx 上的 fn
delete ctx.fn;
return result;
};
// ------------------------------ 测试 ------------------------------
const numbers = [5, 6, 2, 3, 7];
// Function.prototype.__apply()
console.log('Function.prototype.__apply()');
const max = Math.max.__apply(null, numbers);
console.log(max); // 7
// Function.prototype.apply()
console.log('Function.prototype.apply()');
const min = Math.min.apply(null, numbers);
console.log(min); // 2
原型
1. 继承
例举几种比拟罕用的继承形式
/**
* 应用 extends 继承
*/
// 继承类
class Vehicle {}
class Bus extends Vehicle {}
let b = new Bus();
console.log(b instanceof Bus); // true
console.log(b instanceof Vehicle); // true
// 继承一般构造函数
function Person() {}
class Engineer extends Person {}
let e = new Engineer();
console.log(e instanceof Engineer); // true
console.log(e instanceof Person); // true
/**
* 寄生式组合继承
*/
function Person(name) {this.name = name;}
function Man(name, age) {Person.call(this, name, age);
this.age = age;
}
Man.prototype = Object.create(Person.prototype);
Man.prototype.constructor = Man;
const man = new Man('mxin', 18);
console.log(man instanceof Man); // true
console.log(man instanceof Person); // true
2. instanceof
/**
* 模仿 instanceof
* 判断 obj.__proto__ 和 __constructor.prototype 是否相等
* @param {object} obj 实例对象
* @param {function} __constructor 构造函数
*/
function __instanceof(obj, __constructor) {
const prototype = __constructor.prototype;
obj = Object.getPrototypeOf(obj);
while (true) {if (obj === null) return false;
if (obj === prototype) return true;
obj = Object.getPrototypeOf(obj);
}
}
// ------------------------------ 测试 ------------------------------
function C() {}
function D() {}
const o = new C();
// __instanceof()
console.log('__instanceof()');
console.log(__instanceof(o, C));
console.log(__instanceof(o, D));
console.log(__instanceof(o, Object));
// instanceof
console.log('instanceof');
console.log(o instanceof C);
console.log(o instanceof D);
console.log(o instanceof Object);
对象
1. Objcet.create
/**
* 模仿 Object.create
* 创立一个新对象,应用现有的对象来提供新创建的对象的__proto__
* @param {object} prototype 新创建对象的原型对象,为 null 时 只能应用 Object.create()
* @param {object} properties 拜访器描述符,同 Object.defineProperties 第二个参数
* @returns {object}
*/
function __create(prototype, properties) {if (typeof prototype !== 'object') throw new TypeError('Error');
function Constructor() {}
Constructor.prototype = prototype;
const obj = new Constructor();
if (prototype) obj.constructor = Constructor;
// 设置拜访器描述符
if (properties) {if (typeof properties !== 'object') throw TypeError('Error');
Object.defineProperties(obj, properties);
}
return obj;
}
// ------------------------------ 测试 ------------------------------
const person = {
isHuman: false,
printIntroduction: function () {console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
},
};
// __create()
console.log('__create()');
const __me = __create(person);
__me.name = '__mxin';
__me.isHuman = true;
__me.printIntroduction();
// Object.create()
console.log('Object.create()');
const me = Object.create(person);
me.name = 'mxin';
me.isHuman = true;
me.printIntroduction();
// 目前创立污浊空对象只有 Object.create(null) 可行,无奈模仿
const emptyObj = Object.create(null);
console.log(emptyObj);
// {}
// No properties
2. Object.is
- 与
==
运算不同,==
运算符在判断相等前对两边的变量(如果它们不是同一类型) 进行强制转换 (这种行为的后果会将"" == false
判断为true
), 而Object.is
不会强制转换两边的值 - 与
===
运算也不雷同,===
运算符 (也包含==
运算符) 将数字-0
和+0
视为相等,而将Number.NaN
与NaN
视为不相等
/**
* 模仿 Object.is
* 判断两个值是否为同一个值
* 1. 都是 undefined
* 2. 都是 null
* 3. 都是 true 或 false
* 4. 都是雷同长度的字符串且雷同字符按雷同顺序排列
* 5. 都是雷同对象(意味着每个对象有同一个援用)* 6. 都是数字且
* a. 都是 +0
* b. 都是 -0
* c. 都是 NaN
* d. 或都是非零而且非 NaN 且为同一个值
* @param {*} x
* @param {*} y
*/
function __is(x, y) {if (x === y) {return x !== 0 || 1 / x === 1 / y;} else {return x !== x && y !== y;}
}
// ------------------------------ 测试 ------------------------------
// __is()
console.log('__is()');
console.log(`__is('foo', 'foo'): ${__is('foo', 'foo')}`); // true
console.log(`__is('foo', 'bar'): ${__is('foo', 'bar')}`); // false
const __foo = {a: 1};
const __bar = {a: 1};
console.log(`__is(__foo, __foo): ${__is(__foo, __foo)}`); // true
console.log(`__is(__foo, __bar): ${__is(__foo, __bar)}`); // false
console.log(`__is(window, window): ${__is(window, window)}`); // true
console.log(`__is([], []): ${__is([], [])}`); // false
console.log(`__is(null, null): ${__is(null, null)}`); // true
// 特例
console.log(`__is(0, -0): ${__is(0, -0)}`); // false
console.log(`__is(0, +0): ${__is(0, +0)}`); // true
console.log(`__is(-0, -0): ${__is(-0, -0)}`); // true
// console.log(`__is(NaN, 0 / 0): ${__is(NaN, 0 / 0)}`); // true
// Object.is()
console.log('Object.is()');
console.log(`Object.is('foo', 'foo'): ${Object.is('foo', 'foo')}`); // true
console.log(`Object.is('foo', 'bar'): ${Object.is('foo', 'bar')}`); // false
const foo = {a: 1};
const bar = {a: 1};
console.log(`Object.is(foo, foo): ${Object.is(foo, foo)}`); // true
console.log(`Object.is(foo, bar): ${Object.is(foo, bar)}`); // false
console.log(`Object.is(window, window): ${Object.is(window, window)}`); // true
console.log(`Object.is([], []): ${Object.is([], [])}`); // false
console.log(`Object.is(null, null): ${Object.is(null, null)}`); // true
// 特例
console.log(`Object.is(0, -0): ${Object.is(0, -0)}`); // false
console.log(`Object.is(0, +0): ${Object.is(0, +0)}`); // true
console.log(`Object.is(-0, -0): ${Object.is(-0, -0)}`); // true
console.log(`Object.is(NaN, 0 / 0): ${Object.is(NaN, 0 / 0)}`); // true
3. new
/**
* 模仿 new
* 1. 创立原型为 constructor.prototype 的新对象 obj
* 2. 执行构造函数,this 指向 obj
* 3. 判断构造函数返回值是否为对象,是就返回此对象
* 4. 构造函数无返回值返回 obj
* @param {function} constructor
* @param {...any} args
* @returns {object}
*/
function __new(constructor, ...args) {if (typeof constructor !== 'function') throw new TypeError('Error');
// 创立一个空对象,指定原型为 constructor.prototype
const obj = Object.create(constructor.prototype);
// 执行构造函数,绑定 this
const result = constructor.apply(obj, args);
// 如果构造函数返回值是一个对象,那么返回该对象,如果没有就返回 obj
return result && result instanceof Object ? result : obj;
}
// ------------------------------ 测试 ------------------------------
function Person(name, age) {
this.name = name;
this.age = age;
}
// __new
console.log('__new');
const __mxin = __new(Person, '__mxin', 18);
console.log(__mxin);
// Person {name: "__mxin", age: "18"}
// age: "18"
// name: "__mxin"
// __proto__:
// constructor: ƒ Person(name, age)
// __proto__: Object
// new
console.log('new');
const mxin = new Person('mxin', 18);
console.log(mxin);
// Person {name: "mxin", age: "18"}
// age: "18"
// name: "mxin"
// __proto__:
// constructor: ƒ Person(name, age)
// __proto__: Object
4. 浅拷贝
几种罕用形式:
- 自定义循环
- 开展运算符
- Object.assign()
/**
* 浅拷贝,无脑循环
* @param {*} targetObj
*/
function shallowClone(targetObj) {const resObj = {};
for (let key in targetObj) {resObj[key] = targetObj[key];
}
return resObj;
}
// ------------------------------ 测试 ------------------------------
console.log('shallowClone()');
const shallowObj = {
name: 'mxin',
age: 18,
};
/**
* 自定义办法
*/
const a = shallowClone(shallowObj);
a.name = '__mxin';
a.age = 20;
console.log('a', a);
// {name: "__mxin", age: 20}
// age: 20
// name: "__mxin"
/**
* 拓展运算符
*/
const b = {...a};
b.name = '____mxin';
b.age = 22;
console.log('b', b);
// {name: "____mxin", age: 22}
// age: 22
// name: "____mxin"
/**
* Object.assign()
*/
const c = Object.assign({}, shallowObj)
c.name = '______mxin';
c.age = 24;
console.log('c', c);
// {name: "______mxin", age: 24}
// age: 24
// name: "______mxin"
// 不影响原有对象
console.log('shallowObj', shallowObj);
// {name: "mxin", age: 18}
// age: 18
// name: "mxin"
5. 深拷贝
/**
* 深拷贝
* 深层克隆对象构造
* @param {object} target
* @returns {object}
*/
function deepClone(target) {
// 如果不是对象,间接返回自身
if (!isObject(target) || target === null) return target;
// 参数类型校验状况还有很多,没有笼罩全面,能够前期拓展
if (target instanceof Date) return new Date(target);
if (target instanceof RegExp) return new RegExp(target);
const obj = {};
const stack = [
{
parent: obj,
key: null,
data: target,
},
];
while (stack.length) {const node = stack.pop();
const parent = node.parent;
const key = node.key;
const data = node.data;
let res = key ? (parent[key] = {}) : parent;
for (const k in data) {if (data.hasOwnProperty(k)) {if (isObject(data[k])) {
stack.push({
parent: res,
key: k,
data: data[k],
});
} else {res[k] = data[k];
}
}
}
}
return obj;
}
/**
* 判断 target 是否为对象
* @param {*} target
*/
function isObject(target) {return Object.prototype.toString.call(target) === '[object Object]';
}
// ------------------------------ 测试 ------------------------------
console.log('deepClone()');
const deepObj = {
e: {
f: {
g: {h: 1,},
},
},
i: {
j: {
k: {l: 2,},
},
},
};
const d = deepClone(deepObj);
d.e.f.g.h = 2;
d.i.j.k.l = 4;
console.log('d', d);
// 不影响原有对象
console.log('deepObj', deepObj);
6. 对象扁平化
/**
* 对象扁平化
* 将多层嵌套的 key 合并
* @param {object} target
* @param {string} tempKey
* @param {object} res
* @returns {object}
*/
function flattenObject(target, tempKey = '', res = {}) {// 应用 Object.entries() 将键值对转换成数组,确保 key 与 val 的对应关系
for (const [key, val] of Object.entries(target)) {
// 如果 val 是对象,保留合并后的 key 进行递归
if (isObject(val)) {
const tmp = tempKey + key + '.';
flattenObject(val, tmp, res);
} else {
// 当 val 不是对象,合并 key 并对后果对象赋值
const tmp = tempKey + key;
res[tmp] = val;
}
}
return res;
}
/**
* 判断 target 是否为对象
* @param {*} target
*/
function isObject(target) {return Object.prototype.toString.call(target) === '[object Object]';
}
// ------------------------------ 测试 ------------------------------
console.log('flattenObject()');
const object = {
d: {
e: {
f: {
g: {h: 1,},
},
},
i: {
j: {
k: {l: 2,},
},
},
},
};
console.log(flattenObject(object));
// {
// d.e.f.g.h: 1
// d.i.j.k.l: 2
// }
数组
1. 数组扁平化
几种罕用形式:
- 递归
- Array.prototype.flat()
- Array.prototype.reduce()
/**
* 数组扁平化
* 判断数组中元素类型,如果是数组类型就递归,否则间接 push 到 res 中
* @param {array} target
* @param {array} res
* @returns {array}
*/
function flattenArray(target, res = []) {for (const val of target) {if (Array.isArray(val)) {flattenArray(val, res);
} else {res.push(val);
}
}
return res;
}
/**
* 应用 Array.prototype.reduce()
* @param {array} target
*/
function flattenArrayByReduce(target) {const initPre = [];
return target.reduce((pre, current) =>
pre.concat(Array.isArray(current) ? flattenArrayByReduce(current) : current
),
initPre
);
}
// ------------------------------ 测试 ------------------------------
console.log('flattenArray()');
const array = [[0], 1, [2, [3, [4, [5, [6]]]]], [7, [8]]];
/**
* 递归
*/
console.log(flattenArray(array));
// [0, 1, 2, 3, 4, 5, 6, 7, 8]
/**
* Array.prototype.flat()
*/
console.log(array.flat(Number.MAX_SAFE_INTEGER));
// [0, 1, 2, 3, 4, 5, 6, 7, 8]
/**
* Array.prototype.reduce()
*/
console.log(flattenArrayByReduce(array));
// [0, 1, 2, 3, 4, 5, 6, 7, 8]
2. 数组去重
- 应用
set
console.log([...new Set(array)]);
- 应用对象,或者将对象换成
map
,须要留神数组中元素的类型
/**
* 数组去重
* 基于对象实现,也能够应用 Map
* @param {array} target
* @returns {array}
*/
function removeDuplicate(target) {const temp = {};
for (let i = 0; i < target.length; i++) {const item = target[i];
if (Object.prototype.toString.call(item) !== '[object Object]' &&
Object.prototype.toString.call(item) !== '[object Function]' &&
Object.prototype.toString.call(item) !== '[object Symbol]' &&
Object.prototype.toString.call(item) !== '[object Array]'
) {if (temp.hasOwnProperty(item)) {target[i] = target[target.length - 1];
target.length--;
i--;
}
}
temp[item] = item;
}
return target;
}
// ------------------------------ 测试 ------------------------------
console.log('removeDuplicate()');
const array = [
1,
1,
'2',
'2',
true,
true,
false,
false,
undefined,
undefined,
null,
null,
Symbol('3'),
Symbol('3'),
{},
{},
[],
[],];
console.log(removeDuplicate(array));
异步编程
1. Promise
-
Promise
- resolve
- reject
- then
- catch
- finally
- Promise.resolve
- Promise.reject
- Promise.all
- promise.race
const isFunction = variable => typeof variable === 'function';
// 定义 Promise 的三种状态常量
const PENDING = 'pending';
const RESOLVE = 'resolved';
const REJECTED = 'rejected';
class __Promise {constructor(fn) {
this.__status = PENDING;
// 贮存 value,用于 __then 返回
this.__value = null;
// 失败队列,在 __then 时注入,resolve 时触发
this.__rejectedQueues = [];
// 胜利队列,在 __then 时注入,resolve 时触发
this.__resolvedQueues = [];
try {fn(this.__resolve, this.__reject);
} catch (err) {this.__reject(err);
}
}
__resolve = val => {const run = () => {if (this.__status !== PENDING) return;
this.__status = RESOLVE;
// 顺次执行胜利队列中的函数,并清空队列
const runResolved = value => {
let cb;
while ((cb = this.__resolvedQueues.shift())) {cb(value);
}
};
// 顺次执行失败队列中的函数,并清空队列
const runRejected = error => {
let cb;
while ((cb = this.__rejectedQueues.shift())) {cb(error);
}
};
/*
* 如果 resolve 的参数为 Promise 对象,* 则必须期待该 Promise 对象状态扭转后以后 Promsie 的状态才会扭转
* 且状态取决于参数 Promsie 对象的状态
*/
if (val instanceof __Promise) {
val.__then(
value => {
this.__value = value;
runResolved(value);
},
err => {
this.__value = err;
runRejected(err);
}
);
} else {
this.__value = val;
runResolved(val);
}
};
// 异步调用
setTimeout(run);
};
__reject = err => {if (this.__status !== PENDING) return;
const run = () => {
this.__status = REJECTED;
this.__value = err;
let cb;
while ((cb = this.__rejectedQueues.shift())) {cb(err);
}
};
setTimeout(run);
};
__then(onResolved, onRejected) {const { __value, __status} = this;
return new __Promise((onResolvedNext, onRejectedNext) => {
const resolved = value => {
try {if (!isFunction(onResolved)) {onResolvedNext(value);
} else {const res = onResolved(value);
if (res instanceof __Promise) {
// 如果以后回调函数返回__Promise 对象,必须期待其状态扭转后在执行下一个回调
res.__then(onResolvedNext, onRejectedNext);
} else {
// 否则会将返回后果间接作为参数,传入下一个 __then 的回调函数,并立刻执行下一个 __then 的回调函数
onResolvedNext(res);
}
}
} catch (err) {onRejectedNext(err);
}
};
const rejected = error => {
try {if (!isFunction(onRejected)) {onRejectedNext(error);
} else {const res = onRejected(error);
if (res instanceof __Promise) {res.__then(onResolvedNext, onRejectedNext);
} else {onResolvedNext(res);
}
}
} catch (err) {onRejectedNext(err);
}
};
if (__status === PENDING) {this.__resolvedQueues.push(resolved);
this.__rejectedQueues.push(rejected);
}
if (__status === RESOLVE) resolved(__value);
if (__status === REJECTED) rejected(__value);
});
}
__catch(onRejected) {return this.__then(null, onRejected);
}
__finally(cb) {
return this.__then(value => __Promise.resolve(cb()).__then(() => value),
reason =>
__Promise.resolve(cb()).__then(() => {throw new Error(reason);
})
);
}
static resolve(value) {
// 如果参数是 __Promise 实例,间接返回这个实例
if (value instanceof __Promise) return value;
return new __Promise(resolve => resolve(value));
}
static reject(value) {return new __Promise((resolve, reject) => reject(value));
}
static all(list) {return new __Promise((resolve, reject) => {const values = [];
let count = 0;
for (const [i, p] of list.entries()) {
// 数组参数如果不是 __Promise 实例,先调用 __Promise.resolve
this.resolve(p).__then(
res => {values[i] = res;
count++;
// 所有状态都变成 resolved 时返回的 __Promise 状态就变成 resolved
if (count === list.length) resolve(values);
},
err => {
// 有一个被 rejected 时返回的 __Promise 状态就变成 rejected
reject(err);
}
);
}
});
}
static race(list) {return new __Promise((resolve, reject) => {
list.forEach(p => {this.resolve(p).__then(
res => {resolve(res);
},
err => {reject(err);
}
);
});
});
}
}
// ------------------------------ 测试 ------------------------------
console.log('class __Promise {}');
const p1 = new __Promise((resolve, reject) =>
setTimeout(() => {resolve('mxin');
}, 500)
);
const p2 = new __Promise((resolve, reject) =>
setTimeout(() => {resolve('__mxin');
}, 200)
);
const p3 = new __Promise((resolve, reject) => {setTimeout(() => {reject(new Error('mxin3'));
}, 100);
});
// 测试 __resolve __then __finally
new __Promise((resolve, reject) => {resolve('mxin');
})
.__then(res => {console.log('__resolve:', res);
})
.__finally(() => {console.log('__resolve finally');
});
// 测试 __reject __catch __finally
new __Promise((resolve, reject) => {reject(new Error());
})
.__catch(e => {console.log('__reject:', e);
})
.__finally(() => {console.log('__reject finally');
});
// 测试 static resolve
__Promise
.resolve('mxin')
.__then(res => console.log('static resolve:', res))
.__finally(() => console.log('static resolve finally'));
// 测试 static reject
__Promise
.reject(new Error())
.__catch(res => console.log('static reject:', res))
.__finally(() => console.log('static reject finally'));
// 测试 all,可增加 p3 测试 rejected 状态
__Promise
.all([p1, p2])
.__then(res => console.log('all resolve:', res))
.__catch(e => console.log('all reject', e))
.__finally(() => console.log('all finally'));
// 测试 race,速度快的优先返回并完结, 增加 p3 优先 reject
__Promise
.race([p1, p2])
.__then(res => console.log('race resolve:', res))
.__catch(e => console.log('race reject', e))
.__finally(() => console.log('race finally'));
2. async/await
const NEXT = 'next';
const THROW = 'throw';
/**
* 模仿 async 函数
* 1.generator 宰割代码片段
* 2. 应用一个函数让其自迭代
* 3. 应用 promise 将 yield 包裹起来
* 4. 执行下一步的机会由 promise 来管制
* @param {*} fn
*/
function __async(fn) {return function () {
// 获取迭代器实例
const gen = fn.apply(this, arguments);
return new Promise((resolve, reject) => {
// 执行下一步
function _next(value) {__step(gen, resolve, reject, _next, _throw, NEXT, value);
}
// 抛异样
function _throw(err) {__step(gen, resolve, reject, _next, _throw, THROW, err);
}
// 首次触发
_next(void 0);
});
};
}
/**
* 执行迭代步骤,解决下次迭代后果
* 1. 将所有值 promise 化
* 2. 当 promise 执行完之后再执行下一步
* 3. 递归调用 next 函数,直到 done == true
*/
function __step(gen, resolve, reject, _next, _throw, key, arg) {
try {var info = gen[key](arg);
var value = info.value;
} catch (error) {return reject(error);
}
// 迭代实现
if (info.done) {resolve(value);
} else {Promise.resolve(value).then(_next, _throw);
}
}
// ------------------------------ 测试 ------------------------------
console.log('async');
__async(function* () {
const e = yield new Promise(resolve =>
setTimeout(() => {resolve('e');
}, 1000)
);
const a = yield Promise.resolve('a');
const d = yield 'd';
const b = yield Promise.resolve('b');
const c = yield Promise.resolve('c');
return [a, b, c, d, e];
})().then(res => console.log(res) // ['a', 'b', 'c', 'd', 'e']
);
3. 并发
/**
* 异步分片解决并发
* 1. 通过 limitNum 限度并发的 promise 数量
* 2. 长期后果保留到 resArr 中
* 3.start 返回 promise,全副执行结束 finally 中 resolve 最终后果
*/
class Limit {constructor(limitNum, promiseList) {this.resArr = [];
this.handling = 0;
this.resolvedNum = 0;
this.limitNum = limitNum;
this.promiseList = promiseList;
this.runTime = this.promiseList.length;
}
handle(promise) {console.log(promise, this.handling);
return new Promise((resolve, reject) => {promise.then(res => resolve(res)).catch(e => reject(e));
});
}
start() {
const __this = this;
return new Promise(resolve => {const run = () => {if (!__this.promiseList.length) return;
__this.handling += 1;
__this
.handle(__this.promiseList.shift())
.then(res => {__this.resArr.push(res);
})
.catch(e => {const error = new Error(e);
__this.resArr.push(error);
})
.finally(() => {
__this.handling -= 1;
__this.resolvedNum += 1;
if (__this.resolvedNum === __this.runTime) {resolve(__this.resArr);
}
run();});
};
for (let i = 1; i <= __this.limitNum; i++) {run();
}
});
}
}
// ------------------------------ 测试 ------------------------------
console.log('Limit');
const p1 = new Promise((resolve, reject) => {setTimeout(() => {resolve(1);
}, 1000);
});
const p2 = new Promise((resolve, reject) => {setTimeout(() => {resolve(2);
}, 1000);
});
const p3 = new Promise((resolve, reject) => {setTimeout(() => {reject(3);
}, 2000);
});
const p4 = new Promise((resolve, reject) => {setTimeout(() => {resolve(4);
}, 2000);
});
const p5 = new Promise((resolve, reject) => {setTimeout(() => {resolve(5);
}, 3000);
});
const p6 = new Promise((resolve, reject) => {setTimeout(() => {resolve(6);
}, 3000);
});
const promiseList = [p1, p2, p3, p4, p5, p6];
const limit = new Limit(2, promiseList);
limit.start().then(res => {console.log(res);
});
4. 公布 / 订阅
/**
* 事件订阅 / 公布
* 1.on 收集 key 对应的回调函数依赖关系,存入 eventList
* 2.emit 依据第一个参数判断 key 值,并执行其函数依赖
* 3.remove 依据 key 值清空依赖
*/
class __Event {constructor() {this.eventList = [];
}
on(key, fn) {if (!this.eventList[key]) this.eventList[key] = [];
this.eventList[key].push(fn);
}
emit() {const key = [].shift.call(arguments);
const fns = this.eventList[key];
if (!fns || fns.length === 0) return false;
for (const fn of fns) {fn.apply(this, arguments);
}
}
remove(key) {if (!this.eventList[key]) return false;
this.eventList[key] = null;
delete this.eventList[key];
}
}
// ------------------------------ 测试 ------------------------------
// Event
console.log('Event');
const __event = new __Event();
__event.on('name', val => {console.log(`info: ${val}`);
// info: mxin
});
__event.on('name', val => {console.log(`info2: ${val}`);
// info2: mxin
});
// 触发事件,下面两个回调执行对应代码
__event.emit('name', 'mxin');
// 移除事件
__event.remove('name');
// 事件被移除,不再触发
__event.emit('name', 'mxin');
技巧
1. 防抖
/**
* 防抖
* 事件高频触发,距离 wait 时长执行回调
* @param {*} fn
* @param {*} wait
*/
function debounce(fn, wait) {
let timeout;
return function () {
let __this = this,
args = arguments;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {fn.apply(__this, args);
}, wait);
};
}
// ------------------------------ 测试 ------------------------------
// debounce()
console.log('debounce()');
window.onresize = debounce(function () {console.log('扭转窗口大小结束 1000ms 后触执行');
}, 1000);
2. 节流
/**
* 节流
* 高频事件触发,距离 delay 工夫执行一次回调
* @param {*} fn
* @param {*} delay
*/
function throttle(fn, delay) {const prevTime = Date.now();
return function () {const curTime = Date.now();
if (curTime - prevTime > delay) {fn.apply(this, arguments);
prevTime = curTime;
}
};
}
// ------------------------------ 测试 ------------------------------
// throttle()
console.log('throttle()');
window.onresize = throttle(function () {console.log('距离 1000ms 执行一次');
}, 1000);
3. 柯里化
/**
* 柯里化
* 把承受多个参数的函数变换成承受一个繁多参数的函数
* 并返回承受余下的参数且返回后果的新函数
*/
function curry() {const args = [...arguments];
const fn = function () {args.push(...arguments);
return fn;
};
fn.toString = () => {return args.reduce((pre, current) => pre + current);
};
return fn;
}
// ------------------------------ 测试 ------------------------------
// curry
console.log('curry()');
console.log(curry(1)(2)(3)); // 6
console.log(curry(1, 2, 3)(4)); // 10
console.log(curry(1)(2)(3)(4)(5)); // 15
console.log(curry(2, 6)(1)); // 9
Vue
1. Reactive
参考 Vue 3.0
的实现形式
- reactive 创立响应式对象
- effect 副作用
- computed 计算属性
具体实现思路及演示能够看之前写过的一篇文章,点击传送
const reactiveMap = new WeakMap();
const targetMap = new WeakMap();
const effectStack = [];
/**
* 副作用函数
* @param {*} fn
*/
function effect(fn) {
try {
// 将须要执行的 effect 入栈
effectStack.push(fn);
// 执行该 effect,进入 proxy 的 get 拦挡
return fn();} finally {
// 依赖收集结束及所有 get 流程走完,以后 effect 出栈
effectStack.pop();}
}
/**
* 依赖收集
* @param {*} target
* @param {*} key
*/
function track(target, key) {
// 初始化依赖 Map
let depsMap = targetMap.get(target);
if (!depsMap) {targetMap.set(target, (depsMap = new Map()));
}
// 第二层依赖应用 Set 寄存 key 对应的 effect
let dep = depsMap.get(key);
if (!dep) {targetMap.get(target).set(key, (dep = new Set()));
}
// 取以后栈中的 effect 存入第二层依赖中
const activeEffect = effectStack[effectStack.length - 1];
activeEffect && dep.add(activeEffect);
}
/**
* 触发响应,执行 effect
* @param {*} target
* @param {*} key
*/
function trigger(target, key) {const depsMap = targetMap.get(target);
if (depsMap) {const effects = depsMap.get(key);
effects && effects.forEach(run => run());
}
}
/**
* 定义响应式对象,返回 proxy 代理对象
* @param {*} object
*/
function reactive(object) {if (reactiveMap.has(object)) return reactiveMap.get(object);
const proxy = new Proxy(object, handlers);
reactiveMap.set(object, proxy);
return proxy;
}
/**
* 处理器对象,定义捕捉器
*/
const handlers = {set(target, key) {Reflect.set(...arguments);
trigger(target, key);
},
get(target, key) {track(target, key);
return typeof target[key] === 'object'
? reactive(target[key])
: Reflect.get(...arguments);
},
};
/**
* 计算属性
* @param {*} fn
*/
function computed(fn) {
return {get value() {return effect(fn);
},
};
}
module.exports = {
effect,
reactive,
computed,
};
参考资料
- JavaScript 中各种源码实现(前端面试口试必备)
- 32 个手撕 JS,彻底解脱高级前端(面试高频)
- 详解 JS 函数柯里化