笔试题

[toc]

JavaScript

下面代码中 a 在什么情况下会打印 1?

  1. var a = ?;
  2. if(a == 1 && a == 2 && a == 3){
  3. console.log(1);
  4. }
  5. //解答
  6. var a = {
  7. value: 0,
  8. valueOf() {
  9. return ++this.value;
  10. }
  11. };

实现一个 sleep 函数?

比如 sleep(1000) 意味着等待1000毫秒,可从 Promise、Generator、Async/Await 等角度实现

  1. // Promise
  2. function sleep1(time) {
  3. return new Promise(resolve => {
  4. setTimeout(() => {
  5. resolve();
  6. }, time);
  7. })
  8. }
  9. sleep1(1000).then(() => console.log("sleep1"));
  10. // Generator
  11. function* sleep2(time) {
  12. return yield sleep1(time);
  13. }
  14. const s = sleep2(1500);
  15. s.next().value.then(() => console.log("sleep2"));
  16. // Async/Await
  17. async function sleep3(time) {
  18. await sleep1(time);
  19. }
  20. (async () => {
  21. await sleep3(2000);
  22. console.log("sleep3")
  23. })()

实现 (5).add(3).minus(2) 功能?

  1. Number.prototype.add = function(n) {
  2. return this + n;
  3. }
  4. Number.prototype.minus = function(n) {
  5. return this - n;
  6. }

instanceof 的实现原理

  1. while (x.__proto__) {
  2. if (x.__proto__ === y.prototype) {
  3. return true;
  4. }
  5. x.__proto__ = x.__proto__.__proto__;
  6. }
  7. if (x.__proto__ === null) {
  8. return false;
  9. }

考察this指向的题

  1. //请给出如下代码的打印结果
  2. function Foo() {
  3. Foo.a = function() {
  4. console.log(1)
  5. }
  6. this.a = function() {
  7. console.log(2)
  8. }
  9. }
  10. Foo.prototype.a = function() {
  11. console.log(3)
  12. }
  13. Foo.a = function() {
  14. console.log(4)
  15. }
  16. Foo.a();
  17. let obj = new Foo();
  18. obj.a();
  19. Foo.a();
  20. //print
  21. 打印结果: 4 2 1

考察赋值表达式

  1. var a = {n: 1};
  2. var b = a;
  3. a.x = a = {n: 2};
  4. console.log(a.x)
  5. console.log(b.x)
  6. //输出
  7. {n:2}
  8. undefined
  1. // example 1
  2. var a={}, b='123', c=123;
  3. a[b]='b';
  4. a[c]='c';
  5. console.log(a[b]);
  6. ---------------------
  7. // example 2
  8. var a={}, b=Symbol('123'), c=Symbol('123');
  9. a[b]='b';
  10. a[c]='c';
  11. console.log(a[b]);
  12. ---------------------
  13. // example 3
  14. var a={}, b={key:'123'}, c={key:'456'};
  15. a[b]='b';
  16. a[c]='c';
  17. console.log(a[b]);
  18. //输出:
  19. c b c

考察事件循环

  1. async function async1() {
  2. console.log('async1 start');
  3. await async2();
  4. console.log('async1 end');
  5. }
  6. async function async2() {
  7. console.log('async2');
  8. }
  9. console.log('script start');
  10. setTimeout(function() {
  11. console.log('setTimeout');
  12. }, 0)
  13. async1();
  14. new Promise(function(resolve) {
  15. console.log('promise1');
  16. resolve();
  17. }).then(function() {
  18. console.log('promise2');
  19. });
  20. console.log('script end');
  21. //运行结果:
  22. script start
  23. async1 start
  24. async2
  25. promise1
  26. script end
  27. async1 end
  28. promise2
  29. setTimeout

考察作用域

  1. var b = 10;
  2. (function b(){
  3. b = 20;
  4. console.log(b);
  5. })();
  6. //输出:
  7. ƒ b(){
  8. b = 20;
  9. console.log(b);
  10. }
  11. //原因:
  12. 作用域:执行上下文中包含作用域链;
  13. 特性:声明提前:一个声明在函数体内都是可见的,函数声明优先于变量声明;
  14. 在非匿名自执行函数中,函数变量为只读状态无法修改。
  1. var a = 10;
  2. (function () {
  3. console.log(a)
  4. a = 5
  5. console.log(window.a)
  6. var a = 20;
  7. console.log(a)
  8. })()
  9. //输出 undefined 10 20

手写 bind、call、apply

  1. Function.prototype.MyCall = function (context) {
  2. const args = [...arguments].slice(1);
  3. context.fn = this;
  4. const result = context.fn(...args);
  5. delete context.fn;
  6. return result;
  7. }
  8. Function.prototype.MyApply = function (context) {
  9. const args = arguments[1] || [];
  10. context.fn = this;
  11. const result = context.fn(...args);
  12. delete context.fn;
  13. return result;
  14. }
  15. Function.prototype.MyBind = function (context) {
  16. const args = [...arguments].slice(1);
  17. return function () {
  18. context.MyApply(context, args);
  19. }
  20. }

模拟 new 的实现

  1. function myNew(fn) {
  2. const newObj = Object.create(fn.prototype);
  3. result = fn.apply(newObj, [...arguments].slice(1));
  4. return typeof result === "object" ? result : newObj;
  5. }

数据结构

数据处理1(数组基础)

某公司 1 到 12 月份的销售额存在一个对象里面 如下:{1:222, 2:123, 5:888},请把数据处理为如下结构:[222, 123, null, null, 888, null, null, null, null, null, null, null]

  1. //解答:
  2. function convert(obj) {
  3. return Array.from({ length: 12 }).map((item, index) => obj[index] || null).slice(1);
  4. }

数据处理2(数组基础)

  1. var obj = {
  2. '2': 3,
  3. '3': 4,
  4. 'length': 2,
  5. 'splice': Array.prototype.splice,
  6. 'push': Array.prototype.push
  7. }
  8. obj.push(1)
  9. obj.push(2)
  10. console.log(obj)
  11. 输出:[,,1,2] length 4
  12. 解释:Array.prototype.push 将根据 length 将元素填充到对应位置并修改 length 属性 +1,所以输出的结果就是上述结果

数据处理3(消息队列)

要求设计 LazyMan 类,实现以下功能。

  1. LazyMan('Tony');
  2. // Hi I am Tony
  3. LazyMan('Tony').sleep(10).eat('lunch');
  4. // Hi I am Tony
  5. // 等待了10秒...
  6. // I am eating lunch
  7. LazyMan('Tony').eat('lunch').sleep(10).eat('dinner');
  8. // Hi I am Tony
  9. // I am eating lunch
  10. // 等待了10秒...
  11. // I am eating diner
  12. LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
  13. // Hi I am Tony
  14. // 等待了5秒...
  15. // I am eating lunch
  16. // I am eating dinner
  17. // 等待了10秒...
  18. // I am eating junk food
  19. //实现:
  20. class LazyManClass {
  21. constructor(name) {
  22. this.name = name;
  23. this.fns = [];
  24. console.log(`Hi I am ${this.name}`);
  25. setTimeout(() => {
  26. this.next();
  27. });
  28. return this
  29. }
  30. sleep(time) {
  31. const fn = () => {
  32. setTimeout(() => {
  33. console.log(`等待了${time}秒...`)
  34. this.next();
  35. }, time * 1000)
  36. }
  37. this.fns.push(fn);
  38. return this;
  39. }
  40. sleepFirst(time) {
  41. const fn = () => {
  42. setTimeout(() => {
  43. console.log(`等待了${time}秒...`)
  44. this.next();
  45. }, time * 1000)
  46. }
  47. this.fns.unshift(fn);
  48. return this;
  49. }
  50. eat(food) {
  51. const fn = () => {
  52. console.log(`I am eating ${food}`);
  53. this.next();
  54. }
  55. this.fns.push(fn);
  56. return this;
  57. }
  58. next() {
  59. const fn = this.fns.shift();
  60. fn && fn();
  61. }
  62. }
  63. const LazyMan = (name) => {
  64. return new LazyManClass(name);
  65. }
  66. // LazyMan('Tony');
  67. // Hi I am Tony
  68. // LazyMan('Tony').sleep(10).eat('lunch');
  69. // Hi I am Tony
  70. // 等待了10秒...
  71. // I am eating lunch
  72. // LazyMan('Tony').eat('lunch').sleep(10).eat('dinner');
  73. // Hi I am Tony
  74. // I am eating lunch
  75. // 等待了10秒...
  76. // I am eating diner
  77. LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
  78. // Hi I am Tony
  79. // 等待了5秒...
  80. // I am eating lunch
  81. // I am eating dinner
  82. // 等待了10秒...
  83. // I am eating junk food

数据处理4(数组扁平化)

  1. //已知如下数组:
  2. var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
  3. //编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组
  4. // 思路型
  5. function flatten(arr) {
  6. let flattenArr = [];
  7. for (let i = 0; i < arr.length; i++) {
  8. const item = arr[i];
  9. if (Array.isArray(item)) {
  10. flattenArr = flattenArr.concat(flatten(item))
  11. } else {
  12. if (flattenArr.indexOf(item) > -1) continue;
  13. flattenArr.push(item);
  14. }
  15. }
  16. return flattenArr.sort((a, b) => a - b);
  17. }
  18. // API 型
  19. function flatten2(arr) {
  20. return Array.from(new Set(arr.flat(Infinity))).sort((a, b) => a - b);
  21. }

数据处理5(数组大小写取反)

  1. //如何把一个字符串的大小写取反(大写变小写小写变大写),例如 ’AbC' 变成 'aBc'
  2. function convert(str) {
  3. let convertStr = "";
  4. for (let i = 0; i < str.length; i++) {
  5. const code = str.charCodeAt(i);
  6. if (code >= 97) {
  7. convertStr += String.fromCharCode(code - 32);
  8. } else {
  9. convertStr += String.fromCharCode(code + 32);
  10. }
  11. }
  12. return convertStr;
  13. }
  14. console.log(convert("AbC"));

数据处理6(数组随机排序)

  1. //如何实现数组的随机排序?
  2. // 随机数排序
  3. function random1(arr) {
  4. return arr.sort(() => Math.random() - .5);
  5. }
  6. // 随机插入排序
  7. function random2(arr) {
  8. const cArr = [...arr];
  9. const newArr = [];
  10. while (cArr.length) {
  11. const index = Math.floor(Math.random() * cArr.length);
  12. newArr.push(cArr[index]);
  13. cArr.splice(index, 1);
  14. }
  15. return newArr;
  16. }
  17. // 洗牌算法,随机交换排序
  18. function random3(arr) {
  19. const l = arr.length;
  20. for (let i = 0; i < l; i++) {
  21. const index = Math.floor(Math.random() * (l - i)) + i;
  22. const temp = arr[index];
  23. arr[index] = arr[i];
  24. arr[i] = temp;
  25. }
  26. return arr;
  27. }

网络

使用 Proxy + Fetch 实现类似于 axios 的基础 API

  1. const fetch = require("node-fetch");
  2. const axiosOriginal = {
  3. methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
  4. };
  5. const axios = new Proxy(axiosOriginal, {
  6. set() {
  7. throw new Error("Can't set any property");
  8. },
  9. get(target, name) {
  10. const method = name.toLocaleUpperCase();
  11. if (target.methods.indexOf(method) === -1) throw new Error(`Can't support method ${method}`);
  12. return (url, options) => {
  13. return fetch(url, {
  14. method,
  15. ...options
  16. }).then(res => res.text())
  17. }
  18. }
  19. });
  20. axios.get("http://www.baidu.com").then(res => console.log(res));
  21. axios.post("http://www.baidu.com").then(res => console.log(res));

React

考察setState

  1. class Example extends React.Component {
  2. constructor() {
  3. super();
  4. this.state = {
  5. val: 0
  6. };
  7. }
  8. componentDidMount() {
  9. this.setState({val: this.state.val + 1});
  10. console.log(this.state.val); // 第 1 次 log
  11. this.setState({val: this.state.val + 1});
  12. console.log(this.state.val); // 第 2 次 log
  13. setTimeout(() => {
  14. this.setState({val: this.state.val + 1});
  15. console.log(this.state.val); // 第 3 次 log
  16. this.setState({val: this.state.val + 1});
  17. console.log(this.state.val); // 第 4 次 log
  18. }, 0);
  19. }
  20. render() {
  21. return null;
  22. }
  23. };
  24. //输出
  25. 0 0 2 3
  26. //解答:
  27. 第一次和第二次都是在 react 自身声明周期内,触发时 isBatchingUpdates true,所以并不会直接执行更新 state,而是加入了 dirtyComponents,所以打印的时获取的都是更新前的状态 0
  28. 两次 setState,获取到 this.state.val 都是 0,所以执行时都是将 0 设置成 1,在 react 内部会被合并掉,只执行一次,设置完成后 state.val 值为 1.
  29. setTimeout 中的代码,触发时 isBatchingUpdate false,所以能够直接进行更新,所以连着输出 23