编程题(包括算法)

题目

柯里化实现add函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function add(){
let args = [...arguments]
let innerAdd = function(){
args.push(...arguments)
return innerAdd
}
innerAdd.toString = function(){
return args.reduce((a,b)=>a+b)
}
return innerAdd
}

// test
add(1)(2)(3) //6
add(4,1,1)(2) //8

高阶记忆函数

1
2
3
4
5
6
7
8
9
10
function memorize(fn){
var cache = {}
return function(){
var key = Array.of(arguments).join('-')
if(key in cache){
return cache[key]
}
return cache[key] = fn.apply(this, arguments)
}
}

多数组的笛卡尔乘积

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function descartes(data){
return data.reduce((total,list,index)=>{
if(total.length===0){return list}
else{
return [].concat(...total.map(x=>{return list.map(y=>{return `${x}-${y}`})}))//[[a,b]]
}
},[])
}

// test
var array1= ['白色','黑色','红色']
var array2 = ['透气','防滑']
var array3 = ['37码','38码','39码']
var array4 = ['男款','女款']

var data = [array1,array2,array3, array4]
var result = descartes(data)
// result[33]
"红色-防滑-38码-女款"

斐波那契数列

只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则就无法进行“尾调用优化”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 性能最差(太深调用栈)
function Fibonacci (n) {
if ( n <= 1 ) {return 1};
return Fibonacci(n - 1) + Fibonacci(n - 2);
}

// 递归优化(尾递归优化)
function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
if( n <= 1 ) {return ac2};
return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}

// 循环版
function Fibonacci3(n){
if (n===1 || n===2) {
return 1;
}
let ac1 = 1, ac2 = 1;
for (let i = 2; i < n; i++){
[ac1, ac2] = [ac2, ac1 + ac2];
}
return ac2;
}
// 加入缓存版本
function getFio () {
var cache = []
function Fibonacci(n){

if(cache[n]){return cache[n]}
if ( n <= 1 ) {cache[n]=1; return 1};
var temp = Fibonacci(n - 1) + Fibonacci(n - 2);
cache[n] = temp
return temp
}
return Fibonacci
}

阶乘(尾递归)

尾递归的实现,往往需要改写递归函数,确保最后一步只调用自身。做到这一点的方法,就是把所有用到的内部变量改写成函数的参数。

1
2
3
4
function factorial(n, total = 1) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}

对象上实现for of

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// method 1
var obj ={
a:'apple',b:'banana',c:'cherry',
[Symbol.iterator]:function(){
var index =0;
var data = Object.values(this)
return {
next(){
return index<data.length?{value:data[index++], done:false}:{value: undefined,done:true}
}
}
}
}


// method 2
var obj = {
a:'apple',b:'banana',c:'cherry',
* [Symbol.iterator](){
let values = Object.values(this)
for(var i=0;i<values.length;i++){
yield values[i]
}
}
};

// test
for(var i of obj){
console.log(i)
}
// apple
// banana
// cherry

计算数组中每个元素出现的次数

方法:reduce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//计算数组中每个元素出现的次数
let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

var getNameCounts = function(namesArray){
return namesArray.reduce(function(result,item, index, arr){
if(!result[item]){
result[item] =1
}else{
result[item]+=1
}
return result
},{})
}
getNameCounts(names)
// {Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}

数组去重

2种方法: reduce/ Set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var arr = [1,2,3,4,4,1]
// method 1
var removeDuplicate = function(arr){
return arr.reduce(function(prev,cur,index, arr){
if(!prev.includes(cur)){
prev.push(cur)
}
return prev
},[])
}
removeDuplicate(arr)
// [1, 2, 3, 4]

// method 2
[...new Set(arr)]
Array.from(new Set(arr))

将二维数组转化为一维

2 种方法: reduce / flat

1
2
3
4
5
6
7
8
9
10
11
12
// method 1
var arr = [[0, 1], [2, 3], [4, 5]]
var flattenDim2Arr = function(arr){
return arr.reduce(function(prev,cur){
return prev.concat(cur)
},[])
}
flattenDim2Arr(arr)
// [0,1,2,3,4,5]

// method 2
arr.flat()

将多维数组转化为一维

3种方法: reduce(递归)/迭代/ flat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var arr = [[0, 1], [2, 3], [4,[5,6,7]]]

// method 1
var flatternMultiDimArr = function(arr){
return arr.reduce(function(prev,cur){
return prev.concat(Array.isArray(cur)?flattenDim2Arr(cur):cur)
},[])
}
flatternMultiDimArr(arr)
// [0, 1, 2, 3, 4, 5, 6, 7]

// method 2
var flattern = function(arr){
while(arr.some(x=> Array.isArray(x))){
arr = [].concat(...arr)
}
return arr
}

// method 3
arr.flat(Infinity)

对象里的属性求和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var result = [
{
subject: 'math',
score: 10
},
{
subject: 'chinese',
score: 20
},
{
subject: 'english',
score: 30
}
]
var getObjSum = function(result){
return result.reduce(function(prev, cur){
return prev+cur.score
},0)
}
getObjSum(result)
// 60

闭包

请写一个函数 fn,其参数是一个函数 fn2,其返回值是一个函数 fn3,fn3 会调用 fn2。
请写一个函数满足 add(1, 2, 3, …) 得到所有参数的和,参数个数不确定。
请写一个函数满足 add2(1)(2)(3)(………)() 在遇到没有参数的调用时,返回所有参数的和。
请再写一次 add2,命名为 add3,要求其内部使用了 add 函数,且只在最后求和时使用了 add。

issue#1 闭包,fn3在上级作用域获得fn2

1
2
3
4
5
6
7
8
9
10
var fn= function(fn2){
let func = arguments[0]
return function(){
func.call(this)
}
}

var fn2 = function(){console.log('i am fn2')}
var result = fn(fn2)
result()

issue#2

1
2
3
4
5
 var add = function(){
return Array.from(arguments).reduce((sum,item)=>sum+ item,0)
}

add(1,2,3,4)

issue#3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 没有参数的时候返回累加值 有参数的时候返回函数本身
(function(){
let result = 0
window.add2= function(n){

if(n=== undefined){
let argsArray = [...arguments]
result += argsArray.reduce((sum,item)=>sum+ item,0)
return add2
}else{
let final = result
result = 0
return final
}
}

})(0)

var res = add2(1)(2)(3,4)()
var res = add3(1,2)(1,2)()

issue#4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(function(){
let list = []

window.add3= function(n){
if(n === undefined){
list.push(...arguments)
return add3
}else{
let final = add(...list)
list = []
return final
}
}

})()

var res = add2(1)(2)(3,4)()
var res = add3(1,2)(1,2)()

数组合并

请把两个数组 [‘A1’, ‘A2’, ‘B1’, ‘B2’, ‘C1’, ‘C2’, ‘D1’, ‘D2’] 和 [‘A’, ‘B’, ‘C’, ‘D’],合并为 [‘A1’, ‘A2’, ‘A’, ‘B1’, ‘B2’, ‘B’, ‘C1’, ‘C2’, ‘C’, ‘D1’, ‘D2’, ‘D’]。

方法: reduce(找出插入位置) /改造数组后合并后排序

1
2
3
4
5
6
7
// method 1
['A', 'B', 'C', 'D'].reduce(function(result, item){
// 注意此处的浅拷贝
let temp = [...result].reverse()
let idx = temp.length-temp.findIndex(x=>x.startsWith(item))
return [...result.slice(0, idx),item, ...result.slice(idx)]
}, ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2'] )

1
2
3
4
5
6
7
8
// method 2
var arr1 = ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2'];
var arr2 = ['A', 'B', 'C', 'D'].map(x=>x+'3');
[...arr1,...arr2].sort().map(x=>{
if(x[1]==='3'){
return x[0]
}return x
})

大数相加

当年我罗里吧嗦写的代码,现在用reduce一下子省去了很多代码量,而且可读性有了质的飞跃,强烈安利reduce
细节要注意string 和 number转换和一些边界情况处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var str1='31287'
var str2 ='169093'

var arr1 = [...str1.split('').map(x=>parseInt(x))].reverse()
var arr2 = [...str2.split('').map(x=>parseInt(x))].reverse()
var length = str1.length>str2.length?str1.length:str2.length

var a1 = new Array(length).fill(0)
var a2 = new Array(length).fill(0)
a1.splice(0,arr1.length,...arr1)
a2.splice(0,arr2.length,...arr2)


var tempArr = a1.reduce(function(memo, item, index,arr){
memo[index] = memo[index]+item
if(memo[index]>9){
if(index<length-1){
memo[index+1]+=1
}else{
memo.push(1)
}
}
return [...memo]
},a2)

var result=tempArr.reverse().map(x=>{
x= x+''
if(x.length>1){
return x[1]
}
return x
}).join('')

console.log(result)
//200380

当a 等于多少成立

1
2
3
4
// var a = ?;
if(a == 1 && a == 2 && a == 3){
console.log(1);
}

方法:

  • toString
  • valueOf
  • 数组的join(数组调用toString会调用join)
  • Object.defineProperty
  • Symbol.toPrimitive

考察: 隐式转换涉及的方法 & 数据缓存(闭包/generator函数)

背景知识
如果原始类型的值和对象比较,对象会转为原始类型的值,再进行比较。
对象转换成原始类型的值,算法是 先调用valueOf方法;如果返回的还是对象,再接着调用 toString方法

对于数组对象,toString 方法返回一个字符串,该字符串由数组中的每个元素的 toString() 返回值经调用 join() 方法连接(由逗号隔开)组成。
可以看到数组 toString 会调用本身的 join 方法

ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。 Symbol.toPrimitive就是其中一个,它指向一个方法,表示该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// method 1
var a={
i:0,
toString(){
return ++a.i
}
}
// method 1.1 闭包
var a={
valueOf:(x=>()=>++x)(0)
}


// method 2.1
var a={
i:0,
valueOf(){
return ++a.i
}
}


// method 2.2
var a={
gen:(function *(){
yield 1;
yield 2;
yield 3;
})(),
valueOf(){
return this.gen.next().value
}
}

// method 3
var a=[1,2,3]
a.join = a.shift
if(a == 1 && a == 2 && a == 3){
console.log(1);
}

// method 4.1
var i=0
Object.defineProperty(window, 'a',{
get(){
return ++i
}
})

// method 4.2
Object.defineProperty(window, 'a', {
get: function() {
return this.value = this.value ? (this.value += 1) : 1;
}
});

// method 5
var a= {[Symbol.toPrimitive]: (x => ()=>++x)(0)}
if(a == 1 && a == 2 && a == 3) {
console.log('1');
}

说出结果1

考察:map 的callback传入的参数 和parseInt对于radix处理

1
['1','2','3'].map(parseInt)

分析:
实际执行 parseInt(‘1’,0) –pareseInt(‘2’,1) – parseInt(‘3’,2)
得到[1, NaN, NaN]

说出结果2 和修改打印10 和 20

考察:具名自执行函数的变量为只读属性,不可修改,在内部类似const常量,不能被再次赋值

1
2
3
4
5
6
var b = 10;
(function b(){
// 'use strict' TypeError: Assignment to constant variable.
b = 20;
console.log(b);
})();

answer: 打印立即执行的b函数
ƒ b() {
b = 20;
console.log(b)
}

这样改,打印10

1
2
3
4
5
var b = 10;
(function b(){
b = 20;
console.log(window.b);
})();

这样改,打印20

1
2
3
4
5
6
7
8
9
10
11
12
var b = 10;
(function b(){
var b = 20;
console.log(b);
})();


var b = 10;
(function (){
b = 20;
console.log(b);
})();

手写防抖(2个版本)/节流(3个版本)

防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行

防抖场景:用户在输入框不断输入;window 的resize/scroll
节流场景:鼠标连续点击;滚动页面时,每隔一段时间发一次 ajax 请求;监听滚动事件,比如是否滑到底部自动加载更多

防抖的实现重点,就是巧用setTimeout做缓存池,而且可以轻易地清除待执行的代码。防抖是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,都会清除当前的 timer 然后重新设置超时调用,即重新计时。

节流是通过判断是否到达一定时间来触发函数,超过则调用,不超过则使用计时器延后,每次函数触发会修改时间标记位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// 版本1: 暴力版,会频繁创建定时器。首次会执行,最后一次会执行
function myDebounce(fn, wait, immediate =true){
let timer
let isFirst = true
return function(...args){
if(isFirst && immediate){
fn.apply(this, args)
isFirst = false
}
console.log(`timer is ${timer}`)
if(timer)clearTimeout(timer)
timer = setTimeout(()=>{
fn.apply(this, args)
}, wait)
}
}

var add= function(a,b){
console.log(`a+b is${a+b}, seconds is ${new Date().getSeconds()}`)
}


// 版本2: 优化版,小于时间间隔内会重设定时器。首次会执行,最后一次会执行
var debounce = (fn, wait, immediate=false) => {
let timer, startTimeStamp=0;
let context, args;

let run = (timerInterval)=>{
console.log(`timer is ${timer}`)
timer= setTimeout(()=>{
let now = (new Date()).getTime();
let interval=now-startTimeStamp
if(interval<timerInterval){ // the timer start time has been reset,so the interval is less than timerInterval
console.log('debounce reset',timerInterval-interval);
startTimeStamp=now;
run(timerInterval-interval); // reset timer for left time
}else{
if(!immediate){
fn.apply(context,args);
}
clearTimeout(timer);
timer=null;
}

},timerInterval);
}

return function(){
context=this;
args=arguments;
let now = (new Date()).getTime();
startTimeStamp=now; // set timer start time

if(!timer){
console.log('debounce set',wait);
if(immediate) {
fn.apply(context,args);
}
run(wait); // last timer alreay executed, set a new timer
}

}
}


var res = myDebounce(add)
res(1,2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 时间戳版本 节流 :第一次会执行
function myThrottle1(fn, wait = 3000){
let markedTime = Date.now()
let curTime
return function(...args){
curTime = Date.now()
if(curTime- markedTime >= wait ){
fn.apply(this, args)
markedTime = curTime
}
}
}

// 定时器版本节流 :最后一次会执行
function myThrottle2(fn, wait=3000){
let timer
return function(...args){
if(!timer){
timer = setTimeout(()=>{
fn.apply(this,args)
timer = null
},wait)
}
}
}

// 定时器 + 时间戳版本: 第一次会执行,最后一次会执行
function myThrottle3(fn, wait=3000){
let markedTime
let timer
return function(...args){
let curTime = Date.now()

if(markedTime && curTime - markedTime<=wait){
clearTimeout(timer)
timer = setTimeout(()=>{
fn.apply(this, args)
markedTime = curTime
}, wait)

}else{
fn.apply(this, args)
markedTime = curTime
}
}
}

var add= function(a,b){
console.log(`a+b is${a+b}, seconds is ${new Date().getSeconds()}`)
}

var res = myThrottle(add)
res(1,2)

异步题目

  1. 改造下面的代码,使之输出0 - 9

    1
    2
    3
    4
    5
    for (var i = 0; i< 10; i++){
    setTimeout(() => {
    console.log(i);
    }, 1000)
    }

    方法:let创建块级作用域/ setTimeout第三个参数/ 立即执行函数传递参数/ 闭包 / 用数组(取巧的方法)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    // method 1
    for (let i = 0; i< 10; i++){
    setTimeout(() => {
    console.log(i);
    }, 1000)
    }

    // method 2
    for (var i = 0; i< 10; i++){
    setTimeout((i) => {
    console.log(i);
    }, 1000,i)
    }

    // method 3
    for (var i = 0; i< 10; i++){
    (function(i){
    setTimeout(() => {
    console.log(i);
    }, 1000)
    })(i)
    }

    // method 4
    for (var i = 0; i< 10; i++){
    (()=>{
    var temp = i // clousure
    setTimeout(() => {
    console.log(temp);
    }, 1000)
    })()
    }

    // method 5
    var arr= []
    for (var i = 0; i< 10; i++){
    arr.push(i)
    setTimeout(() => {
    console.log(arr.shift());
    }, 1000)
    }

promise 执行顺序

核心 理解promise resolve后才将then 放入微任务,await a; 语句b 实际上是 Promise.resovle(a).then(b), a 如果不是返回promise,那么b会被马上放入微任务中,如果a返回了promise,这个promise resolved后b才被放入微任务中。
由于因为async await 本身就是promise+generator的语法糖。所以await后面的代码是microtask。
题目来源

原始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}

console.log('script start');

setTimeout(function() {
console.log('setTimeout');
}, 0)

async1();

new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
/*
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/

变式1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
//async2做出如下更改: 注意这里不是返回一个promise,函数返回是undefined
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
}
console.log('script start');

setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();

new Promise(function(resolve) {
console.log('promise3');
resolve();
}).then(function() {
console.log('promise4');
});

console.log('script end');
/*
script start
async1 start
promise1
promise3
script end
promise2
async1 end
promise4
setTimeout
*/

变式2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
async function async1() {
console.log('async1 start');
await async2();
//更改如下:
setTimeout(function() {
console.log('setTimeout1')
},0)
}
async function async2() {
//更改如下:
setTimeout(function() {
console.log('setTimeout2')
},0)
}
console.log('script start');

setTimeout(function() {
console.log('setTimeout3');
}, 0)
async1();

new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
/*
script start
async1 start
promise1
script end
promise2
setTimeout3
setTimeout2
setTimeout1
*/

变式3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
async function a1 () {
console.log('a1 start')
await a2()
console.log('a1 end')
}
async function a2 () {
console.log('a2')
}

console.log('script start')

setTimeout(() => {
console.log('setTimeout')
}, 0)

Promise.resolve().then(() => {
console.log('promise1')
})

a1()

let promise2 = new Promise((resolve) => {
resolve('promise2.then')
console.log('promise2')
})

promise2.then((res) => {
console.log(res)
Promise.resolve().then(() => {
console.log('promise3')
})
})
console.log('script end')
/*
script start
a1 start
a2
promise2
script end
promise1
a1 end
promise2.then
promise3
setTimeout
*/

promise 串行 并行 all race finally

all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// promise all
function promiseAll(promises){
return new Promise(function(resolve,reject){
if(!Array.isArray(promises)){
return reject(new TypeError("argument must be anarray"))
}
var countNum=0;
var promiseNum=promises.length;
var resolvedvalue=new Array(promiseNum);
for(var i=0;i<promiseNum;i++){
(function(i){
Promise.resolve(promises[i]).then(function(value){
countNum++; // 记录成功返回的数量
resolvedvalue[i]=value;
if(countNum===promiseNum){
return resolve(resolvedvalue)
}
},function(reason){
return reject(reason)
)
})(i)
}
})
}
// test
var p1=Promise.resolve(1),
p2=Promise.resolve(2),
p3=Promise.resolve(3);
promiseAll([p1,p2,p3]).then(function(value){
console.log(value)
})

ps: Promise.all错误处理
当promise捕获到error 的时候,代码吃掉这个异常,返回resolve,约定特殊格式表示这个调用成功,catch后仍然返回一个resovle的promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var p1 =new Promise(function(resolve,reject){
setTimeout(function(){
resolve(1);
},0)
});
var p2 = new Promise(function(resolve,reject){
setTimeout(function(){
resolve(2);
},200)
});
var p3 = new Promise(function(resolve,reject){
setTimeout(function(){
try{
console.log(XX.BBB);
}
catch(exp){
resolve("error");
}
},100)
});
Promise.all([p1, p2, p3]).then(function (results) {
console.log("success")
console.log(results);
}).catch(function(r){
console.log("err");
console.log(r);
});

race

1
2
3
4
5
6
// 实现promise race
Promise._race = promises => new Promise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, reject)
})
})

finally

finally本质上是then方法的特例。finally方法总是会返回原来的值。

1
2
3
4
5
6
7
8
// promise fainally
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};

serial

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function serial(reqList){
return reqList.reduce((total, promiseFunc)=>{
return total.then(x=>{return promiseFunc(x)})
}, Promise.resolve())
}

// 打印出1 2 4 8 16 32
function serial(promiseList){
return promiseList.reduce((total, promise)=>{
return total.then(x=>{return promise(x*2)})
}, Promise.resolve(Math.pow(2,-1)))
}

function createPromise(){
return function(n){
return new Promise((resolve)=>{
console.log(n)
setTimeout(resolve,2000,n)
})
}
}
var list = [1,2,3,4,5].map(x=>createPromise())
serial(list)

parallel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 实现并行
(function(){
var max =3
var sendList = []
window.parallel= function(reqList){
while (sendList.length<max && reqList.length>0){
var index = sendList.length
var func = reqList.shift()
sendList.push(func)
func().then(x=>{
sendList.splice(index,1)
if(reqList.length>0){
parallel(reqList)
}
})
}
}

})()

// test
function createPromise(n){
return function(){
return new Promise((resolve)=>{
console.log(n)
setTimeout(resolve,3000,n)
})
}
}

var list = [1,2,3,4,5,6,7,8,9,10].map(x=>createPromise(x))
parallel(list)

拓展

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function machine() {

}
machine('ygy').execute()
// start ygy
machine('ygy').do('eat').execute();
// start ygy
// ygy eat

machine('ygy').wait(5).do('eat').execute();
// start ygy
// wait 5s(这里等待了5s)
// ygy eat
machine('ygy').waitFirst(5).do('eat').execute();
// wait 5s
// start ygy
// ygy eat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 本质 串行执行promise
function machine(name){
return new Machine(name)
}
function wrapPromise(callback,delay=0){
return new Promise((resolve, reject)=>{
setTimeout(()=>{
callback && callback()
console.log('call', delay)
resolve()
},delay*1000)
})
}

function Machine(name){
this.name = name
this.queue = []
this.queue.push(()=>{
return wrapPromise(()=>{console.log(`start ${this.name}`)})
})
}


Object.assign(Machine.prototype,{
do(action){
this.queue.push(()=>{
return wrapPromise(()=>{console.log(`${this.name} ${action}`)})
})
return this
},
wait(second){
this.queue.push(()=>{
return wrapPromise(null, second)
})
return this
},
waitFirst(second){
this.queue.unshift(()=>{
return wrapPromise(null, second)
})
return this
},
execute(){
this.queue.reduce((total, promiseFunc)=>{
return total.then(x=>{return promiseFunc()})
}, Promise.resolve())
}
})

API详解

  • Array.prototype.reduce((callback,[initialValue])
    reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。

    callback (执行数组中每个值的函数,包含四个参数)
    1、previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
    2、currentValue (数组中当前被处理的元素)
    3、index (当前元素在数组中的索引)
    4、array (调用 reduce 的数组)

    initialValue (作为第一次调用 callback 的第一个参数。)
    如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引,prev是数组的第一个值。如果提供initialValue,从索引0开始,prevshi initialValue。
    Point To MDN

  • arrayObject.prototype.concat(arrayX,arrayX)
    注意参数arrayX如果是数组,则将arrayX的成员添加到arrayObject,如果不是则,直接添加arrayX到arrayObject

  • arrayObject.prototype.map(callback, thisArg)
    map 方法会给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值(包括 undefined)组合起来形成一个新数组
    函数会被 自动传入三个参数:数组元素,元素索引,原数组本身

    Point To MDN

  • parseInt(string, radix))
    radix:一个介于2和36之间的整数(数学系统的基础),当未指定基数时,不同的实现会产生不同的结果,通常将值默认为10。
    返回解析后的整数值。 如果被解析参数的第一个字符无法被转化成数值类型,则返回 NaN
    parseInt(‘123’, 5) // 将’123’看作5进制数,返回十进制数38 => 15^2 + 25^1 + 3*5^0 = 38


    在基数为 undefined,或者基数为 0 或者没有指定的情况下,JavaScript 作如下处理:

    如果字符串 string 以”0x”或者”0X”开头, 则基数是16 (16进制).
    如果字符串 string 以”0”开头, 基数是8(八进制)或者10(十进制),那么具体是哪个基数由实现环境决定。ECMAScript 5 规定使用10,但是并不是所有的浏览器都遵循这个规定。因此,永远都要明确给出radix参数的值。
    如果字符串 string 以其它任何值开头,则基数是10 (十进制)。
    Point To MDN

  • Object.prototype.valueOf()
    你很少需要自己调用valueOf方法;当遇到要预期的原始值的对象时,JavaScript会自动调用它。
    返回 指定对象的原始值。如果对象没有原始值,则valueOf将返回对象本身。JavaScript的许多内置对象都重写了该函数,以实现更适合自身的功能需要。
    Point To MDN

  • Object.prototype.toString()
    当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。
    返回指定对象的原始值。
    如果此方法在自定义对象中未被覆盖,toString() 返回 “[object type]”,其中type是对象的类型。
    Point To MDN

    参考

    JS数组reduce()方法详解及高级技巧
    每天一道面试题
    从 (a==1&&a==2&&a==3) 成立中看javascript的隐式类型转换