乐趣区

关于程序员:前端面试常见编程题汇总

JS 编程题

阿里面试题(变量赋值)

let a = {n: 1,};
let b = a;
a.x = a = {n: 2,};

console.log(a.x); // undefined
console.log(b); // {n: 1, x: {n: 2}}
let a = 12,
  b = 12;
function fn() {// console.log(a, b); // Uncaught ReferenceError: Cannot access 'a' before initialization at fn
  console.log(b); // 12
  let a = (b = 13);
  console.log(a, b); // 13 13
}
fn();
console.log(a, b); // 12 13
let i = 1;
let fn = (i) => (n) => console.log(n + ++i);
let f = fn(1);
f(2); // 4
fn(3)(4); // 8
f(5); // 8
console.log(i); // 1
var n = 0;
function a() {
  var n = 10;
  function b() {
    n++;
    console.log(n);
  }
  b();
  return b;
}
var c = a();
c();
console.log(n);

// 11 12 0

参考 js 间断赋值的问题

美团面试题

var obj = {
  2: 3,
  3: 4,
  length: 2,
  push: Array.prototype.push,
};
obj.push(1);
obj.push(2);
console.log(obj);
// {2: 1, 3: 2, length: 4, push: ƒ} 解析:因为对象的 length 为 2, 所以 push 1 2 会笼罩 2 3 的值

// 比照
var obj = {
  length: 2,
  push: Array.prototype.push,
};
obj.push(1);
obj.push(2);
obj.push(3);
console.log(obj);
// {2: 1, 3: 2, 4: 3, length: 5, push: ƒ}

阿里经典面试题(考查变量晋升 / 静态方法 / 实例办法 / 原型办法调用)

function Foo() {getName = function () {console.log(1);
  };
  return this;
}
Foo.getName = function () {console.log(2);
};
Foo.prototype.getName = function () {console.log(3);
};
var getName = function () {console.log(4);
};
function getName() {console.log(5);
}

Foo.getName(); // 2 解析:调用函数的静态方法
getName(); // 4 解析:函数表达式会笼罩函数申明式办法
Foo().getName(); // 1 解析:调用函数本身的办法
getName(); // 1 解析: 受前一行代码执行影响相当于调用 this.getName(), 其中 this 指向 window,因为 Foo 办法外面定义 getName 的时候没有申明, 所以变成了全局变量
new Foo.getName(); // 2 解析:相当于执行 new (Foo.getName)()
new Foo().getName(); // 3 解析:调用函数的实例办法, 相当于执行(new Foo()).getName()
new new Foo().getName(); // 3 解析:相当于执行 new (new Foo()).getName)()
// 操作运算符的优先级: () > new > .

字节面试题(递归 / 微工作 / 宏工作)

function fn() {fn();
}
fn(); // Uncaught RangeError: Maximum call stack size exceeded

var num = 0;
function fn() {console.log(num++);
  setTimeout(fn, 1000);
}
fn(); // 能够失常执行,为什么?// 解析:起因是因为 setTImeout 属于异步宏工作,不在主线程栈内存中

字节编程题

const list = [1, 2, 3];
const square = (num) => {return new Promise((resolve, reject) => {setTimeout(() => {resolve(num * num);
    }, 1000);
  });
};

function test() {list.forEach(async (x) => {const res = await square(x);
    console.log(res);
  });
}
test();

// 执行后果:1s 之后输入 1 4 9

// 不能批改 square 办法,实现每隔一秒输入后果
const list = [1, 2, 3];
const square = (num) => {return new Promise((resolve, reject) => {setTimeout(() => {resolve(num * num);
    }, 1000);
  });
};

function test() {list.forEach((x, index) => {setTimeout(async () => {const res = await square(x);
      console.log(res);
    }, index * 1000);
  });
}
test();

笔者自出的立刻执行函数赋值编程题

var a = 111;
(function a() {console.log(a);
  a = 222;
  console.log(a);
})();
console.log(a);

// 输入后果:// ƒ a() {//   console.log(a);
//   a = 222;
//   console.log(a);
// }
// ƒ a() {//   console.log(a);
//   a = 222;
//   console.log(a);
// }
// 111

var c = 111;
(function c(c) {console.log(c);
  c = 123;
  console.log(c);
})(c);
console.log(c);

// 输入后果:// 111
// 123
// 111

快手面试编程题

实现 add(1)(2)(3)(4)(5).sum()和 add(1)(2, 3)(4)(5).sum()参数不定的累加成果

let add = (...args) => {let foo = (...newArgs) => {return add(...args, ...newArgs);
  };
  foo.toString = () => {return args.reduce((a, b) => a + b);
  };
  foo.sum = () => {return foo.toString();
  };
  return foo;
};

// 测试后果:console.log(add(1)(2)(3)(4)(5).sum()); // 15
console.log(add(1)(2, 3)(4)(5).sum()); // 15

// 优化版代码
let add = (...args) => {let foo = (...newArgs) => {return add(...args, ...newArgs);
  };
  foo.sum = () => {return args.reduce((a, b) => a + b);
  };
  return foo;
};

// 测试后果:console.log(add(1)(2)(3)(4)(5).sum()); // 15
console.log(add(1)(2, 3)(4)(5).sum()); // 15

求字符串 '(1+2)*3’ 运算后果(携程面试题)

办法一:应用 eval 办法

// 非严格模式下
let str = '(1+2)*3';
let result = eval(str);
console.log(result);

// 严格模式下报错:Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src'strict-dynamic'

办法二:应用 new Funciton 办法

// 非严格模式下
let str = '(1+2)*3';
function strCalc(str) {return new Function(`return ${str}`)();}
let result = strCalc(str);
console.log(result);

// 严格模式下报错:Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src'strict-dynamic'

办法三:利用 script 标签内容为可执行代码

let str = '(1+2)*3';
function strCalc(str) {let myScript = document.createElement('script');
  myScript.innerHTML = `window.golal_calc_result=${str}`; // 把执行后果保留到全局对象
  document.body.appendChild(myScript);
  document.body.removeChild(myScript);
}
strCalc(str);
console.log(window.golal_calc_result);

考查 JS 事件循环微工作和宏工作

console.log(1);
setTimeout(function () {console.log(2);
}, 0);
var promise = new Promise(function (resolve, reject) {console.log(3);
  setTimeout(function () {console.log(4);
    resolve();}, 1000);
});
promise.then(function () {console.log(5);
  setTimeout(function () {console.log(6);
  }, 0);
});
console.log(7);
// 输入后果程序:1 3 7 2 4 5 6
// 解析:JS 代码执行优先级:主线程 -> 微工作 -> 宏工作
var promise = new Promise(function (resolve, reject) {setTimeout(function () {console.log(1);
    resolve();}, 3000);
});
promise
  .then(function () {setTimeout(function () {console.log(2);
    }, 2000);
  })
  .then(function () {setTimeout(function () {console.log(3);
    }, 1000);
  })
  .then(function () {setTimeout(function () {console.log(4);
    }, 0);
  });
// 输入后果:3s 后输入 1 和 4,再过 1s 输入 3,再过 1s 输入 2
// 解析:promise.then()办法要等 resolve()执行当前,才会执行前面的 then 办法,前面的这些办法按定时器异步流程解决

理解更多

async/await 和 setTimeout 以及 promise(字节面试题)

async function async1() {console.log("async1 start");
  await async2();
  console.log(`async1 end`);
}

async function async2() {console.log("async2");
}

console.log("script start");

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

async1();

new Promise((resolve, reject) => {console.log("promise1");
  resolve();}).then(() => {console.log("promise2");
});

console.log("script end");

// 输入后果:// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout

promise 串行问题(腾讯文档)

let promiseArr = [() => {
    return new Promise(res => {console.log('run 1', Date.now());
      res('run 1 resolve');
    });
  },
  () => {
    return new Promise(res => {console.log('run 2', Date.now());
      res('run 2 resolve');
    });
  },
  () => {
    return new Promise(res => {console.log('run 3', Date.now());
      res('run 3 resolve');
    });
  },
]

async function fn () {for (let i = 0; i < promiseArr.length; i++) {
    // 串行打印 console.log;
    // await promiseArr[i]();
    // 串行打印 console.log 并执行 resolve
    await promiseArr[i]().then((value) => {console.log(value);
    });
  }
}
fn();

事件循环(字节面试题)

function test () {console.log(1);
  Promise.resolve().then(test);
}
test();
setTimeout(() => {console.log(2)}, 0)

// 打印后果:// 始终输入 1 不会执行 setTimeout 外面的回调函数

promise、async/await

async function fn() {let data = await (() => 4)();
    console.log(data);
}
console.log(1);
new Promise(resolve => resolve(console.log(2))).then(data => console.log(data));
console.log(3);
fn();

// 打印后果:// 1
// 2
// 3
// undefined
// 4
// Promise {<fulfilled>: undefined}
// 字节面试题
new Promise((reslove, reject) => {reject();
}).then(null, () => {console.log(1);
}).then(() => {console.log(2);
}).then(() => {console.log(3);
});

// 打印后果:1 2 3
new Promise((reslove, reject) => {reject();
}).then(null, () => {console.log(1);
}).then(() => {new Promise((reslove, reject) => {reject();
    }).then(null, () => {console.log('a');
    }).then(() => {console.log('b');
    }).then(() => {console.log('c');
    })
}).then(() => {console.log(3);
})

// 打印后果:// 1
// a
// 3
// b
// c
new Promise((resolve, reject) => {resolve();
}).then(null, () => {console.log(1);
}).then(() => {new Promise((resolve, reject) => {console.log(2);
        resolve();}).then(null, () => {console.log('a');
    }).then(() => {console.log('b');
    }).then(() => {console.log('c');
    })
}).then(() => {console.log(3);
})

// 打印后果:// 2
// 3
// b
// c
new Promise((resolve, reject) => {reject();
}).then(null, () => {console.log(1);
}).then(() => {new Promise((resolve, reject) => {console.log(2);
        reject();}).then(null, () => {console.log('a');
    }).then(() => {console.log('b');
    }).then(() => {console.log('c');
    })
}).then(() => {console.log(3);
})
// 1
// 2
// a
// 3
// b
// c

腾讯视频编程题

function foo () {
  var a = 0;
  return function () {console.log(a++);
  }
}
var f1 = foo(),
f2 = foo();
f1(); // 0
f1(); // 1
f2(); // 0
function Page() {console.log(this);
  return this.hosts;
}
Page.hosts = ['h1'];
Page.prototype.hosts = ['h2'];
var p1 = new Page();
var p2 = Page();
console.log(p1.hosts); // undefined
console.log(p2.hosts); // Uncaught TypeError: Cannot read property 'hosts' of undefined

如果让一个不可迭代对象,变成可迭代

var obj = {
  0: 0,
  1: 1,
  length: 2,
};
for (i of obj) {console.log(i);
}
// 报错:Uncaught TypeError: obj is not iterable

var obj = {
  0: 0,
  1: 1,
  length: 2,
  [Symbol.iterator]: Array.prototype[Symbol.iterator],
};
for (i of obj) {console.log(i);
}

// 原理:可迭代对象都领有 @@iterator 属性

实现深拷贝

罕用的简略实现形式:类型判断 + 递归

function deepClone(obj) {var newObj = obj instanceof Array ? [] : {};
  for (var i in obj) {newObj[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i];
  }
  return newObj;
}

// test
var obj = {
  number: 1,
  string: "abc",
  bool: true,
  undefined: undefined,
  null: null,
  symbol: Symbol("s"),
  arr: [1, 2, 3],
  date: new Date(),
  userInfo: {
    name: "Better",
    position: "front-end engineer",
    skill: ["React", "Vue", "Angular", "Nodejs", "mini programs"],
  },
  func: function () {console.log("hello better");
  },
};
console.log(deepClone(obj));

从打印的后果来看,这种实现形式还存在很多问题:这种形式只能实现特定的 object 的深度复制(比方对象、数组和函数),不能实现 null 以及包装对象 Number,String,Boolean,以及 Date 对象,RegExp 对象的复制。

一行代码实现形式:联合应用 JSON.stringify()JSON.parse()

var obj = {
  number: 1,
  string: "abc",
  bool: true,
  undefined: undefined,
  null: null,
  symbol: Symbol("s"),
  arr: [1, 2, 3],
  date: new Date(),
  userInfo: {
    name: "Better",
    position: "front-end engineer",
    skill: ["React", "Vue", "Angular", "Nodejs", "mini programs"],
  },
  func: function () {console.log("hello better");
  },
};

var copyObj = JSON.parse(JSON.stringify(obj));

console.log(copyObj);

从打印后果能够得出以下论断:

  1. undefinedsymbolfunction类型间接被过滤掉了
  2. date类型被主动转成了字符串类型

实现一个 bind 函数

原理:应用 apply() 或者 call() 办法

初始版本

Function.prototype.customBind = function (context) {
  var self = this; // 保留函数的上下文
  var args = [].slice.call(arguments, 1); // 获取自定义 bind 函数的参数
  return function () {args = args.concat([].slice.call(arguments)); // 获取自定义 bind 函数返回函数传入的参数
    return self.apply(context, args);
  };
};

var obj = {
  name: "Better",
  position: "front-end engineer",
};
var func = function (age) {console.log("name", this.name);
  console.log("position", this.position);
  console.log("age", age);
};
var f = func.customBind(obj, 18);
f();

思考到原型链(最终版)

Function.prototype.customBind = function (context) {
  // 必须在函数上应用,否则抛出谬误
  if (typeof this !== "function") {
    throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
  }

  var self = this; // 保留函数的上下文
  var args = Array.prototype.slice.call(arguments, 1); // 获取自定义 bind 函数的参数

  var fNOP = function () {};

  var fBound = function () {var bindArgs = Array.prototype.slice.call(arguments); // 获取自定义 bind 函数返回函数传入的参数
    return self.apply(
      this instanceof fNOP ? this : context,
      args.concat(bindArgs)
    );
  };

  // 这里应用寄生组合继承
  fNOP.prototype = this.prototype;
  fBound.prototype = new fNOP();
  return fBound;
};

// 测试
var obj = {
  name: "Better",
  position: "front-end engineer",
};
var func = function (age) {console.log("name", this.name);
  console.log("position", this.position);
  console.log("age", age);
};
var f = func.customBind(obj, 18);
f();

点击理解更多

性能优化之防抖和节流

对于频繁触发的事件,比方,scrollkeyupmouseoverresize等事件,如果不做一些非凡解决的话,可能会影响性能,甚至造成页面卡顿。

防抖和节流就能很好的解决这类问题。

防抖

定义:在规定工夫内,屡次触发事件后,事件处理函数只执行一次,并且是在触发操作完结后执行。

原理:对处理函数进行延时操作,若设定的延时到来之前,再次触发事件,则革除上一次的延时操作定时器,从新定时。

function debounce(fn, wait) {
  var timeId = null;
  return function () {
    var context = this; // 保留绑定事件的对象,如 document
    var args = arguments; // 获取事件参数,如 event
    timeId && clearTimeout(timeId); // 如果规定工夫内(wait)再次触发事件,则革除定时器
    timeId = setTimeout(function () {fn.apply(context, args); // 应用 apply 办法把 fn 函数的 this 指向事件对象
    }, wait);
  };
}

// 测试
function func() {console.log(111);
}
document.addEventListener("mouseover", debounce(func, 1000));

如果心愿立刻执行一次,而后等到进行触发 n 秒后,才能够从新触发执行。

function debounce(fn, wait, immediately) {
  var timeId = null;
  return function () {
    var context = this;
    var args = arguments;
    timeId && clearTimeout(timeId);
    if (immediately) {
      // 如果曾经执行过,则不再执行
      var canExecute = !timeId;
      timeId = setTimeout(function () {timeId = null;}, wait);
      if (canExecute) {fn.apply(context, args);
      }
    } else {timeId = setTimeout(function () {fn.apply(context, args); // 应用 apply 办法把 fn 函数的 this 指向事件对象
      }, wait);
    }
  };
}

// 测试
function func() {console.log(111);
}
document.addEventListener("mouseover", debounce(func, 1000, true));
// document.addEventListener('mouseover', debounce(func, 1000));

点击理解更多

节流

定义:触发函数事件后,规定工夫距离内无奈间断调用,只有上一次函数执行后,过了规定的工夫距离,能力进行下一次的函数调用。

原理:如果你继续触发事件,每隔一段时间,只执行一次事件。

对于节流的实现,有两种支流的实现形式,一种是应用工夫戳,一种是设置定时器。

应用工夫戳,当触发事件的时候,咱们取出以后的工夫戳,而后减去之前的工夫戳(最一开始值设为 0),如果大于设置的工夫周期,就执行函数,而后更新工夫戳为以后的工夫戳,如果小于,就不执行。

// 应用工夫戳
function throttle(fn, wait) {
  var prev = 0;
  return function () {
    var context = this;
    var args = arguments;
    var now = new Date().getTime();
    // if (!prev) prev = now;
    if (now - prev > wait) {
      // 如果工夫距离大于 wait,执行函数
      fn.apply(context, args);
      prev = now; // 把以后工夫赋值给前一个工夫
    }
  };
}

// 测试
function func() {console.log(111);
}
document.addEventListener("mouseover", throttle(func, 1000));

应用定时器:当触发事件的时候,咱们设置一个定时器,再触发事件的时候,如果定时器存在,就不执行,直到定时器执行,而后执行函数,清空定时器,这样就能够设置下个定时器。

// 应用定时器
function throttle(fn, wait) {
  var timeId = null;
  return function () {
    var context = this;
    var args = arguments;
    if (!timeId) {
      // 如果没有定时器
      timeId = setTimeout(function () {fn.apply(context, args);
        timeId = null;
      }, wait);
    }
  };
}

// 测试
function func() {console.log(111);
}
document.addEventListener("mouseover", throttle(func, 1000));

总结:

  1. 第一种事件会立即执行,第二种事件会在 n 秒后第一次执行
  2. 第一种事件进行触发后没有方法再执行事件,第二种事件进行触发后仍然会再执行一次事件

点击理解更多

应用 setTimeout 实现 setInterval 性能

咱们平时开发中尽量避免应用 setInterval 反复定时器,这种反复定时器的规定有两个问题:

  1. 某些距离会被跳过
  2. 多个定时器的代码执行工夫可能会比预期小
var i = 0;
function count() {console.log(i++);
  setTimeout(count, 1000);
}
setTimeout(count, 1000);

或者应用 arguments.callee

var i = 0;
setTimeout(function () {
  // do something
  console.log(i++);
  setTimeout(arguments.callee, 1000);
}, 1000);

如何实现 sleep 成果

办法一:应用 promise

function sleep(time) {return new Promise(function (resolve, reject) {console.log("start");
    setTimeout(function () {resolve();
    }, time);
  });
}

sleep(1000).then(function () {console.log("end");
});
// 先输入 start,提早 1000ms 后输入 end

办法二:应用 async/await

function sleep(time) {return new Promise(function (resolve, reject) {setTimeout(function () {
      // do something
      resolve();}, time);
  });
}

async function test() {console.log("start");
  var result = await sleep(1000);
  console.log("end");
  return result;
}

test(); // 先输入 start,提早 1000ms 后输入 end

办法三:应用 generate

function* sleep(time) {yield new Promise((resolve, reject) => {console.log("start");
    setTimeout(() => {
      // do something
      resolve();}, time);
  });
}

sleep(1000)
  .next()
  .value.then(() => {console.log("end");
  }); // 先输入 start,提早 1000ms 后输入 end

如何实现 a == 1 && a == 2 && a == 3 为 true

办法一:联合应用数组的 toString()shift()办法

var a = [1, 2, 3];
// a.join = a.shift;
// a.valueOf = a.shift;
a.toString = a.shift;

console.log(a == 1 && a == 2 && a == 3); // true

原理:当简单类型数据与根本类型数据作比拟时会产生隐性转换,会调用 toString() 或者 valueOf() 办法

办法二:原理和办法一一样都是批改 toString() 办法

var a = {
  value: 1,
  toString: function () {return a.value++;},
};
console.log(a == 1 && a == 2 && a == 3); // true

实现 add(1)(2)(3)这类办法以及扩大办法

// 一般写法
var add = function (a) {return function (b) {return function (c) {return a + b + c;};
  };
};

console.log(add(1)(2)(3)); // 6

// 扩大写法
function addExtend(x) {
  var sum = x;
  var temp = function (y) {
    sum = sum + y;
    return temp;
  };
  temp.toString = function () {return sum;};
  return temp;
}

console.log(addExtend(1)(2)(3)); // ƒ 6
console.log(typeof addExtend(1)(2)(3)); // function
console.log(Number(addExtend(1)(2)(3))); // 6
console.log(Number(addExtend(1)(2)(3)(4)(5))); // 15

闭包和工作队列例子

for (var i = 0; i < 5; i++) {
  setTimeout((function (i) {console.log(i);
    })(i),
    i * 1000
  );
}

// 测试后果:立刻输入 0 1 2 3 4
for (var i = 0; i < 5; i++) {setTimeout(function () {console.log(i);
  }, i * 1000);
}

// 测试后果:立刻输入 5 而后每隔一秒输入 5 总共输入 5 次
for (let i = 0; i < 5; i++) {setTimeout(function () {console.log(i);
  }, i * 1000);
}

// 测试后果:立刻输入 0 而后每隔一秒别离输入 1 2 3 4

应用正则表达式匹配 url 参数键值对(兼容 url 中蕴含多个? 和 #号)

let reg = /([^?&#]+=[^?&#]+)/g;
let str =
  "https://c2b.brightoilonline.com/bdh5/channel.html?chelun_params=ad0bb3e5fc3b3bfeb9b53cc9686eb1aaaecb8a53e9f4a77c5ed68714123b25913219200cba48d9044c7b48325436960f42c1cde18f349ef63ab53ee791970243a8ba79f9a2dc8aa1#/chelunGasList?pcode=c2b8g717rkj603o15005&fromApp=true";

console.log(str.match(reg));
// 测试后果:["chelun_params=ad0bb3e5fc3b3bfeb9b53cc9686eb1aaaecb…f42c1cde18f349ef63ab53ee791970243a8ba79f9a2dc8aa1", "pcode=c2b8g717rkj603o15005", "fromApp=true"]

str =
  "https://c2b-test2.brightoilonline.com/bdh5/channel.html#/chelungaslist?pcode=c2b0293jm44x97an6339&fromApp=true";
console.log(str.match(reg));
// 测试后果:["pcode=c2b0293jm44x97an6339", "fromApp=true"]

str =
  "https://c2b-test2.brightoilonline.com/bdh5/channel.html?pcode=c2b0293jm44x97an6339&fromApp=true";
console.log(str.match(reg));
// 测试后果:["pcode=c2b0293jm44x97an6339", "fromApp=true"]

解析 URL Params 为对象

let url =
  "http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled";
parseParam(url);
/* 后果
{ user: 'anonymous',
  id: [123, 456], // 反复呈现的 key 要组装成数组,能被转成数字的就转成数字类型
  city: '北京', // 中文需解码
  enabled: true, // 未指定值得 key 约定为 true
}
*/

function parseParam(url) {const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 前面的字符串取出来
  const paramsArr = paramsStr.split("&"); // 将字符串以 & 宰割后存到数组中
  let paramsObj = {};
  // 将 params 存到对象中
  paramsArr.forEach((param) => {if (/=/.test(param)) {
      // 解决有 value 的参数
      let [key, val] = param.split("="); // 宰割 key 和 value
      val = decodeURIComponent(val); // 解码
      val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字

      if (paramsObj.hasOwnProperty(key)) {
        // 如果对象有 key,则增加一个值
        paramsObj[key] = [].concat(paramsObj[key], val);
      } else {
        // 如果对象没有这个 key,创立 key 并设置值
        paramsObj[key] = val;
      }
    } else {
      // 解决没有 value 的参数
      paramsObj[param] = true;
    }
  });
  return paramsObj;
}


// 进阶:应用原生 URLSearchParams 对象
function optParseQuery(url) {const u = new URL(url)
  const sp = new URLSearchParams(u.search)
  return sp.entries()}
console.log(optParseQuery(url))
// {
//     "user": "anonymous",
//     "id": "456",
//     "city": "北京",
//     "enabled": ""
// }

数组对象去重

/**
* @param {Array} arr 去重的数组对象
* @param {String} key 依据 key 进行去重
* @return {Array} 去重后的数组对象
*/
// 办法一
function uniqueArrObjByKey (arr, key) {const map = Object.create(null)
    const res = []
    arr.forEach(item => {if (!map[item[key]]) {map[item[key]] = true
          res.push(item)
        }
    })
    return res
}

// 办法二
function uniqueArrObjByKey (arr, key) {const map = Object.create(null)
    const res = arr.reduce((cur, next) => {map[next[key]] ? '' : map[next[key]] = true && cur.push(next)
      return cur
    }, []) // 设置 cur 默认类型为数组,并且初始值为空的数组
    return res
}

// 测试
var arr = [{name: '廖小新', age: 18}, {name: '廖小物', age: 18}, {name: '廖小新', age: 20}, {name: '廖小物', age: 18}, {name: '廖小念', age: 18}]
console.log(uniqueArrObjByKey(arr, 'name'))
console.log(uniqueArrObjByKey(arr, 'age'))

给定两个数组,编写一个函数来计算它们的交加

阐明:

  • 输入后果中的每个元素肯定是惟一的。
  • 咱们能够不思考输入后果的程序。
// 办法一:应用 filter 和 set 办法
function findIntersection(arr1, arr2) {return [...new Set(arr1.filter((item) => arr2.includes(item)))];
}

var arr1 = [1, 2, 2, 3];
var arr2 = [2, 3, 3, 4];
findIntersection(arr1, arr2); // [2, 3]

// 办法二:应用 map 哈希表
function findIntersection(arr1, arr2) {var res = new Set();
  var set1 = new Set(arr1);
  var set2 = new Set(arr2);
  for (let item of set2) {if (set1.has(item)) {res.add(item);
    }
  }
  return [...res];
}
var arr1 = [1, 2, 2, 3];
var arr2 = [2, 3, 3, 4];
findIntersection(arr1, arr2); // [2, 3]

function findIntersection(arr1, arr2) {var map = {};
  var res = [];
  arr1.forEach((item) => {map[item] = true;
  });
  arr2.forEach((item) => {if (map[item]) {res.push(item);
    }
  });
  res = [...new Set(res)];
  return res;
}
var arr1 = [1, 2, 2, 3];
var arr2 = [2, 3, 3, 4];
findIntersection(arr1, arr2); // [2, 3]

// 扩大求三个数组的交加
function findThreeIntersection(arr1, arr2, arr3) {const map1 = new Set(arr1);
  const map2 = new Set(arr2);
  const map3 = new Set(arr3);
  const res = [];
  arr1.forEach((item) => {if (map2.has(item) && map3.has(item)) {res.push(item);
    }
  });
  return [...new Set(res)];
}
var arr1 = [1, 2, 2, 3, 5];
var arr2 = [2, 3, 3, 4, 3, 5];
var arr3 = [1, 2, 3, 3, 4, 5];
findThreeIntersection(arr1, arr2, arr3); // [2, 3]

更多汇合操作参考 Set

es6 尾调用 - 尾递归

求正整数 n 的阶乘

function factorial (n) {if (n === 1) return 1
    return n * factorial(n - 1)
}
console.time()
factorial(1000)
console.timeEnd()
// default: 1.697998046875 ms
// 应用尾递归
function factorial (n, total) {if (n === 1) return total
    return factorial(n - 1,  n * total) 
}
console.time()
factorial(1000, 1)
console.timeEnd()

es6 尾调用 - 尾递归

本文由 mdnice 多平台公布

退出移动版