给定两个数组,写一个办法来计算它们的交加
例如:给定 nums1 = [1, 2, 2, 1],nums2 = [2, 2],返回 [2, 2]。
function union (arr1, arr2) { return arr1.filter(item => { return arr2.indexOf(item) > - 1; })} const a = [1, 2, 2, 1]; const b = [2, 3, 2]; console.log(union(a, b)); // [2, 2]
手写 Promise.then
then
办法返回一个新的 promise
实例,为了在 promise
状态发生变化时(resolve
/ reject
被调用时)再执行 then
里的函数,咱们应用一个 callbacks
数组先把传给then的函数暂存起来,等状态扭转时再调用。
那么,怎么保障后一个 **then**
里的办法在前一个 **then**
(可能是异步)完结之后再执行呢? 咱们能够将传给 then
的函数和新 promise
的 resolve
一起 push
到前一个 promise
的 callbacks
数组中,达到承前启后的成果:
- 承前:以后一个
promise
实现后,调用其resolve
变更状态,在这个resolve
里会顺次调用callbacks
里的回调,这样就执行了then
里的办法了 - 启后:上一步中,当
then
里的办法执行实现后,返回一个后果,如果这个后果是个简略的值,就间接调用新promise
的resolve
,让其状态变更,这又会顺次调用新promise
的callbacks
数组里的办法,周而复始。。如果返回的后果是个promise
,则须要等它实现之后再触发新promise
的resolve
,所以能够在其后果的then
里调用新promise
的resolve
then(onFulfilled, onReject){ // 保留前一个promise的this const self = this; return new MyPromise((resolve, reject) => { // 封装前一个promise胜利时执行的函数 let fulfilled = () => { try{ const result = onFulfilled(self.value); // 承前 return result instanceof MyPromise? result.then(resolve, reject) : resolve(result); //启后 }catch(err){ reject(err) } } // 封装前一个promise失败时执行的函数 let rejected = () => { try{ const result = onReject(self.reason); return result instanceof MyPromise? result.then(resolve, reject) : reject(result); }catch(err){ reject(err) } } switch(self.status){ case PENDING: self.onFulfilledCallbacks.push(fulfilled); self.onRejectedCallbacks.push(rejected); break; case FULFILLED: fulfilled(); break; case REJECT: rejected(); break; } }) }
留神:
- 间断多个
then
里的回调办法是同步注册的,但注册到了不同的callbacks
数组中,因为每次then
都返回新的promise
实例(参考下面的例子和图) - 注册实现后开始执行构造函数中的异步事件,异步实现之后顺次调用
callbacks
数组中提前注册的回调
实现每隔一秒打印 1,2,3,4
// 应用闭包实现for (var i = 0; i < 5; i++) { (function(i) { setTimeout(function() { console.log(i); }, i * 1000); })(i);}// 应用 let 块级作用域for (let i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, i * 1000);}
实现非负大整数相加
JavaScript对数值有范畴的限度,限度如下:
Number.MAX_VALUE // 1.7976931348623157e+308Number.MAX_SAFE_INTEGER // 9007199254740991Number.MIN_VALUE // 5e-324Number.MIN_SAFE_INTEGER // -9007199254740991
如果想要对一个超大的整数(> Number.MAX_SAFE_INTEGER
)进行加法运算,然而又想输入个别模式,那么应用 + 是无奈达到的,一旦数字超过 Number.MAX_SAFE_INTEGER
数字会被立刻转换为迷信计数法,并且数字精度相比以前将会有误差。
实现一个算法进行大数的相加:
function sumBigNumber(a, b) { let res = ''; let temp = 0; a = a.split(''); b = b.split(''); while (a.length || b.length || temp) { temp += ~~a.pop() + ~~b.pop(); res = (temp % 10) + res; temp = temp > 9 } return res.replace(/^0+/, '');}
其次要的思路如下:
- 首先用字符串的形式来保留大数,这样数字在数学示意上就不会发生变化
- 初始化res,temp来保留两头的计算结果,并将两个字符串转化为数组,以便进行每一位的加法运算
- 将两个数组的对应的位进行相加,两个数相加的后果可能大于10,所以可能要仅为,对10进行取余操作,将后果保留在以后位
- 判断以后位是否大于9,也就是是否会进位,若是则将temp赋值为true,因为在加法运算中,true会主动隐式转化为1,以便于下一次相加
- 反复上述操作,直至计算完结
将数字每千分位用逗号隔开
数字有小数版本:
let format = n => { let num = n.toString() // 转成字符串 let decimals = '' // 判断是否有小数 num.indexOf('.') > -1 ? decimals = num.split('.')[1] : decimals let len = num.length if (len <= 3) { return num } else { let temp = '' let remainder = len % 3 decimals ? temp = '.' + decimals : temp if (remainder > 0) { // 不是3的整数倍 return num.slice(0, remainder) + ',' + num.slice(remainder, len).match(/\d{3}/g).join(',') + temp } else { // 是3的整数倍 return num.slice(0, len).match(/\d{3}/g).join(',') + temp } }}format(12323.33) // '12,323.33'
数字无小数版本:
let format = n => { let num = n.toString() let len = num.length if (len <= 3) { return num } else { let remainder = len % 3 if (remainder > 0) { // 不是3的整数倍 return num.slice(0, remainder) + ',' + num.slice(remainder, len).match(/\d{3}/g).join(',') } else { // 是3的整数倍 return num.slice(0, len).match(/\d{3}/g).join(',') } }}format(1232323) // '1,232,323'
Function.prototype.bind
Function.prototype.bind = function(context, ...args) { if (typeof this !== 'function') { throw new Error("Type Error"); } // 保留this的值 var self = this; return function F() { // 思考new的状况 if(this instanceof F) { return new self(...args, ...arguments) } return self.apply(context, [...args, ...arguments]) }}
模板引擎实现
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; // 如果模板没有模板字符串间接返回}
参考:前端手写面试题具体解答
实现Promise
var PromisePolyfill = (function () { // 和reject不同的是resolve须要尝试开展thenable对象 function tryToResolve (value) { if (this === value) { // 次要是避免上面这种状况 // let y = new Promise(res => setTimeout(res(y))) throw TypeError('Chaining cycle detected for promise!') } // 依据标准2.32以及2.33 对对象或者函数尝试开展 // 保障S6之前的 polyfill 也能和ES6的原生promise混用 if (value !== null && (typeof value === 'object' || typeof value === 'function')) { try { // 这里记录这次then的值同时要被try包裹 // 次要起因是 then 可能是一个getter, 也也就是说 // 1. value.then可能报错 // 2. value.then可能产生副作用(例如屡次执行可能后果不同) var then = value.then // 另一方面, 因为无奈保障 then 的确会像预期的那样只调用一个onFullfilled / onRejected // 所以减少了一个flag来避免resolveOrReject被屡次调用 var thenAlreadyCalledOrThrow = false if (typeof then === 'function') { // 是thenable 那么尝试开展 // 并且在该thenable状态扭转之前this对象的状态不变 then.bind(value)( // onFullfilled function (value2) { if (thenAlreadyCalledOrThrow) return thenAlreadyCalledOrThrow = true tryToResolve.bind(this, value2)() }.bind(this), // onRejected function (reason2) { if (thenAlreadyCalledOrThrow) return thenAlreadyCalledOrThrow = true resolveOrReject.bind(this, 'rejected', reason2)() }.bind(this) ) } else { // 领有then 然而then不是一个函数 所以也不是thenable resolveOrReject.bind(this, 'resolved', value)() } } catch (e) { if (thenAlreadyCalledOrThrow) return thenAlreadyCalledOrThrow = true resolveOrReject.bind(this, 'rejected', e)() } } else { // 根本类型 间接返回 resolveOrReject.bind(this, 'resolved', value)() } } function resolveOrReject (status, data) { if (this.status !== 'pending') return this.status = status this.data = data if (status === 'resolved') { for (var i = 0; i < this.resolveList.length; ++i) { this.resolveList[i]() } } else { for (i = 0; i < this.rejectList.length; ++i) { this.rejectList[i]() } } } function Promise (executor) { if (!(this instanceof Promise)) { throw Error('Promise can not be called without new !') } if (typeof executor !== 'function') { // 非标准 但与Chrome谷歌保持一致 throw TypeError('Promise resolver ' + executor + ' is not a function') } this.status = 'pending' this.resolveList = [] this.rejectList = [] try { executor(tryToResolve.bind(this), resolveOrReject.bind(this, 'rejected')) } catch (e) { resolveOrReject.bind(this, 'rejected', e)() } } Promise.prototype.then = function (onFullfilled, onRejected) { // 返回值穿透以及谬误穿透, 留神谬误穿透用的是throw而不是return,否则的话 // 这个then返回的promise状态将变成resolved即接下来的then中的onFullfilled // 会被调用, 然而咱们想要调用的是onRejected if (typeof onFullfilled !== 'function') { onFullfilled = function (data) { return data } } if (typeof onRejected !== 'function') { onRejected = function (reason) { throw reason } } var executor = function (resolve, reject) { setTimeout(function () { try { // 拿到对应的handle函数解决this.data // 并以此为根据解析这个新的Promise var value = this.status === 'resolved' ? onFullfilled(this.data) : onRejected(this.data) resolve(value) } catch (e) { reject(e) } }.bind(this)) } // then 承受两个函数返回一个新的Promise // then 本身的执行永远异步与onFullfilled/onRejected的执行 if (this.status !== 'pending') { return new Promise(executor.bind(this)) } else { // pending return new Promise(function (resolve, reject) { this.resolveList.push(executor.bind(this, resolve, reject)) this.rejectList.push(executor.bind(this, resolve, reject)) }.bind(this)) } } // for prmise A+ test Promise.deferred = Promise.defer = function () { var dfd = {} dfd.promise = new Promise(function (resolve, reject) { dfd.resolve = resolve dfd.reject = reject }) return dfd } // for prmise A+ test if (typeof module !== 'undefined') { module.exports = Promise } return Promise})()PromisePolyfill.all = function (promises) { return new Promise((resolve, reject) => { const result = [] let cnt = 0 for (let i = 0; i < promises.length; ++i) { promises[i].then(value => { cnt++ result[i] = value if (cnt === promises.length) resolve(result) }, reject) } })}PromisePolyfill.race = function (promises) { return new Promise((resolve, reject) => { for (let i = 0; i < promises.length; ++i) { promises[i].then(resolve, reject) } })}
手写 instanceof 办法
instanceof 运算符用于判断构造函数的 prototype 属性是否呈现在对象的原型链中的任何地位。
实现步骤:
- 首先获取类型的原型
- 而后取得对象的原型
- 而后始终循环判断对象的原型是否等于类型的原型,直到对象原型为
null
,因为原型链最终为null
具体实现:
function myInstanceof(left, right) { let proto = Object.getPrototypeOf(left), // 获取对象的原型 prototype = right.prototype; // 获取构造函数的 prototype 对象 // 判断构造函数的 prototype 对象是否在对象的原型链上 while (true) { if (!proto) return false; if (proto === prototype) return true; proto = Object.getPrototypeOf(proto); }}
Object.is
Object.is
解决的次要是这两个问题:
+0 === -0 // trueNaN === NaN // false
const is= (x, y) => { if (x === y) { // +0和-0应该不相等 return x !== 0 || y !== 0 || 1/x === 1/y; } else { return x !== x && y !== y; }}
实现千位分隔符
// 保留三位小数parseToMoney(1234.56); // return '1,234.56'parseToMoney(123456789); // return '123,456,789'parseToMoney(1087654.321); // return '1,087,654.321'
function parseToMoney(num) { num = parseFloat(num.toFixed(3)); let [integer, decimal] = String.prototype.split.call(num, '.'); integer = integer.replace(/\d(?=(\d{3})+$)/g, '$&,'); return integer + '.' + (decimal ? decimal : '');}
原生实现
function ajax() { let xhr = new XMLHttpRequest() //实例化,以调用办法 xhr.open('get', 'https://www.google.com') //参数2,url。参数三:异步 xhr.onreadystatechange = () => { //每当 readyState 属性扭转时,就会调用该函数。 if (xhr.readyState === 4) { //XMLHttpRequest 代理以后所处状态。 if (xhr.status >= 200 && xhr.status < 300) { //200-300申请胜利 let string = request.responseText //JSON.parse() 办法用来解析JSON字符串,结构由字符串形容的JavaScript值或对象 let object = JSON.parse(string) } } } request.send() //用于理论收回 HTTP 申请。不带参数为GET申请}
实现Promise相干办法
实现Promise的resolve
实现 resolve 静态方法有三个要点:
- 传参为一个
Promise
, 则间接返回它。 - 传参为一个
thenable
对象,返回的Promise
会追随这个对象,采纳它的最终状态作为本人的状态。 - 其余状况,间接返回以该值为胜利状态的
promise
对象。
Promise.resolve = (param) => { if(param instanceof Promise) return param; return new Promise((resolve, reject) => { if(param && param.then && typeof param.then === 'function') { // param 状态变为胜利会调用resolve,将新 Promise 的状态变为胜利,反之亦然 param.then(resolve, reject); }else { resolve(param); } })}
实现 Promise.reject
Promise.reject 中传入的参数会作为一个 reason 一成不变地往下传, 实现如下:
Promise.reject = function (reason) { return new Promise((resolve, reject) => { reject(reason); });}
实现 Promise.prototype.finally
后面的promise
不论胜利还是失败,都会走到finally
中,并且finally
之后,还能够持续then
(阐明它还是一个then办法是要害),并且会将初始的promise
值一成不变的传递给前面的then
.
Promise.prototype.finally最大的作用
finally
里的函数,无论如何都会执行,并会把后面的值一成不变传递给下一个then
办法中- 如果
finally
函数中有promise
等异步工作,会等它们全副执行结束,再联合之前的胜利与否状态,返回值
Promise.prototype.finally六大状况用法
// 状况1Promise.resolve(123).finally((data) => { // 这里传入的函数,无论如何都会执行 console.log(data); // undefined})// 状况2 (这里,finally办法相当于做了两头解决,起一个过渡的作用)Promise.resolve(123).finally((data) => { console.log(data); // undefined}).then(data => { console.log(data); // 123})// 状况3 (这里只有reject,都会走到下一个then的err中)Promise.reject(123).finally((data) => { console.log(data); // undefined}).then(data => { console.log(data);}, err => { console.log(err, 'err'); // 123 err})// 状况4 (一开始就胜利之后,会期待finally里的promise执行结束后,再把后面的data传递到下一个then中)Promise.resolve(123).finally((data) => { console.log(data); // undefined return new Promise((resolve, reject) => { setTimeout(() => { resolve('ok'); }, 3000) })}).then(data => { console.log(data, 'success'); // 123 success}, err => { console.log(err, 'err');})// 状况5 (尽管一开始胜利,然而只有finally函数中的promise失败了,就会把其失败的值传递到下一个then的err中)Promise.resolve(123).finally((data) => { console.log(data); // undefined return new Promise((resolve, reject) => { setTimeout(() => { reject('rejected'); }, 3000) })}).then(data => { console.log(data, 'success');}, err => { console.log(err, 'err'); // rejected err})// 状况6 (尽管一开始失败,然而也要等finally中的promise执行完,能力把一开始的err传递到err的回调中)Promise.reject(123).finally((data) => { console.log(data); // undefined return new Promise((resolve, reject) => { setTimeout(() => { resolve('resolve'); }, 3000) })}).then(data => { console.log(data, 'success');}, err => { console.log(err, 'err'); // 123 err})
源码实现
Promise.prototype.finally = function (callback) { return this.then((data) => { // 让函数执行 外部会调用办法,如果办法是promise,须要期待它实现 // 如果以后promise执行时失败了,会把err传递到,err的回调函数中 return Promise.resolve(callback()).then(() => data); // data 上一个promise的胜利态 }, err => { return Promise.resolve(callback()).then(() => { throw err; // 把之前的失败的err,抛出去 }); })}
实现 Promise.all
对于 all 办法而言,须要实现上面的外围性能:
- 传入参数为一个空的可迭代对象,则间接进行
resolve
。 - 如果参数中有一个
promise
失败,那么Promise.all
返回的promise
对象失败。 - 在任何状况下,
Promise.all
返回的promise
的实现状态的后果都是一个数组
Promise.all = function(promises) { return new Promise((resolve, reject) => { let result = []; let index = 0; let len = promises.length; if(len === 0) { resolve(result); return; } for(let i = 0; i < len; i++) { // 为什么不间接 promise[i].then, 因为promise[i]可能不是一个promise Promise.resolve(promise[i]).then(data => { result[i] = data; index++; if(index === len) resolve(result); }).catch(err => { reject(err); }) } })}
实现promise.allsettle
MDN:Promise.allSettled()
办法返回一个在所有给定的promise
都曾经
fulfilled或
rejected后的
promise,并带有一个对象数组,每个对象示意对应的
promise`后果
当您有多个彼此不依赖的异步工作胜利实现时,或者您总是想晓得每个promise
的后果时,通常应用它。
【译】Promise.allSettled
跟Promise.all
相似, 其参数承受一个Promise
的数组, 返回一个新的Promise
, 惟一的不同在于, 其不会进行短路, 也就是说当Promise全副解决实现后咱们能够拿到每个Promise
的状态, 而不论其是否解决胜利。
用法 | 测试用例
let fs = require('fs').promises;let getName = fs.readFile('./name.txt', 'utf8'); // 读取文件胜利let getAge = fs.readFile('./age.txt', 'utf8');Promise.allSettled([1, getName, getAge, 2]).then(data => { console.log(data);});// 输入后果/* [ { status: 'fulfilled', value: 1 }, { status: 'fulfilled', value: 'zf' }, { status: 'fulfilled', value: '11' }, { status: 'fulfilled', value: 2 } ]*/let getName = fs.readFile('./name123.txt', 'utf8'); // 读取文件失败let getAge = fs.readFile('./age.txt', 'utf8');// 输入后果/* [ { status: 'fulfilled', value: 1 }, { status: 'rejected', value: [Error: ENOENT: no such file or directory, open './name123.txt'] { errno: -2, code: 'ENOENT', syscall: 'open', path: './name123.txt' } }, { status: 'fulfilled', value: '11' }, { status: 'fulfilled', value: 2 } ]*/
实现
function isPromise (val) { return typeof val.then === 'function'; // (123).then => undefined}Promise.allSettled = function(promises) { return new Promise((resolve, reject) => { let arr = []; let times = 0; const setData = (index, data) => { arr[index] = data; if (++times === promises.length) { resolve(arr); } console.log('times', times) } for (let i = 0; i < promises.length; i++) { let current = promises[i]; if (isPromise(current)) { current.then((data) => { setData(i, { status: 'fulfilled', value: data }); }, err => { setData(i, { status: 'rejected', value: err }) }) } else { setData(i, { status: 'fulfilled', value: current }) } } })}
实现 Promise.race
race 的实现相比之下就简略一些,只有有一个 promise 执行完,间接 resolve 并进行执行
Promise.race = function(promises) { return new Promise((resolve, reject) => { let len = promises.length; if(len === 0) return; for(let i = 0; i < len; i++) { Promise.resolve(promise[i]).then(data => { resolve(data); return; }).catch(err => { reject(err); return; }) } })}
实现一个简版Promise
// 应用var promise = new Promise((resolve,reject) => { if (操作胜利) { resolve(value) } else { reject(error) }})promise.then(function (value) { // success},function (value) { // failure})
function myPromise(constructor) { let self = this; self.status = "pending" // 定义状态扭转前的初始状态 self.value = undefined; // 定义状态为resolved的时候的状态 self.reason = undefined; // 定义状态为rejected的时候的状态 function resolve(value) { if(self.status === "pending") { self.value = value; self.status = "resolved"; } } function reject(reason) { if(self.status === "pending") { self.reason = reason; self.status = "rejected"; } } // 捕捉结构异样 try { constructor(resolve,reject); } catch(e) { reject(e); }}
// 增加 then 办法myPromise.prototype.then = function(onFullfilled,onRejected) { let self = this; switch(self.status) { case "resolved": onFullfilled(self.value); break; case "rejected": onRejected(self.reason); break; default: }}var p = new myPromise(function(resolve,reject) { resolve(1)});p.then(function(x) { console.log(x) // 1})
应用class实现
class MyPromise { constructor(fn) { this.resolvedCallbacks = []; this.rejectedCallbacks = []; this.state = 'PENDING'; this.value = ''; fn(this.resolve.bind(this), this.reject.bind(this)); } resolve(value) { if (this.state === 'PENDING') { this.state = 'RESOLVED'; this.value = value; this.resolvedCallbacks.map(cb => cb(value)); } } reject(value) { if (this.state === 'PENDING') { this.state = 'REJECTED'; this.value = value; this.rejectedCallbacks.map(cb => cb(value)); } } then(onFulfilled, onRejected) { if (this.state === 'PENDING') { this.resolvedCallbacks.push(onFulfilled); this.rejectedCallbacks.push(onRejected); } if (this.state === 'RESOLVED') { onFulfilled(this.value); } if (this.state === 'REJECTED') { onRejected(this.value); } }}
Promise 实现-具体
- 能够把
Promise
看成一个状态机。初始是pending
状态,能够通过函数resolve
和reject
,将状态转变为resolved
或者rejected
状态,状态一旦扭转就不能再次变动。 then
函数会返回一个Promise
实例,并且该返回值是一个新的实例而不是之前的实例。因为Promise
标准规定除了pending
状态,其余状态是不能够扭转的,如果返回的是一个雷同实例的话,多个then
调用就失去意义了。- 对于
then
来说,实质上能够把它看成是flatMap
// 三种状态const PENDING = "pending";const RESOLVED = "resolved";const REJECTED = "rejected";// promise 接管一个函数参数,该函数会立刻执行function MyPromise(fn) { let _this = this; _this.currentState = PENDING; _this.value = undefined; // 用于保留 then 中的回调,只有当 promise // 状态为 pending 时才会缓存,并且每个实例至少缓存一个 _this.resolvedCallbacks = []; _this.rejectedCallbacks = []; _this.resolve = function (value) { if (value instanceof MyPromise) { // 如果 value 是个 Promise,递归执行 return value.then(_this.resolve, _this.reject) } setTimeout(() => { // 异步执行,保障执行程序 if (_this.currentState === PENDING) { _this.currentState = RESOLVED; _this.value = value; _this.resolvedCallbacks.forEach(cb => cb()); } }) }; _this.reject = function (reason) { setTimeout(() => { // 异步执行,保障执行程序 if (_this.currentState === PENDING) { _this.currentState = REJECTED; _this.value = reason; _this.rejectedCallbacks.forEach(cb => cb()); } }) } // 用于解决以下问题 // new Promise(() => throw Error('error)) try { fn(_this.resolve, _this.reject); } catch (e) { _this.reject(e); }}MyPromise.prototype.then = function (onResolved, onRejected) { var self = this; // 标准 2.2.7,then 必须返回一个新的 promise var promise2; // 标准 2.2.onResolved 和 onRejected 都为可选参数 // 如果类型不是函数须要疏忽,同时也实现了透传 // Promise.resolve(4).then().then((value) => console.log(value)) onResolved = typeof onResolved === 'function' ? onResolved : v => v; onRejected = typeof onRejected === 'function' ? onRejected : r => throw r; if (self.currentState === RESOLVED) { return (promise2 = new MyPromise(function (resolve, reject) { // 标准 2.2.4,保障 onFulfilled,onRjected 异步执行 // 所以用了 setTimeout 包裹下 setTimeout(function () { try { var x = onResolved(self.value); resolutionProcedure(promise2, x, resolve, reject); } catch (reason) { reject(reason); } }); })); } if (self.currentState === REJECTED) { return (promise2 = new MyPromise(function (resolve, reject) { setTimeout(function () { // 异步执行onRejected try { var x = onRejected(self.value); resolutionProcedure(promise2, x, resolve, reject); } catch (reason) { reject(reason); } }); })); } if (self.currentState === PENDING) { return (promise2 = new MyPromise(function (resolve, reject) { self.resolvedCallbacks.push(function () { // 思考到可能会有报错,所以应用 try/catch 包裹 try { var x = onResolved(self.value); resolutionProcedure(promise2, x, resolve, reject); } catch (r) { reject(r); } }); self.rejectedCallbacks.push(function () { try { var x = onRejected(self.value); resolutionProcedure(promise2, x, resolve, reject); } catch (r) { reject(r); } }); })); }};// 标准 2.3function resolutionProcedure(promise2, x, resolve, reject) { // 标准 2.3.1,x 不能和 promise2 雷同,防止循环援用 if (promise2 === x) { return reject(new TypeError("Error")); } // 标准 2.3.2 // 如果 x 为 Promise,状态为 pending 须要持续期待否则执行 if (x instanceof MyPromise) { if (x.currentState === PENDING) { x.then(function (value) { // 再次调用该函数是为了确认 x resolve 的 // 参数是什么类型,如果是根本类型就再次 resolve // 把值传给下个 then resolutionProcedure(promise2, value, resolve, reject); }, reject); } else { x.then(resolve, reject); } return; } // 标准 2.3.3.3.3 // reject 或者 resolve 其中一个执行过得话,疏忽其余的 let called = false; // 标准 2.3.3,判断 x 是否为对象或者函数 if (x !== null && (typeof x === "object" || typeof x === "function")) { // 标准 2.3.3.2,如果不能取出 then,就 reject try { // 标准 2.3.3.1 let then = x.then; // 如果 then 是函数,调用 x.then if (typeof then === "function") { // 标准 2.3.3.3 then.call( x, y => { if (called) return; called = true; // 标准 2.3.3.3.1 resolutionProcedure(promise2, y, resolve, reject); }, e => { if (called) return; called = true; reject(e); } ); } else { // 标准 2.3.3.4 resolve(x); } } catch (e) { if (called) return; called = true; reject(e); } } else { // 标准 2.3.4,x 为根本类型 resolve(x); }}
实现Promisify
const fs = require('fs')const path = require('path')// node中应用// const fs = require('fs').promises 12.18版// const promisify = require('util').promisify// 包装node api promise化 典型的高级函数const promisify = fn=>{ return (...args)=>{ return new Promise((resolve,reject)=>{ fn(...args, (err,data)=>{ if(err) { reject(err) } resolve(data) }) }) }}// const read = promisify(fs.readFile)// read(path.join(__dirname, './promise.js'), 'utf8').then(d=>{// console.log(d)// })// promise化node所有apiconst promisifyAll = target=>{ Reflect.ownKeys(target).forEach(key=>{ if(typeof target[key] === 'function') { target[key+'Async'] = promisify(target[key]) } }) return target}// promise化fs下的函数const promisifyNew = promisifyAll(fs)promisifyNew.readFileAsync(path.join(__dirname, './promise.js'), 'utf8').then(d=>{ console.log(d)})module.exports = { promisify, promisifyAll}
残缺实现Promises/A+标准
/** * Promises/A+标准 实现一个promise * https://promisesaplus.com/*/const EMUM = { PENDING: 'PENDING', FULFILLED: 'FULFILLED', REJECTED: 'REJECTED'}// x 返回值// promise2 then的时候new的promise// promise2的resolve, rejectconst resolvePromise = (x, promise2, resolve, reject)=>{ // 解析promise的值解析promise2是胜利还是失败 传递到上层then if(x === promise2) { reject(new TypeError('类型谬误')) } // 这里的x如果是一个promise的话 可能是其余的promise,可能调用了胜利 又调用了失败 // 避免resolve的时候 又throw err抛出异样到reject了 let called // 如果x是promise 那么就采纳他的状态 // 有then办法是promise if(typeof x === 'object' && typeof x!== null || typeof x === 'function') { // x是对象或函数 try { let then = x.then // 缓存,不必屡次取值 if(typeof then === 'function') { // 是promise,调用then办法外面有this,须要传入this为x能力取到then办法外面的值this.value then.call(x, y=>{// 胜利 // y值可能也是一个promise 如resolve(new Promise()) 此时的y==new Promise() // 递归解析y,直到拿到一般的值resolve(x进来) if(called) return; called = true; resolvePromise(y, promise2, resolve, reject) },r=>{// 一旦失败间接失败 if(called) return; called = true; reject(r) }) } else { // 一般对象不是promise resolve(x) } } catch (e) { // 对象取值可能报错,用defineProperty定义get 抛出异样 if(called) return; called = true; reject(e) } } else { // x是一般值 resolve(x) // 间接胜利 }}class myPromise { constructor(executor) { this.status = EMUM.PENDING // 以后状态 this.value = undefined // resolve接管值 this.reason = undefined // reject失败返回值 /** * 同一个promise能够then屡次(公布订阅模式) * 调用then时 以后状态是期待态,须要将以后胜利或失败的回调寄存起来(订阅) * 调用resolve时 将订阅函数进行执行(公布) */ // 胜利队列 this.onResolvedCallbacks = [] // 失败队列 this.onRejectedCallbacks = [] const resolve = value =>{ // 如果value是一个promise,须要递归解析 // 如 myPromise.resolve(new myPromise()) 须要解析value if(value instanceof myPromise) { // 不停的解析 直到值不是promise return value.then(resolve,reject) } if(this.status === EMUM.PENDING) { this.status = EMUM.FULFILLED this.value = value this.onResolvedCallbacks.forEach(fn=>fn()) } } const reject = reason =>{ if(this.status === EMUM.PENDING) { this.status = EMUM.REJECTED this.reason = reason this.onRejectedCallbacks.forEach(fn=>fn()) } } try { executor(resolve,reject) } catch(e) { reject(e) } } then(onFulFilled, onRejected) { // 透传 解决默认不传的状况 // new Promise((resolve,reject)=>{ // resolve(1) // }).then().then().then(d=>{}) // new Promise((resolve,reject)=>{ // resolve(1) // }).then(v=>v).then(v=>v).then(d=>{}) // new Promise((resolve,reject)=>{ // reject(1) // }).then().then().then(null, e=>{console.log(e)}) // new Promise((resolve,reject)=>{ // reject(1) // }).then(null,e=>{throw e}).then(null,e=>{throw e}).then(null,e=>{console.log(e)}) onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : v => v onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err} // 调用then 创立一个新的promise let promise2 = new myPromise((resolve,reject)=>{ // 依据value判断是resolve 还是reject value也可能是promise if(this.status === EMUM.FULFILLED) { setTimeout(() => { try { // 胜利回调后果 let x = onFulFilled(this.value) // 解析promise resolvePromise(x, promise2,resolve,reject) } catch (error) { reject(error) } }, 0); } if(this.status === EMUM.REJECTED) { setTimeout(() => { try { let x = onRejected(this.reason) // 解析promise resolvePromise(x, promise2,resolve,reject) } catch (error) { reject(error) } }, 0); } // 用户还未调用resolve或reject办法 if(this.status === EMUM.PENDING) { this.onResolvedCallbacks.push(()=>{ try { let x = onFulFilled(this.value) // 解析promise resolvePromise(x, promise2,resolve,reject) } catch (error) { reject(error) } }) this.onRejectedCallbacks.push(()=>{ try { let x = onRejected(this.reason) // 解析promise resolvePromise(x, promise2,resolve,reject) } catch (error) { reject(error) } }) } }) return promise2 } catch(errCallback) { // 等同于没有胜利,把失败放进去而已 return this.then(null, errCallback) } // myPromise.resolve 具备期待性能的 如果参数的promise会期待promise解析结束在向下执行 static resolve(val) { return new myPromise((resolve,reject)=>{ resolve(val) }) } // myPromise.reject 间接将值返回 static reject(reason) { return new myPromise((resolve,reject)=>{ reject(reason) }) } // finally传入的函数 无论胜利或失败都执行 // Promise.reject(100).finally(()=>{console.log(1)}).then(d=>console.log('success',d)).catch(er=>console.log('faild',er)) // Promise.reject(100).finally(()=>new Promise()).then(d=>console.log(d)).catch(er=>) finally(callback) { return this.then((val)=>{ return myPromise.resolve(callback()).then(()=>val) },(err)=>{ return myPromise.resolve(callback()).then(()=>{throw err}) }) } // Promise.all static all(values) { return new myPromise((resolve,reject)=>{ let resultArr = [] let orderIndex = 0 const processResultByKey = (value,index)=>{ resultArr[index] = value // 解决齐全部 if(++orderIndex === values.length) { resolve(resultArr) // 解决实现的后果返回去 } } for (let i = 0; i < values.length; i++) { const value = values[i]; // 是promise if(value && typeof value.then === 'function') { value.then((val)=>{ processResultByKey(val,i) },reject) } else { // 不是promise状况 processResultByKey(value,i) } } }) } static race(promises) { // 采纳最新胜利或失败的作为后果 return new myPromise((resolve,reject)=>{ for (let i = 0; i < promises.length; i++) { let val = promises[i] if(val && typeof val.then === 'function') { // 任何一个promise先调用resolve或reject就返回后果了 也就是返回执行最快的那个promise的后果 val.then(resolve,reject) }else{ // 一般值 resolve(val) } } }) }}/** * =====测试用例-==== */// let promise1 = new myPromise((resolve,reject)=>{// setTimeout(() => {// resolve('胜利')// }, 900);// })// promise1.then(val=>{// console.log('success', val)// },reason=>{// console.log('fail', reason)// })/** * then的应用形式 一般值象征不是promise * * 1、then中的回调有两个办法 胜利或失败 他们的后果返回(一般值)会传递给外层的下一个then中 * 2、能够在胜利或失败中抛出异样,走到下一次then的失败中 * 3、返回的是一个promsie,那么会用这个promise的状态作为后果,会用promise的后果向下传递 * 4、错误处理,会默认先找离本人最新的错误处理,找不到就向下查找,找打了就执行 */// read('./name.txt').then(data=>{// return '123'// }).then(data=>{// }).then(null,err=>{// })// // .catch(err=>{ // catch就是没有胜利的promise// // })/** * promise.then实现原理:通过每次返回一个新的promise来实现(promise一旦胜利就不能失败,失败就不能胜利) * */// function read(data) {// return new myPromise((resolve,reject)=>{// setTimeout(() => {// resolve(new myPromise((resolve,reject)=>resolve(data)))// }, 1000);// })// }// let promise2 = read({name: 'poetry'}).then(data=>{// return data// }).then().then().then(data=>{// console.log(data,'-data-')// },(err)=>{// console.log(err,'-err-')// })// finally测试// myPromise// .resolve(100)// .finally(()=>{// return new myPromise((resolve,reject)=>setTimeout(() => {// resolve(100)// }, 100))// })// .then(d=>console.log('finally success',d))// .catch(er=>console.log(er, 'finally err'))/** * promise.all 测试 * * myPromise.all 解决并发问题 多个异步并发获取最终的后果*/// myPromise.all([1,2,3,4,new myPromise((resolve,reject)=>{// setTimeout(() => {// resolve('ok1')// }, 1000);// }),new myPromise((resolve,reject)=>{// setTimeout(() => {// resolve('ok2')// }, 1000);// })]).then(d=>{// console.log(d,'myPromise.all.resolve')// }).catch(err=>{// console.log(err,'myPromise.all.reject')// })// 实现promise中断请求let promise = new Promise((resolve,reject)=>{ setTimeout(() => { // 模仿接口调用 ajax调用超时 resolve('胜利') }, 10000);})function promiseWrap(promise) { // 包装一个promise 能够管制原来的promise是胜利 还是失败 let abort let newPromsie = new myPromise((resolve,reject)=>{ abort = reject }) // 只有管制newPromsie失败,就能够管制被包装的promise走向失败 // Promise.race 任何一个先胜利或者失败 就能够取得后果 let p = myPromise.race([promise, newPromsie]) p.abort = abort return p}let newPromise = promiseWrap(promise)setTimeout(() => { // 超过3秒超时 newPromise.abort('申请超时')}, 3000);newPromise.then(d=>{ console.log('d',d)}).catch(err=>{ console.log('err',err)})// 应用promises-aplus-tests 测试写的promise是否标准// 全局装置 cnpm i -g promises-aplus-tests// 命令行执行 promises-aplus-tests promise.js// 测试入口 产生提早对象myPromise.defer = myPromise.deferred = function () { let dfd = {} dfd.promise = new myPromise((resolve,reject)=>{ dfd.resolve = resolve dfd.reject = reject }) return dfd}// 提早对象用户// // promise解决嵌套问题// function readData(url) {// let dfd = myPromise.defer()// fs.readFile(url, 'utf8', function (err,data) {// if(err) {// dfd.reject()// }// dfd.resolve(data)// })// return dfd.promise// }// readData().then(d=>{// return d// })module.exports = myPromise
Array.prototype.forEach()
Array.prototype.forEach = function(callback, thisArg) { if (this == null) { throw new TypeError('this is null or not defined'); } if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function'); } const O = Object(this); const len = O.length >>> 0; let k = 0; while (k < len) { if (k in O) { callback.call(thisArg, O[k], k, O); } k++; }}
instanceof
instanceof
运算符用于检测构造函数的prototype
属性是否呈现在某个实例对象的原型链上。
const myInstanceof = (left, right) => { // 根本数据类型都返回false if (typeof left !== 'object' || left === null) return false; let proto = Object.getPrototypeOf(left); while (true) { if (proto === null) return false; if (proto === right.prototype) return true; proto = Object.getPrototypeOf(proto); }}
实现一个JS函数柯里化
事后解决的思维,利用闭包的机制
- 柯里化的定义:接管一部分参数,返回一个函数接管残余参数,接管足够参数后,执行原函数
- 函数柯里化的次要作用和特点就是
参数复用
、提前返回
和提早执行
- 柯里化把屡次传入的参数合并,柯里化是一个高阶函数
- 每次都返回一个新函数
- 每次入参都是一个
当柯里化函数接管到足够参数后,就会执行原函数,如何去确定何时达到足够的参数呢?
有两种思路:
- 通过函数的
length
属性,获取函数的形参个数,形参的个数就是所需的参数个数 - 在调用柯里化工具函数时,手动指定所需的参数个数
将这两点联合一下,实现一个简略 curry
函数
通用版
// 写法1function curry(fn, args) { var length = fn.length; var args = args || []; return function(){ newArgs = args.concat(Array.prototype.slice.call(arguments)); if (newArgs.length < length) { return curry.call(this,fn,newArgs); }else{ return fn.apply(this,newArgs); } }}
// 写法2// 分批传入参数// redux 源码的compose也是用了相似柯里化的操作const curry = (fn, arr = []) => {// arr就是咱们要收集每次调用时传入的参数 let len = fn.length; // 函数的长度,就是参数的个数 return function(...args) { let newArgs = [...arr, ...args] // 收集每次传入的参数 // 如果传入的参数个数等于咱们指定的函数参数个数,就执行指定的真正函数 if(newArgs.length === len) { return fn(...newArgs) } else { // 递归收集参数 return curry(fn, newArgs) } }}
// 测试function multiFn(a, b, c) { return a * b * c;}var multi = curry(multiFn);multi(2)(3)(4);multi(2,3,4);multi(2)(3,4);multi(2,3)(4)
ES6写法
const curry = (fn, arr = []) => (...args) => ( arg => arg.length === fn.length ? fn(...arg) : curry(fn, arg))([...arr, ...args])
// 测试let curryTest=curry((a,b,c,d)=>a+b+c+d)curryTest(1,2,3)(4) //返回10curryTest(1,2)(4)(3) //返回10curryTest(1,2)(3,4) //返回10
// 柯里化求值// 指定的函数function sum(a,b,c,d,e) { return a + b + c + d + e}// 传入指定的函数,执行一次let newSum = curry(sum)// 柯里化 每次入参都是一个参数newSum(1)(2)(3)(4)(5)// 偏函数newSum(1)(2)(3,4,5)
// 柯里化简略利用// 判断类型,参数多少个,就执行多少次收集function isType(type, val) { return Object.prototype.toString.call(val) === `[object ${type}]`}let newType = curry(isType)// 相当于把函数参数一个个传了,把第一次先缓存起来let isString = newType('String')let isNumber = newType('Number')isString('hello world')isNumber(999)
实现一个JSON.stringify
JSON.stringify(value[, replacer [, space]]):
Boolean | Number| String
类型会主动转换成对应的原始值。undefined
、任意函数以及symbol
,会被疏忽(呈现在非数组对象的属性值中时),或者被转换成null
(呈现在数组中时)。- 不可枚举的属性会被疏忽如果一个对象的属性值通过某种间接的形式指回该对象自身,即循环援用,属性也会被疏忽
- 如果一个对象的属性值通过某种间接的形式指回该对象自身,即循环援用,属性也会被疏忽
function jsonStringify(obj) { let type = typeof obj; if (type !== "object") { if (/string|undefined|function/.test(type)) { obj = '"' + obj + '"'; } return String(obj); } else { let json = [] let arr = Array.isArray(obj) for (let k in obj) { let v = obj[k]; let type = typeof v; if (/string|undefined|function/.test(type)) { v = '"' + v + '"'; } else if (type === "object") { v = jsonStringify(v); } json.push((arr ? "" : '"' + k + '":') + String(v)); } return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}") }}jsonStringify({x : 5}) // "{"x":5}"jsonStringify([1, "false", false]) // "[1,"false",false]"jsonStringify({b: undefined}) // "{"b":"undefined"}"
实现ES6的extends
function B(name){ this.name = name;};function A(name,age){ //1.将A的原型指向B Object.setPrototypeOf(A,B); //2.用A的实例作为this调用B,失去继承B之后的实例,这一步相当于调用super Object.getPrototypeOf(A).call(this, name) //3.将A原有的属性增加到新实例上 this.age = age; //4.返回新实例对象 return this;};var a = new A('poetry',22);console.log(a);
实现双向数据绑定
let obj = {}let input = document.getElementById('input')let span = document.getElementById('span')// 数据劫持Object.defineProperty(obj, 'text', { configurable: true, enumerable: true, get() { console.log('获取数据了') }, set(newVal) { console.log('数据更新了') input.value = newVal span.innerHTML = newVal }})// 输出监听input.addEventListener('keyup', function(e) { obj.text = e.target.value})
实现深拷贝
- 浅拷贝: 浅拷贝指的是将一个对象的属性值复制到另一个对象,如果有的属性的值为援用类型的话,那么会将这个援用的地址复制给对象,因而两个对象会有同一个援用类型的援用。浅拷贝能够应用 Object.assign 和开展运算符来实现。
- 深拷贝: 深拷贝绝对浅拷贝而言,如果遇到属性值为援用类型的时候,它新建一个援用类型并将对应的值复制给它,因而对象取得的一个新的援用类型而不是一个原有类型的援用。深拷贝对于一些对象能够应用 JSON 的两个函数来实现,然而因为 JSON 的对象格局比 js 的对象格局更加严格,所以如果属性值里边呈现函数或者 Symbol 类型的值时,会转换失败
(1)JSON.stringify()
JSON.parse(JSON.stringify(obj))
是目前比拟罕用的深拷贝办法之一,它的原理就是利用JSON.stringify
将js
对象序列化(JSON字符串),再应用JSON.parse
来反序列化(还原)js
对象。- 这个办法能够简略粗犷的实现深拷贝,然而还存在问题,拷贝的对象中如果有函数,undefined,symbol,当应用过
JSON.stringify()
进行解决之后,都会隐没。
let obj1 = { a: 0, b: { c: 0 } };let obj2 = JSON.parse(JSON.stringify(obj1));obj1.a = 1;obj1.b.c = 1;console.log(obj1); // {a: 1, b: {c: 1}}console.log(obj2); // {a: 0, b: {c: 0}}
(2)函数库lodash的_.cloneDeep办法
该函数库也有提供_.cloneDeep用来做 Deep Copy
var _ = require('lodash');var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3]};var obj2 = _.cloneDeep(obj1);console.log(obj1.b.f === obj2.b.f);// false
(3)手写实现深拷贝函数
// 深拷贝的实现function deepCopy(object) { if (!object || typeof object !== "object") return; let newObject = Array.isArray(object) ? [] : {}; for (let key in object) { if (object.hasOwnProperty(key)) { newObject[key] = typeof object[key] === "object" ? deepCopy(object[key]) : object[key]; } } return newObject;}