santa955 / blog Goto Github PK
View Code? Open in Web Editor NEWmy blogs site.
Home Page: http://www.yandan66.com
License: MIT License
my blogs site.
Home Page: http://www.yandan66.com
License: MIT License
Object.create(proto,[propertiesObject])
方法用于创建一个新对象,并把创建对象的__proto__
属性指向proto
参数所指对象。实现如下:
Object.ucreate = function (proto) {
function Fn () { }
//因为构造函数的实例的__proto__指向构造函数的prototype对象
Fn.prototype = proto
//重新指定构造函数原型对象的指向
Fn.prototype.constructor = Fn
// Fn实列的__proto__指向 Fn.prototype
return new Fn()
}
测试用例如下:
var person = {
isHuman: false,
printIntroduction: function() {
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`)
}
}
var me = Object.ucreate(person)
me.name = 'Matthew' // "name" is a property set on "me", but not on "person"
me.isHuman = true // inherited properties can be overwritten
me.printIntroduction()
// 参考:https://febook.hzfe.org/awesome-interview/book1/coding-promise
// 1. 实现Promise构造函数
function Promise(executor) {
// 2.1. Promise 的状态
// Promise 必须处于以下三种状态之一:pending,fulfilled 或者 rejected。
this.state = "pending";
// 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须按照它们对应的 then 的原始调用顺序来执行。
this.onFulfilledCallback = [];
// 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须按照它们对应的 then 的原始调用顺序来执行。
this.onRejectedCallback = [];
const self = this;
function resolve(value) {
setTimeout(function () {
// 2.1.1. 当 Promise 处于 pending 状态时:
// 2.1.1.1. 可以转换到 fulfilled 或 rejected 状态。
// 2.1.2. 当 Promise 处于 fulfilled 状态时:
// 2.1.2.1. 不得过渡到任何其他状态。
// 2.1.2.2. 必须有一个不能改变的值。
if (self.state === "pending") {
self.state = "fulfilled";
self.data = value;
// 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须按照它们对应的 then 的原始调用顺序来执行。
for (let i = 0; i < self.onFulfilledCallback.length; i++) {
self.onFulfilledCallback[i](value);
}
}
});
}
function reject(reason) {
setTimeout(function () {
// 2.1.1. 当 Promise 处于 pending 状态时:
// 2.1.1.1. 可以转换到 fulfilled 或 rejected 状态。
// 2.1.3. 当 Promise 处于 rejected 状态时:
// 2.1.2.1. 不得过渡到任何其他状态。
// 2.1.2.2. 必须有一个不能改变的值。
if (self.state === "pending") {
self.state = "rejected";
self.data = reason;
// 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须按照它们对应的 then 的原始调用顺序来执行。
for (let i = 0; i < self.onRejectedCallback.length; i++) {
self.onRejectedCallback[i](reason);
}
}
});
}
// 补充说明:用户传入的函数可能也会执行异常,所以这里用 try...catch 包裹
try {
executor(resolve, reject);
} catch (reason) {
reject(reason);
}
}
// 2. 实现方法 then
// 一个 promise 必须提供一个 then 方法来访问其当前值或最终值或 rejected 的原因。
// 一个 promise 的 then 方法接受两个参数:
// promise.then(onFulfilled, onRejected)
Promise.prototype.then = function (onFulfilled, onRejected) {
const self = this;
let promise2;
// 2.2.7. then 必须返回一个 promise
return (promise2 = new Promise(function (resolve, reject) {
// 2.2.2. 如果 onFulfilled 是一个函数:
// 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。
// 2.2.2.2. 它一定不能在 promise 的状态变为 fulfilled 前被调用。
// 2.2.2.3. 它最多只能被调用一次。
if (self.state === "fulfilled") {
// 2.2.4. onFulfilled 或 onRejected 在执行上下文堆栈仅包含平台代码之前不得调用。
// 3.1. 这可以通过“宏任务”机制(例如 setTimeout 或 setImmediate)或“微任务”机制(例如 MutationObserver 或 process.nextTick)来实现。
setTimeout(function () {
// 2.2.1. onFulfilled 和 onRejected 都是可选参数:
// 2.2.1.1. 如果 onFulfilled 不是一个函数,它必须被忽略。
if (typeof onFulfilled === "function") {
try {
// 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。
// 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
const x = onFulfilled(self.data);
// 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
promiseResolutionProcedure(promise2, x, resolve, reject);
} catch (e) {
// 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
reject(e);
}
} else {
// 2.2.7.3. 如果 onFulfilled 不是一个函数且 promise1 为 fulfilled 状态,promise2 必须用和 promise1 一样的值来变为 fulfilled 状态。
resolve(self.data);
}
});
}
// 2.2.3. 如果 onRejected 是一个函数,
// 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。
// 2.2.3.2. 它一定不能在 promise 的状态变为 rejected 前被调用。
// 2.2.3.3. 它最多只能被调用一次。
else if (self.state === "rejected") {
// 2.2.4. onFulfilled 或 onRejected 在执行上下文堆栈仅包含平台代码之前不得调用。
// 3.1. 这可以通过“宏任务”机制(例如 setTimeout 或 setImmediate)或“微任务”机制(例如 MutationObserver 或 process.nextTick)来实现。
setTimeout(function () {
// 2.2.1. onFulfilled 和 onRejected 都是可选参数:
// 2.2.1.2. 如果 onRejected 不是一个函数,它必须被忽略。
if (typeof onRejected === "function") {
try {
// 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。
// 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
const x = onRejected(self.data);
// 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
promiseResolutionProcedure(promise2, x, resolve, reject);
} catch (e) {
// 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
reject(e);
}
}
// 2.2.7.4. 如果 onRejected 不是一个函数且 promise1 为 rejected 状态,promise2 必须用和 promise1 一样的 reason 来变为 rejected 状态。
else {
reject(self.data);
}
});
} else if (self.state === "pending") {
// 2.2.6. then 可能会被同一个 promise 多次调用。
// 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须按照它们对应的 then 的原始调用顺序来执行。
self.onFulfilledCallback.push(function (promise1Value) {
if (typeof onFulfilled === "function") {
try {
// 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。
// 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
const x = onFulfilled(self.data);
// 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
promiseResolutionProcedure(promise2, x, resolve, reject);
} catch (e) {
// 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
reject(e);
}
}
// 2.2.7.3. 如果 onFulfilled 不是一个函数且 promise1 为 fulfilled 状态,promise2 必须用和 promise1 一样的值来变为 fulfilled 状态。
else {
resolve(promise1Value);
}
});
// 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须按照它们对应的 then 的原始调用顺序来执行。
self.onRejectedCallback.push(function (promise1Reason) {
if (typeof onRejected === "function") {
try {
// 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。
// 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
const x = onRejected(self.data);
// 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
promiseResolutionProcedure(promise2, x, resolve, reject);
} catch (e) {
// 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
reject(e);
}
}
// 2.2.7.4. 如果 onRejected 不是一个函数且 promise1 为 rejected 状态,promise2 必须用和 promise1 一样的 reason 来变为 rejected 状态。
else {
reject(promise1Reason);
}
});
}
}));
};
// 3. Promise 处理程序
// Promise 处理程序是一个将 promise 和 value 作为输入的抽象操作,我们将其表示为 [[Resolve]](promise, x)。
// 补充说明:这里我们将 resolve 和 reject 也传入进来,因为后续要根据不同的逻辑对 promise 执行 fulfill 或 reject 操作。
function promiseResolutionProcedure(promise2, x, resolve, reject) {
// 2.3.1. 如果 promise 和 x 引用的是同一个对象,promise 将以一个 TypeError 作为 reason 来进行 reject。
if (promise2 === x) {
return reject(new TypeError("Chaining cycle detected for promise"));
}
// 2.3.2. 如果 x 是一个 promise,根据它的状态:
if (x instanceof Promise) {
// 2.3.2.1. 如果 x 的状态为 pending,promise 必须保持 pending 状态直到 x 的状态变为 fulfilled 或 rejected。
if (x.state === "pending") {
x.then(function (value) {
promiseResolutionProcedure(promise2, value, resolve, reject);
}, reject);
}
// 2.3.2.2. 如果 x 的状态为 fulfilled,那么 promise 也用同样的值来执行 fulfill 操作。
else if (x.state === "fulfilled") {
resolve(x.data);
}
// 2.3.2.3. 如果 x 的状态为 rejected,那么 promise 也用同样的 reason 来执行 reject 操作。
else if (x.state === "rejected") {
reject(x.data);
}
return;
}
// 2.3.3. 除此之外,如果 x 是一个对象或者函数,
if (x && (typeof x === "object" || typeof x === "function")) {
// 2.3.3.3.3. 如果 resolvePromise 和 rejectPromise 都被调用,或者多次调用同样的参数,则第一次调用优先,任何之后的调用都将被忽略。
let isCalled = false;
try {
// 2.3.3.1. 声明一个 then 变量来保存 then
let then = x.then;
// 2.3.3.3. 如果 then 是一个函数,将 x 作为 this 来调用它,第一个参数为 resolvePromise,第二个参数为 rejectPromise,其中:
if (typeof then === "function") {
then.call(
x,
// 2.3.3.3.1. 假设 resolvePromise 使用一个名为 y 的值来调用,运行 promise 处理程序 [[Resolve]](promise, y)。
function resolvePromise(y) {
// 2.3.3.3.3. 如果 resolvePromise 和 rejectPromise 都被调用,或者多次调用同样的参数,则第一次调用优先,任何之后的调用都将被忽略。
if (isCalled) return;
isCalled = true;
return promiseResolutionProcedure(promise2, y, resolve, reject);
},
// 2.3.3.3.2. 假设 rejectPromise 使用一个名为 r 的 reason 来调用,则用 r 作为 reason 对 promise 执行 reject 操作。
function rejectPromise(r) {
// 2.3.3.3.3. 如果 resolvePromise 和 rejectPromise 都被调用,或者多次调用同样的参数,则第一次调用优先,任何之后的调用都将被忽略。
if (isCalled) return;
isCalled = true;
return reject(r);
}
);
}
// 2.3.3.4. 如果 then 不是一个函数,使用 x 作为值对 promise 执行 fulfill 操作。
else {
resolve(x);
}
} catch (e) {
// 2.3.3.2. 如果检索 x.then 的结果抛出异常 e,使用 e 作为 reason 对 promise 执行 reject 操作。
// 2.3.3.3.4. 如果调用 then 时抛出一个异常 e,
// 2.3.3.3.4.1. 如果 resolvePromise 或 rejectPromise 已经被调用过了,则忽略异常。
if (isCalled) return;
isCalled = true;
// 2.3.3.3.4.2. 否则,使用 e 作为 reason 对 promise 执行 reject 操作。
reject(e);
}
}
// 2.3.4. 如果 x 不是一个对象或者函数,使用 x 作为值对 promise 执行 fulfill 操作。
else {
resolve(x);
}
}
//使用Promise实现
const sleep = (time) => {
return new Promise((r => setTimeout(() => r(), time)))
}
//使用回调实现
function sleep (callback, time) {
if (typeof callback === 'function') {
setTimeout(callback, time)
}
}
使用方法:
async function sleepAsync() {
console.log('before sleep')
await sleep(1000)
console.log('after sleep')
}
sleepAsync()
function output () {
console.log(1)
}
sleep(output, 1000)
主要实现下面的API:
function EventEmitter () {
/**
* {
* event1: [fn1, fn2, fn3, ...],
* event2: [fn1, fn2, fn3, ...]
* }
*/
this.listeners = {}
}
EventEmitter.prototype.on = function (event, fn) {
let listeners = this.listeners
if (!listeners[event]) {
this.listeners[event] = []
}
this.listeners[event].push(fn)
}
EventEmitter.prototype.emit = function (event) {
let listeners = this.listeners
let args = [].slice.call(arguments)
let fns = listeners[event] || []
//删除事件名称
args.shift()
for (const fn of fns) {
fn.apply(this, args)
}
}
EventEmitter.prototype.off = function (event, fn) {
let listeners = this.listeners
if (listeners[event]) {
let index = listeners[event].indexOf(fn)
if (index >= 0) {
this.listeners[event].splice(index, 1)
}
}
}
EventEmitter.prototype.once = function (event, fn) {
const cb = () => {
let args = [].slice.call(arguments)
fn.apply(this, args)
// 注意此处要解除绑定的是函数cb
this.off(event, cb)
}
this.on(event, cb)
}
测试用例:
var evt = new EventEmitter()
var hello = function (from) {
console.log(`${from} hello greet`)
}
var today = function () {
console.log(new Date().toLocaleString())
}
evt.on('hello', hello)
evt.once('today', today)
evt.emit('hello', 'jack') // jack hello greet
evt.emit('hello', 'make && joe') // make && joe hello greet
evt.off('hello', hello)
evt.emit('hello', 'pony') // nothing
evt.emit('today') // 2021/4/2上午11:27:25
evt.emit('today') // nothing
主要参考文章:
节流函数与防抖函数动画演示:
throttle
节流函数的主要特点是:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。现在主流的实现方式有两种:一种是使用时间戳,另外一种是使用定时器。
function throttle (fn, delay = 500) {
let prev = 0
return function () {
let now = Date.now()
if (now - prev > delay) {
fn.apply(this, arguments)
prev = now
}
}
}
调用方式:
const throttleFn = throttle(fn, 300)
container.onmousemove = throttle(fn, 1000)
这种方式实现的本质是加一个锁,如果函数执行了就重置状态
//方式一
function throttle (fn, delay = 500) {
let timer = null
return function () {
let context = this
let args = arguments
if (timer) return
timer = setTimeout(() => {
fn.apply(context, args)
timer = null
}, delay)
}
}
//方式二
function throttle (fn, delay = 500) {
let flag = true
return (...args) => {
if (!flag) return
flag = false
setTimeout(() => {
fn.apply(this, args)
flag = true
}, delay)
}
debounce
防抖的主要特点是:无论触发事件多少次,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行,总之,就是要触发完事件 n 秒内不再触发事件,我才执行。
function debounce (fn, delay = 500) {
let timeout
return function () {
let context = this
let args = arguments
clearTimeout(timeout)
timeout = setTimeout(function () {
//注意this指向和参数问题
fn.apply(context, args)
}, delay)
}
}
防抖和节流的可取消操作是一样的实现方式,就是在返回的函数上加上一个方法,清除定时器。下面以防抖为例:
function debounce (fn, delay = 500) {
let timeout
let debounced = function () {
let context = this
let args = arguments
clearTimeout(timeout)
timeout = setTimeout(function () {
//注意this指向和参数问题
fn.apply(context, args)
}, delay)
}
// 设置取消方式
debounced.cancel = function () {
clearTimeout(timeout)
timeout = null
}
//记得返回
return debounced
}
function quicklySort(nums, left, right) {
if (left >= right) return
var index = partition(nums, left, right)
quicklySort(nums, left, index)
quicklySort(nums, index + 1, right)
return nums
}
function swap(nums, i, j) {
var temp = nums[i]
nums[i] = nums[j]
nums[j] = temp
return nums
}
function partition(nums, left, right) {
var pivot = nums[left]
var i = left
swap(nums, left, right)
//注意j的起始值是i,不是0
for (var j = i; j < right; j++) {
if (nums[j] > pivot) {
swap(nums, i, j)
i++
}
}
//注意最后交换的是i与right的值
swap(nums, i, right)
return i
}
var arr = [1, 9, 8, 9, 0, 1, 45, 3];
console.log(quicklySort(arr, 0, arr.length - 1))
var sortArray = function (nums) {
quickSort(nums, 0, nums.length - 1)
return nums
};
function quickSort(nums, left, right) {
if (left >= right) return
var pivot = nums[left]
var i = left
var j = right
while (i < j) {
//一定要从右边开始,i和j的顺序不能调换,先j--,再i++
while (i < j && nums[j] >= pivot) j--
while (i < j && nums[i] <= pivot) i++
var temp = nums[i]
nums[i] = nums[j]
nums[j] = temp
}
nums[left] = nums[i]
nums[i] = pivot
quickSort(nums, left, i - 1)
quickSort(nums, i + 1, right)
}
Promise.any()
接收一个Promise
可迭代对象,只要其中的一个 promise
成功,就返回那个已经成功的 promise
,如果所有的promise
都失败,返回失败结果的数组(和Promise.all
返回的格式相同)。
Promise.any = Promise.any || function (promises) {
if (!Array.isArray(promises)) {
throw new TypeError('Type Error')
}
return new Promise((resolve, reject) => {
let len = promises.length
let errs = new Array(len)
for (let i = 0; i < len; i++) {
Promise.resolve(promises[i]).then(val => {
resolve(val)
}, err => {
errs[i] = err
len--
if (len === 0) {
reject(errs)
}
})
}
}
var p1 = new Promise(r => {
setTimeout(() => {
r('done')
}, 1000)
})
var p3 = new Promise(r => {
setTimeout(() => {
r(500)
}, 500)
})
var p2 = new Promise((r, j) => {
setTimeout(() => {
j('error')
}, 1500)
})
var p4 = new Promise((r, j) => {
setTimeout(() => {
j('error2000')
}, 2000)
})
Promise.any([p1, p2, p4]).then(r => {
console.log(r) // 'done'
}).catch(err => {
console.error(err)
})
Promise.any([p2, p4]).then(r => {
console.log(r) // 'done'
}).catch(err => {
console.error(err) //[ 'error', 'error2000' ]
})
实现一个函数fetchWithRetry(xhr, times = 5, delay = 1000)
,在请求失败delay
时间后自动发起times
次重试。
const fetchWithRetry = function (xhr, times = 5, delay = 1000) {
return new Promise((resolve, reject) => {
const run = () => {
xhr()
.then(resolve)
.catch(err => {
times--
console.log(`还剩${times}次重试`)
if (times >= 0) {
setTimeout(() => { run() }, delay)
} else {
reject(err)
}
})
}
run()
})
}
测试用例
let xhr = () => {
return new Promise((r, j) => {
setTimeout(() => { j('出错了') }, 1000)
})
}
fetchWithRetry(xhr)
// 还剩4次重试
// 还剩3次重试
// 还剩2次重试
// 还剩1次重试
// 还剩0次重试
//https://github.com/sisterAn/JavaScript-Algorithms/issues/56
function uAsync(gen) {
return new Promise((resolve, reject) => {
let g = gen()
function next(v) {
try {
let res = g.next(v)
if (res.done) {
resolve(res.value)
}
Promise
.resolve(res.value)
.then(next)
.then(resolve, reject)
} catch (error) {
reject(error)
}
}
next()
})
}
function getNum (num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num + 1)
}, 1000)
})
}
var func = function* (){
var f1 = yield getNum(1)
var f2 = yield getNum(f1)
console.log(f2) // 3
}
uAsync (func)
我们在完成函数依赖调用(前一个函数的返回结果作为后一个函数的参数)时,常常会嵌套多层调用。compose
函数的作用主要是将需要嵌套执行的函数平铺,并按照参数的顺序从右往左执行,结果作为最终函数的参数。
具体实现如下:
function compose () {
let args = [].slice.call(arguments)
return function () {
let fn = args.pop()
//注意此处的参数和后面循环的差异
let res = fn.apply(this, arguments)
while (args.length > 0) {
fn = args.pop()
res = fn.call(this, res)
}
return res
}
}
测试用例:
现在实现一个函数,输入一个数,先执行加1,再乘2, 最后减5操作
function subtract (a) {
return a - 5
}
function multi (a) {
return a * 2
}
function addOne (a) {
return a + 1
}
//使用compose前
subtract(multi(addOne(2))) //输出1
//使用compose
compose(subtract, multi, addOne)(2) //输出1
柯里化要求函数具有固定数量的参数。使用 rest 参数的函数,例如 f(...args),不能以这种方式进行柯里化。
//参考: https://github.com/mqyqingfeng/Blog/issues/42#issuecomment-342688679
// 实现步骤:
// 检查传入的参数长度与原函数参数个数比较
// 1. 如果少于原函数参数个数,说明还不能执行原函数,拼接参数,返回函数
// 2. 如果大于或等于原函数参数个数,返回原函数执行结果
function curry(fn, args = []) {
//function.length 返回函数参数长度
var length = fn.length;
return function () {
var params = [...args]
for(var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
params.push(arg);
}
// 情况 1
if (params.length < length) {
return curry.call(this, fn, params);
}else {
//情况 2
return fn.apply(this, params);
}
}
}
//实现方式2
//参考:https://zh.javascript.info/currying-partials
function curry(fn) {
var len = fn.length
return function curried(...args) {
if (args.length >= len) {
return fn.apply(this, args)
} else {
//注意此处返回函数
return function (...args2) {
return curried.apply(this, args.concat(args2));
}
}
}
}
function PromiseWithAbort (promise) {
// 在这里包装一个 promise,可以控制原来的promise是成功还是失败
let abort
let newPromise = new Promise((resolve, reject) => {
abort = reject
})
// 任何一个先成功或者失败 就可以获取到结果
let p = Promise.race([promise, newPromise])
p.abort = abort
return p
}
使用方法
//模拟请求超时
let promise = function () {
return new Promise((r, j) => {
setTimeout(() => { r('超时了') }, 3000)
})
}
let wrappedPromise = PromiseWithAbort(promise())
//超时前取消
setTimeout(() => {
wrappedPromise.abort('我取消请求了')
}, 1000)
wrappedPromise.then((data => {
console.log('成功的结果: ' + data)
})).catch(e => {
console.log('失败的结果: ' + e)
})
来源:
掘金
该Promise.allSettled()
方法返回一个在所有给定的promise
都已经fulfilled
或rejected
后的promise
,并带有一个对象数组,每个对象表示对应的promise
结果。
Promise.allSettled = Promise.allSettled || function (promises) {
if (!Array.isArray(promises)) {
throw new TypeError('Type Error')
}
return new Promise((resolve, reject) => {
let len = promises.length
let res = new Array(len)
for (let i = 0; i < len; i++) {
Promise.resolve(promises[i]).then(
(val) => {
res[i] = val
len--
if (len === 0) {
resolve(res)
}
},
err => {
res[i] = err
len--
if (len === 0) {
resolve(res)
}
})
}
})
}
var p1 = new Promise(r => {
setTimeout(() => {
r('done')
}, 1000)
})
var p2 = new Promise((r, j) => {
setTimeout(() => {
j('error')
}, 1500)
})
Promise.allSettled([p1, p2]).then(r => {
console.log(r) // [ 'done', 'error' ]
})
//参考:https://github.com/matthew-andrews/Promise.prototype.finally/blob/master/finally.js
Promise.prototype.finally = function (callback) {
let constructor = this.constructor
return this.then(
value => constructor.resolve(callback()).then(() => value),
reason => constructor.resolve(callback()).then(() => { throw reason })
)
}
Promise
的 finally
方法不管成功还是失败都会执行,而不会永远最后执行。例如:
new Promise((resolve, reject) => {
resolve();
}).finally(() => {
console.log('finally1');
}).then(() => {
console.log('then');
}).finally(() => {
console.log('finally2');
});
// finally1
// then
// finally2
ES5
实现class
关键字主要是使用Object.defineProperty()
方法实现
let createClass = {
checkType: function (obj, constructor) {
if (!(obj instanceof constructor)) {
throw new TypeError('Can not call a class as a function.')
}
},
create: function (constructor, protoProps, staticProps) {
protoProps && this.defineProperties(constructor.propertype, protoProps)
staticProps && this.defineProperties(constructor, staticProps)
return constructor
},
defineProperties: function (target, props) {
for (var descriptor of props) {
descriptor.enumerable = descriptor.enumerable || false
descriptor.configurale = true
if ('value' in descriptor) descriptor.writable = true
Object.defineProperty(target, descriptor.key, { ...descriptor })
}
},
inherits: function (subClass, superClass) {
if (typeof subClass !== 'function' && superClass !== null) {
throw new TypeError('Super expression must either be null or a function')
}
subClass.propertype = Object.create(superClass && superClass.propertype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
})
if (superClass) {
Object.setPrototypeOf
? Object.setPrototypeOf(subClass, superClass)
: subClass.__proto__ = superClass
}
}
}
使用方法:
var Person = function () {
function Person (name) {
createClass.checkType(this, Person)
this.name = name
}
createClass.create(Person, [{
key: 'sayHello',
value: function sayHello () {
return 'hello, I am ' + this.name
}
}, {
key: 'name',
get: function get () {
return 'kevin'
},
set: function set (newName) {
console.log('new name 为:' + newName)
}
}], [{
key: 'onlySayHello',
value: function onlySayHello () {
return 'hello'
}
}])
return Person
}()
instanceof
运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
由于实例对象存在一个隐形的__proto__
对象,这个对象指向实例所对应的构造函数的原型对象prototype
。回溯__proto__
对象到最后会为null
所以只要我们不断遍历,检测__proto__
与构造函数的原型对象prototype
是否相等即可。
function uInstanceof (instance, constructor) {
while (instance) {
if (instance.__proto__ === constructor.prototype) {
return true
}
instance = instance.__proto__
}
return false
}
测试用例如下:
class A{}
var c = new A()
console.log(uInstanceof(c, A)); // true
console.log(uInstanceof(c, Object)); // true
console.log(uInstanceof(c, Array)); // false
Promise.all = Promise.all || function (promises) {
if (!Array.isArray(promises)) {
throw new TypeError('Type Error')
}
return new Promise((resolve, reject) => {
let len = promises.length
let res = new Array(len)
for (let i = 0; i < len; i++) {
Promise.resolve(promises[i]).then((val) => {
res[i] = val
len--
if (len === 0) {
resolve(res)
}
}, err => {
reject(err)
})
}
}
var p1 = new Promise(r => {
setTimeout(() => {
r('done')
}, 1000)
})
var p2 = new Promise((r, j) => {
setTimeout(() => {
j('error')
}, 1500)
})
Promise.all([p1, p2]).then(r => {
console.log(r)
}).catch(err => {
console.error(err) //error
})
Promise.race = Promise.race || function (promises) {
if (!Array.isArray(promises)) {
throw new TypeError('Type Error')
}
return new Promise((resolve, reject) => {
for (let p of promises) {
Promise.resolve(p).then(resolve, reject)
}
})
}
var p1 = new Promise(r => {
setTimeout(() => {
r('done')
}, 1000)
})
var p2 = new Promise((r, j) => {
setTimeout(() => {
j('error')
}, 1500)
})
Promise.race([p1, p2]).then(r => {
console.log(r)
}).catch(err => {
console.error(err)
})
const list = [
{ id: 1, name: '部门A', parentId: null },
{ id: 2, name: '部门B', parentId: 1 },
{ id: 3, name: '部门C', parentId: 1 },
{ id: 4, name: '部门D', parentId: 1 },
{ id: 5, name: '部门E', parentId: 2 },
{ id: 6, name: '部门F', parentId: 3 },
{ id: 7, name: '部门G', parentId: 2 },
{ id: 8, name: '部门H', parentId: 4 }
]
// 实现方式1
// 参考:https://juejin.cn/post/7021517725306617869
function listToTree(list) {
var map = {}
var tree = []
var len = list.length
for (var i = 0; i < len; i++) {
var item = list[i]
var { id, children } = item
map[id] = item
if (!children) {
map[id].children = []
}
}
for (var i = 0; i < len; i++) {
var item = list[i]
var { id, parentId } = item
if (map[parentId]) {
map[parentId].children.push(item)
} else {
tree.push(item)
}
}
return tree
}
//实现方式2
function toTree(arr, parentId) {
function loop(parentId) {
return arr.reduce((acc, cur) => {
if (cur.parentId === parentId) {
cur.children = loop(cur.id)
acc.push(cur)
}
return acc
}, [])
}
return loop(parentId)
}
def lower_bound(array, first, last, value): # 求非降序范围[first, last)内第一个不小于value的值的位置
while first < last: # 搜索区间[first, last)不为空
mid = first + (last - first) // 2 # 防溢出
if array[mid] < value: first = mid + 1
else: last = mid
return first # last也行,因为[first, last)为空的时候它们重合
function shuffle (nums) {
let getRandom = function (low, high) {
//返回随机数在区间[low, high]上,包含最大值和最小值
return Math.floor(Math.random() * (high - low + 1)) + low
}
let len = nums.length
for (let i = 0; i < len; i++) {
let rIndex = getRandom(i, len - 1)
let temp = nums[i]
nums[i] = nums[rIndex]
nums[rIndex] = temp
}
return nums
}
console.log(shuffle([1, 2, 3, 4]))
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.