加深对 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 指向 objconst F = print.__bind(obj, 26);F(178); // mxin, 26, 178// new 状况const _obj = new F(145); // undefined, 26, 145console.log(_obj); // print {}// Function.prototype.bind()console.log('Function.prototype.bind()');const Fn = print.bind(obj, 26);Fn(178); // mxin, 26, 178const __obj = new Fn(145); // undefined, 26, 145console.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); // trueconsole.log(b instanceof Vehicle); // true// 继承一般构造函数function Person() {}class Engineer extends Person {}let e = new Engineer();console.log(e instanceof Engineer); // trueconsole.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); // trueconsole.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));// instanceofconsole.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')}`); // trueconsole.log(`__is('foo', 'bar'): ${__is('foo', 'bar')}`); // falseconst __foo = { a: 1 };const __bar = { a: 1 };console.log(`__is(__foo, __foo): ${__is(__foo, __foo)}`); // trueconsole.log(`__is(__foo, __bar): ${__is(__foo, __bar)}`); // falseconsole.log(`__is(window, window): ${__is(window, window)}`); // trueconsole.log(`__is([], []): ${__is([], [])}`); // falseconsole.log(`__is(null, null): ${__is(null, null)}`); // true// 特例console.log(`__is(0, -0): ${__is(0, -0)}`); // falseconsole.log(`__is(0, +0): ${__is(0, +0)}`); // trueconsole.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')}`); // trueconsole.log(`Object.is('foo', 'bar'): ${Object.is('foo', 'bar')}`); // falseconst foo = { a: 1 };const bar = { a: 1 };console.log(`Object.is(foo, foo): ${Object.is(foo, foo)}`); // trueconsole.log(`Object.is(foo, bar): ${Object.is(foo, bar)}`); // falseconsole.log(`Object.is(window, window): ${Object.is(window, window)}`); // trueconsole.log(`Object.is([], []): ${Object.is([], [])}`); // falseconsole.log(`Object.is(null, null): ${Object.is(null, null)}`); // true// 特例console.log(`Object.is(0, -0): ${Object.is(0, -0)}`); // falseconsole.log(`Object.is(0, +0): ${Object.is(0, +0)}`); // trueconsole.log(`Object.is(-0, -0): ${Object.is(-0, -0)}`); // trueconsole.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;}// __newconsole.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// newconsole.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 __finallynew __Promise((resolve, reject) => { resolve('mxin');}) .__then(res => { console.log('__resolve:', res); }) .__finally(() => { console.log('__resolve finally'); });// 测试 __reject __catch __finallynew __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]; }}// ------------------------------ 测试 ------------------------------// Eventconsole.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;}// ------------------------------ 测试 ------------------------------// curryconsole.log('curry()');console.log(curry(1)(2)(3)); // 6console.log(curry(1, 2, 3)(4)); // 10console.log(curry(1)(2)(3)(4)(5)); // 15console.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函数柯里化