1. ==判断规则

[ ] == ![ ] 的运算过程以及隐形转换的规则是什么

2. Object的属性key的类型

  1. var a = {}
  2. var b = {}
  3. a[b] = 4
  4. // {[object Object]: 4}

3. valueOf与toString的执行

  1. var obj = {
  2. toString: () => { console.log('string'); return { } },
  3. valueOf: () => { console.log('valueOf'); return { } },
  4. }
  5. // obj*3 结果是什么

因为会先调用 valueof获取值,但因返回不是基本类型,无法计算,会再去调用 toString 获取值,同样也无法计算,所以最后报错

  1. // valueOf
  2. // string
  3. // Uncaught TypeError: Cannot convert object to primitive value

3. 写一个函数sum满足如下情况

  1. sum(1)(2) === 3
  2. sum(1)(2)(3) == 6
  3. sum(1)(2)(3)(4) == 10
  4. function sum() {}

4. 箭头函数的this指向问题

  1. var a = 10
  2. var obj = {
  3. a: 20,
  4. say: () => {
  5. console.log(this.a)
  6. }
  7. }
  8. var anotherObj = { a: 30 }
  9. obj.say() // 10
  10. obj.say.apply(anotherObj)() // 10
  11. obj.say.bind(anotherObj)(); // 10
  12. obj.say.bind().bind(anotherObj); // 10

箭头函数的this是指向父作用域的 this ,是在声明的时候就已确定,而不是运行的时候。同时箭头函数的 this 不能被 applybind 改变。 bind 方法只有调用一边时才有用,多次调用无效

5. 从输入url到页面完成加载发生了什么?

6. 写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?

vue和react都是采用diff算法来对比新旧虚拟节点,从而更新节点。在vue的diff函数中(建议先了解一下diff算法过程)。
在交叉对比中,当新节点跟旧节点头尾交叉对比没有结果时,会根据新节点的key去对比旧节点数组中的key,从而找到相应旧节点(这里对应的是一个key => index 的map映射)。如果没找到就认为是一个新增节点。而如果没有key,那么就会采用遍历查找的方式去找到对应的旧节点。一种一个map映射,另一种是遍历查找。相比而言。map映射的速度更快。

7. 什么是防抖和节流?有什么区别?如何实现?

  1. 防抖

    触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间

  • 思路:

    每次触发事件时都取消之前的延时调用方法

  1. function debounce(fn) {
  2. let timeout = null; // 创建一个标记用来存放定时器的返回值
  3. return function () {
  4. clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
  5. timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
  6. fn.apply(this, arguments);
  7. }, 500);
  8. };
  9. }
  10. function sayHi() {
  11. console.log('防抖成功');
  12. }
  13. var inp = document.getElementById('inp');
  14. inp.addEventListener('input', debounce(sayHi)); // 防抖
  1. 节流

    高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率

  • 思路:

    每次触发事件时都判断当前是否有等待执行的延时函数

  1. function throttle(fn) {
  2. let canRun = true; // 通过闭包保存一个标记
  3. return function () {
  4. if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
  5. canRun = false; // 立即设置为false
  6. setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
  7. fn.apply(this, arguments);
  8. // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
  9. canRun = true;
  10. }, 500);
  11. };
  12. }
  13. function sayHi(e) {
  14. console.log(e.target.innerWidth, e.target.innerHeight);
  15. }
  16. window.addEventListener('resize', throttle(sayHi));

8. 如何判断this的指向

9. JSON.parse(JSON.stringify())的缺点

1、如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2、如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象
3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor
6、如果对象中存在循环引用的情况也无法正确实现深拷贝

10. 对象与数组的遍历

  1. var a = { a: 1 };
  2. var b = { b: 2 };
  3. b.__proto__ = a;
  4. Object.defineProperty(b, "c", {
  5. value: 3
  6. });
  7. b[Symbol()] = 4;
  8. // ["b"] 返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性).
  9. Object.keys(b);
  10. // b : 2 a : 1 循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)
  11. for (var i in b) {
  12. console.log(i, ":", b[i]);
  13. }
  14. // ["b", "c"] 返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性).
  15. Object.getOwnPropertyNames(obj);
  16. // ["b", "c", Symbol()] 返回一个数组,包含对象自身的所有属性,不管属性名是Symbol或字符串,也不管是否可枚举.
  17. Reflect.ownKeys(b);
  18. // 数组
  19. var arr=[1,2,3,4];
  20. // 使用forEach
  21. arr.forEach(function(val, index) {
  22. console.log(val, index);
  23. });
  24. // 使用for..in..
  25. // 还会找原型链上的可枚举属性
  26. for (var i in arr){
  27. console.log(i,":",arr[i]);
  28. }
  29. // 使用for-of遍历
  30. // 不仅支持数组,还支持大多数类数组对象,例如DOM NodeList对象.
  31. // 也支持字符串遍历,它将字符串视为一系列的Unicode字符来进行遍历.
  32. for (var value of arr){
  33. console.log(value);
  34. }