1

let i;
for (i = 0; i < 3; i++) {
  let log = () => {
    console.log(i);
  };
  setTimeout(log, 0);
}

function demo() {
  let x = (b = 0);
  return x;
}

demo();
console.log(typeof x);
console.log(typeof b);

解析

  1. demo(),可以看到用 let 定义了 x,之后将 x return 出去;b 未定义,直接赋值的,那么就默认在 window 下。
  2. typeof x, 并未定义 x,所以是 undefined
  3. typeof b, b 是 0,所以是 number.
  4. 同步都执行完了,那就开始执行异步。由于 i 是没有在 for 循环块级作用域里面定义,拿到的始终跟最后一次的值。

2.

let sender = { a: 1, c: undefined };

function demo1() {
  with (sender) {
    a = 2;
    b = 3;
    c = 4;
  }
}

demo1();
console.log(sender);

function demo2(str) {
  eval(str);
  console.log(x);
}
var x = 1;
demo2('var x = 3');
console.log(x);

解析

  1. with 作用域,只会赋值当前已有属性的值
  2. 执行 demo2,eval 执行,此时 x 是 3。

3.

var a = {};
var b = { key: 'b' };
var c = { key: 'c' };
var d = [3, 5, 6];
a[b] = 123;
a[c] = 345;
a[d] = 333;

console.log(a[b]);
console.log(a[c]);
console.log(a[d]);

4.

new Promise(res => {
  setTimeout(() => {
    console.log(0);
  }, 0);
  res();
}).then(() => {
  setTimeout(() => {
    console.log(1);
  }, 0);
});

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

new Promise(res => setTimeout(res, 0)).then(() => {
  console.log(3);
  setTimeout(() => {
    console.log(4);
  }, 0);
  new Promise(r => r()).then(() => console.log(5));
});

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

new Promise(res => res()).then(() => {
  console.log(7);
});

for (var i = 8; i < 11; i++) {
  setTimeout(() => {
    console.log(i);
  }, 0);
  console.log(i);
}

解析

慢慢思考,沉下心做。

  1. 先同步再异步, 先微任务再宏任务。队列要分清除。

new Promise(xxx) 是同步,then() 是微任务,setTimeout 是宏任务。

5.

function Foo() {
  getName = function() {
    console.log(1);
  };
  return this;
}
getName();

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

function getName() {
  console.log(5);
}

biz();
function biz() {
  console.log(8);
}

if ([] == false) {
  function biz() {
    console.log(6);
  }
} else {
  function biz() {
    console.log(7);
  }
}

biz();
Foo.getName();
getName();
Foo().getName();
getName();
Foo.getName();
new Foo().getName();

解析

先定义后执行!

6. 三数之和,请挑选出数组中三个随机整数和位 100 的所有数据

function threeSum(arr) {
  const len = arr.length;
  let result = [];
  for (let i = 0; i < len; i++) {
    for (let j = i + 1; j < len; j++) {
      for (let k = j + 1; k < len; k++) {
        if (arr[i] + arr[j] + arr[k] === 100) {
          result.push([arr[i], arr[j], arr[k]]);
        }
      }
    }
  }
  return result;
}

threeSum([20, 30, 60, -10, 50, 15, 25]);

// [ [ 20, 30, 50 ], [ 60, -10, 50 ], [ 60, 15, 25 ] ]

7. 请用数组方法和 Math.random() 在一条语句的情况下实现生成给定位数的随机数组。

例如生成 10 位随机数组[96, 64, 70, 30, 43, 38, 39, 77, 80, 35], 数组内的数字随机生成。

new Array(10).fill(0).map(item => Math.random() * 100);
Array.from(new Array(10)).map(item => Math.random() * 100);
Array.from({ length: 10 }, () => Math.random() * 10);

8. 实现一个 bind 方法

Function.prototype.bind = function() {
  let thatFunc = this,
    thatArgs = arguments[0];
  let slice = Array.prototype.slice;
  let args = slice.call(arguments, 1);
  return function() {
    let _args = args.concat(slice.call(arguments));
    return thatFunc.apply(thatArgs, _args);
  };
};

9. 实现一个 setter 方法

例如

let setter = function(content, key, value) {
  // todo
};
let n = {
  a: {
    b: {
      c: {
        d: 1,
        bx: {
          y: 1
        }
      }
    }
  }
};
setter(n, 'a.b.c.d', 3);
console.log(n.a.b.c.d); // 3

setter(n, 'a.b.c.bx', 1);
console.log(n.a.b.c.bx); // 1
let setter = function(content, key, value) {
  let path = key.split('.');
  for (let i = 0; i < path.length; i++) {
    const k = path[i];
    if (i === path.length - 1) {
      content[k] = value;
      break;
    }
    content = content[k];
  }
};

10. 对象解构赋值

var obj1 = { name: 'xiaoming', age: 8, weight: '60kg' };
var obj2 = { account: 'admin', password: 'admin' };
var obj3 = { remember: true };

合并 obj1, obj2, obj3, 使用新变量 obj4 保存

let obj4 = { ...obj1, ...obj2, ...obj3 };

// 或者
let obj4 = Object.assign({}, obj1, obj2, obj3);

获取除了 name 和 account 的属性

function getOtherProps(object, keys) {
  return Object.keys(object).reduce((obj, key) => {
    if (keys.includes(key)) return obj;
    obj[key] = object[key];
    return obj;
  }, {});
}

getOtherProps(obj4, ['name', 'key']);

11. 实现一个方法可以对数组对象内的 name 去重

var list = [
  { id: 1, name: 'xiaomin' },
  { id: 2, name: 'liudehua' },
  { id: 3, name: 'guofucheng' },
  { id: 4, name: 'zhangxueyou' },
  { id: 5, name: 'liming' },
  { id: 6, name: 'xiaomin' }
];
function checkDuplicate(list) {
  let result = [],
    keys = [];
  for (const item of list) {
    if (!keys.includes(item.name)) {
      keys.push(item.name);
      result.push(item);
    }
  }
  return result;
}

function checkDuplicate(list) {
  let _obj = {};
  return list.reduce((obj, item, index, arr) => {
    if (!_obj[item.name]) {
      obj.push(item);
      _obj[item.name] = true;
    }
    return obj;
  }, []);
}

checkDuplicate(list);

12. 实现以下需求

var arr = [
  { id: 1, pid: 0, title: '首页' },
  { id: 2, pid: 1, title: '订单列表' },
  { id: 3, pid: 1, title: '刊登管理' },
  { id: 4, pid: 3, title: '刊登列表' },
  { id: 5, pid: 3, title: '刊登设置' },
  { id: 6, pid: 0, title: '登录' }
];

// 测试
function deep(list, id) {
  if (!Array.isArray(list)) return list;
  for (const item of list) {
    if (item.id === id) return item;
    if (Array.isArray(item.children)) {
      return deep(item.children, id);
    }
  }
  return null;
}

var newArr = arr.slice();
newArr.sort((a, b) => a.pid - b.pid);

let obj = newArr.reduce((arr, item) => {
  if (item.pid === 0) {
    arr.push(item);
    return arr;
  }
  const pNdoe = deep(arr, item.pid);
  if (!pNdoe.children) pNdoe.children = [];
  pNdoe.children.push(item);
  return arr;
}, []);

console.log(obj);

13. 实现 calculate

calculate(2)(3)('+'); // 5

calculate(2)(3)('*'); // 6

function calculate() {
  const slice = Array.prototype.slice;
  let args = slice.call(arguments);
  const temp = function() {
    let argsAll = args.concat(slice.call(arguments));
    if (argsAll.length < 3) {
      return calculate.apply(null, argsAll);
    }
    const idx = argsAll.length - 1;
    const sign = argsAll.slice(idx);
    const arr = argsAll.splice(0, idx);
    var result = (function getFn(sign) {
      return new Function(`return [${arr}].reduce((a, b) => a ${sign} b)`)();
    })(sign);
    return result;
  };
  return temp;
}

14. 不实用 for 或 while, 仅使用数组内置的方法,创建一个长度为 110 的数组,并且每个元素的值等于数组长度加上下标的值

new Array(110).fill(0).map((item, idx, arr) => arr.length + idx);

15. 判断下列的值

function test(a, b) {
  b = a + 10;
  console.log(b);
  return {
    test: function(a, b) {
      return test(b, a);
    }
  };
}

var a = test(100, 200);
a.test(300);
a.test(400);

var b = test(101)
  .test(201)
  .test(401);

var c = test(102).test(202, 302);
c.test();

耐心做,完全没难度。

16. 二分法

// 普通while循环
function binarySearch(arr, key) {
  let low = 0,
    high = arr.length - 1;
  while (low <= high) {
    let mid = parseInt((low + high) / 2);
    if (arr[mid] < key) {
      low = mid + 1;
    } else if (arr[mid] > key) {
      high = mid - 1;
    } else {
      return mid;
    }
  }
}

// 递归
function binarySearch(arr, key, low = 0, high = arr.length - 1) {
  const mid = parseInt((low + high) / 2);
  if (arr[mid] < key) {
    low = mid + 1;
    return binarySearch(arr, key, low, high);
  } else if (arr[mid] > key) {
    high = mid - 1;
    return binarySearch(arr, key, low, high);
  } else {
    return mid;
  }
}

binarySearch([1, 2, 3, 4, 5], 3); // 2

17. 合并两个有效数组

e.g1:

输入: nums1 = [1, 2, 3, 0, 0, 0], m = 3, nums2 = [2, 5, 6], n = 3 输出: [1, 2, 2, 3, 5, 6]

e.g2:

输入: nums1 = [1], m = 1, nums2 = [], n = 0 输出: [1]

/**
 * 双指针
 * 从两个数组的尾巴开始算起,将大的塞进去,因为是升序数组
 *
 */
function merge(nums1, m, nums2, n) {
  let p1 = m - 1,
    p2 = n - 1,
    len = m + n - 1;
  while (p2 >= 0) {
    nums1[len--] = nums1[p1] > nums2[p2] ? nums1[p1--] : nums2[p2--];
  }
  return nums1;
}

18. 实现 padEnd

/**
 * 如果string字符串长度小于 length 则在右侧填充字符。 如果超出length长度则截断超出的部分。
 * @example
  padEnd('abc', 6, '_-');  => 'abc_-_'
  padEnd('abc', 6, '0');  => 'abc000'
  padEnd('abcd', 3);        => 'abc'
 */
function padEnd(string, length, chars) {
  const sLen = string.length;
  if (!chars) {
    if (sLen > length) {
      return string.substring(0, length);
    }
  }
  const diff = length - sLen;
  for (let i = 0; i < diff; i++) {
    string += chars;
    if (string.length > length) {
      return string.substring(0, length);
    }
  }
  return string;
}

19. 实现 isEqual

/**
 * 执行深比较来确定两者的值是否相等
 * var obj1 = { 'a': 1 };
 * var obj2 = { 'a': 1 };
 * isEqual(obj1, obj2);
 */
function isEqual(obj1, obj2) {
  const obj1Type = toString.call(obj1);
  const obj2Type = toString.call(obj2);
  if (obj1Type !== obj2Type) return false;

  const key1Len = Object.keys(obj1).length;
  const key2Len = Object.keys(obj2).length;
  if (key1Len !== key2Len) return false;

  let flag = false;
  Object.keys(obj1).forEach(k => {
    flag = obj1[k] === obj2[k];
  });
  return flag;
}

20. 实现两个大数相加

/**
 * JS 在存放整数的时候是有一个安全范围的,一旦数字超过这个范围便会损失精度。
 * 实现两个大数相加
 * let a = "9007199254740991";
 * let b = "1234567899999999999";
 */
let a = '9007199254740991';
let b = '1234567899999999999';

function add(a, b) {
  const maxLen = Math.max(a.length, b.length);
  // 补0, 方便做相加
  if (a.length > b.length) {
    b = b.padStart(maxLen, 0);
  } else {
    a = a.padStart(maxLen, 0);
  }
  let carry = 0; // 进位
  let t = 0;
  let sum = '';
  for (let i = maxLen - 1; i >= 0; i--) {
    t = parseInt(a[i]) + parseInt(b[i]) + carry;
    carry = Math.floor(t / 10);
    sum = (t % 10) + sum;
  }
  if (carry === 1) {
    sum = `1${sum}`;
  }
  return sum;
}

21. 请写出快速排序

/**
 * 请写出快速排序
 * @example
 * // test
 * var array = [4, 7, 87, 34, 56, 69, 19, 26, 7, 9, 33];
 * var result = quicksort(array);
 */
function quicksort(array) {
  if (array.length <= 1) return array;
  const midIndex = Math.floor(array.length / 2);
  const mid = array.splice(midIndex, 1)[0];
  let left = [],
    right = [];
  for (let i = 0; i < array.length; i++) {
    if (array[i] < mid) {
      left.push(array[i]);
    } else {
      right.push(array[i]);
    }
  }
  return quicksort(left).concat([mid], quicksort(right));
}

22. 实现 Observer 函数

/**
 * 写一个 Observer 函数,实现 如下功能:
 * var obj = observe({a:1})
 * obj.a = 2  // 打印 a 被修改为 2
 */

function observe(obj) {
  return new Proxy(obj, {
    get: (target, key) => target[key],
    set: (target, key, value) => {
      console.log('修改后的值为:' + value);
      target[key] = value;
    }
  });
}
Last Updated:
Contributors: kk