每日一题

来源于https://github.com/Advanced-Frontend/Daily-Interview-Question/blob/master/datum/summary.md

一天最少一道题,把看过的题整理一下

 

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

  key是给每一个vnode的唯一id,可以依靠key,更准确, 更的拿到oldVnode中对应的vnode节点。

1) 更准确

  因为带key就不是就地复用了,在sameNode函数 a.key === b.key对比中可以避免就地复用的情况。所以会更加准确。

2) 更快

  利用key的唯一性生成map对象来获取对应节点,比遍历方式更快。(这个观点,就是我最初的那个观点。从这个角度看,map会比遍历更快。)

在交叉对比中,当新节点跟旧节点头尾交叉对比没有结果时,会根据新节点的key去对比旧节点数组中的key,从而找到相应旧节点(这里对应的是一个key => index 的map映射)。如果没找到就认为是一个新增节点。而如果没有key,那么就会采用遍历查找的方式去找到对应的旧节点。一种一个map映射,另一种是遍历查找。相比而言。map映射的速度更快

第 2 题:['1', '2', '3'].map(parseInt) what & why ?

1)map()方法

var new_array = arr.map(function callback(currentValue[, index[, array]]) {
 // Return element for new_array 
}[, thisArg])

  callback 接受三个参数: currentValue( callback 数组中正在处理的当前元素 ),index可选(callback 数组中正在处理的当前元素的索引),array可选(callback  map 方法被调用的数组

2)
parseInt(string, radix) 方法

  则是用来解析字符串的,使字符串成为指定基数的整数。

  接收两个参数,第一个表示被处理的值(字符串),第二个表示为解析时的基数(需要在2-36之间)

3) parseInt('1', 0) //radix为0时,且string参数不以“0x”和“0”开头时,按照10为基数处理。这个时候返回1

  parseInt('2', 1) //radix小于2,NAN

  parseInt('3', 2) //2进制的时候值应该是0 1,3是不合法的

第 3 题:什么是防抖和节流?有什么区别?如何实现?

 

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 
14     var inp = document.getElementById('inp');
15     inp.addEventListener('input', debounce(sayHi)); // 防抖

 

2)节流

  高频触发的事件,但在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));
 1 function throtte(func, time){
 2     let activeTime = 0;
 3     return () => {
 4       const current = Date.now();
 5       if(current - activeTime > time) {
 6         func.apply(this, arguments);
 7         activeTime = Date.now();
 8       }
 9     }
10   }

3)总结:

防抖和节流的目的都是防止一个事件频繁触发回调函数,区别:

节流函数 不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数。

防抖动 只是在最后一次事件后才触发一次函数。

 

第 4 题:介绍下 Set、Map、WeakSet 和 WeakMap 的区别?(ES6语法)

 

参考阮一峰的ES6语法http://es6.ruanyifeng.com/#docs/set-map

Set 和 Map 主要的应用场景在于 数据重组 和 数据储存

 

1) Set

一种新的数据结构,类似于数组,但是成员唯一且无序

// 去重数组的重复对象
let arr = [1, 2, 3, 2, 1, 1]
[... new Set(arr)]    // [1, 2, 3]

Set不会发生类型转换,所以5"5"是两个不同的值。它类似于精确相等运算符(===),主要的区别是**NaN等于自身,而精确相等运算符认为NaN不等于自身。

  • 实例属性
    • constructor: 构造函数
    • size:元素数量
    • let set = new Set([1, 2, 3, 2, 1])
      
      console.log(set.length)    // undefined
      console.log(set.size)    // 3
  • 实例方法
    • add(value):新增,相当于 array里的push

    • delete(value):存在即删除集合中value

    • has(value):判断集合中是否存在 value

    • clear():清空集合

    • keys():返回一个包含集合中所有键的迭代器
    • values():返回一个包含集合中所有值得迭代器
    • entries():返回一个包含Set对象中所有元素得键值对迭代器
    • forEach(callbackFn, thisArg):用于对集合成员执行callbackFn操作,如果提供了 thisArg 参数,回调中的this会是这个参数,没有返回值
 1 let set = new Set()
 2 set.add(1).add(2).add(1)
 3 
 4 set.has(1)    // true
 5 set.has(3)    // false
 6 set.delete(1)    
 7 set.has(1)    // false
 8  
 9 //Array.from 方法可以将 Set 结构转为数组
10 
11 const items = new Set([1, 2, 3, 2])
12 const array = Array.from(items)
13 console.log(array)    // [1, 2, 3]
14 //
15 const arr = [...items]
16 console.log(arr)    // [1, 2, 3]

2) 字典(Map)

集合和字典:

  • 共同点:集合、字典 可以储存不重复的值
  • 不同点:集合 是以 [value, value]的形式储存元素,字典 是以 [key, value] 的形式储存

3) WeakSet

4) WeakMap 

 

第 7 题:ES5/ES6 的继承除了写法以外还有什么区别?

1) class 声明会提升,但不会初始化赋值。Foo 进入暂时性死区,类似于 letconst 声明变量。

 1 const bar = new Bar(); // it's ok
 2 function Bar() {
 3   this.bar = 42;
 4 }
 5 
 6 const foo = new Foo(); // ReferenceError: Foo is not defined
 7 class Foo {
 8   constructor() {
 9     this.foo = 42;
10   }
11 }

2) class 声明内部会启用严格模式。

// 引用一个未声明的变量
function Bar() {
  baz = 42; // it's ok
}
const bar = new Bar();

class Foo {
  constructor() {
    fol = 42; // ReferenceError: fol is not defined
  }
}
const foo = new Foo();

3) class的所有方法(静态方法和实例方法)都是不可枚举的。

 1 // 引用一个未声明的变量
 2 function Bar() {
 3   this.bar = 42;
 4 }
 5 Bar.answer = function() {
 6   return 42;
 7 };
 8 Bar.prototype.print = function() {
 9   console.log(this.bar);
10 };
11 const barKeys = Object.keys(Bar); // ['answer']
12 const barProtoKeys = Object.keys(Bar.prototype); // ['print']
13 
14 class Foo {
15   constructor() {
16     this.foo = 42;
17   }
18   static answer() {
19     return 42;
20   }
21   print() {
22     console.log(this.foo);
23   }
24 }
25 const fooKeys = Object.keys(Foo); // []
26 const fooProtoKeys = Object.keys(Foo.prototype); // []

4) class 的所有方法(包括静态方法和实例方法)都没有原型对象 prototype,所以也没有[[construct]],不能使用 new 来调用

 1 function Bar() {
 2   this.bar = 42;
 3 }
 4 Bar.prototype.print = function() {
 5   console.log(this.bar);
 6 };
 7 
 8 const bar = new Bar();
 9 const barPrint = new bar.print(); // it's ok
10 
11 class Foo {
12   constructor() {
13     this.foo = 42;
14   }
15   print() {
16     console.log(this.foo);
17   }
18 }
19 const foo = new Foo();
20 const fooPrint = new foo.print(); // TypeError: foo.print is not a constructor

5) 必须使用 new 调用 class

 1 function Bar() {
 2   this.bar = 42;
 3 }
 4 const bar = Bar(); // it's ok
 5 
 6 class Foo {
 7   constructor() {
 8     this.foo = 42;
 9   }
10 }
11 const foo = Foo(); // TypeError: Class constructor Foo cannot be invoked without 'new'

6) class 内部无法重写类名

 1 function Bar() {
 2   Bar = 'Baz'; // it's ok
 3   this.bar = 42;
 4 }
 5 const bar = new Bar();
 6 // Bar: 'Baz'
 7 // bar: Bar {bar: 42}  
 8 
 9 class Foo {
10   constructor() {
11     this.foo = 42;
12     Foo = 'Fol'; // TypeError: Assignment to constant variable
13   }
14 }
15 const foo = new Foo();
16 Foo = 'Fol'; // it's ok

 

第 8 题:setTimeout、Promise、Async/Await 的区别

1. setTimeout
1 console.log('script start')    //1. 打印 script start
2 setTimeout(function(){
3     console.log('settimeout')    // 4. 打印 settimeout
4 })    // 2. 调用 setTimeout 函数,并定义其完成后执行的回调函数
5 console.log('script end')    //3. 打印 script start

6 // 输出顺序:script start->script end->settimeout

setTimeout最后执行,判断到没有定时的时间点,到了就执行

 

2. Promise

Promise本身是同步的立即执行函数(如果一个任务已经完成,再添加回调函数,该回调函数会立即执行), 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行,打印p的时候,是打印的返回结果,一个Promise实例。

 1 console.log('script start')
 2 let promise1 = new Promise(function (resolve) {
 3     console.log('promise1')
 4     resolve()
 5     console.log('promise1 end')
 6 }).then(function () {
 7     console.log('promise2')
 8 })
 9 setTimeout(function(){
10     console.log('settimeout')
11 })
12 console.log('script end')
 输出顺序: script start->promise1->promise1 end->script end->promise2->settimeout

 

3. async/await
 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 
10 console.log('script start');
11 async1();
12 console.log('script end')
输出顺序:script start->async1 start->async2->script end->async1 end

async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。 

await的含义为等待,async 函数需要等待await后的函数执行完成并且有了返回结果(Promise对象)之后,才能继续执行下面的代码。await通过返回一个Promise对象来实现同步的效果。 

 

 

第 9 题:异步笔试题

请写出下面代码的运行结果

 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');

 

/*
script start
async1 start
async2
promise1
script end async1 end promise2 setTimeout */

第 10 题:算法手写题

已知如下数组:var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];

将数组扁平化并去除其中重复数据,最终得到一个升序且不重复的数组

 1 var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10]
 2 // 扁平化
 3 let flatArr = arr.flat(4)
 4 // 去重
 5 let disArr = Array.from(new Set(flatArr))
 6 // 排序
 7 let result = disArr.sort(function(a, b) {
 8     return a-b
 9 })
10 console.log(result)
11 // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1

补充:arr.flat(depth) 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

 1 var arr1 = [1, 2, [3, 4]];
 2 arr1.flat(); 
 3 // [1, 2, 3, 4]
 4 
 5 var arr2 = [1, 2, [3, 4, [5, 6]]];
 6 arr2.flat();
 7 // [1, 2, 3, 4, [5, 6]]
 8 
 9 var arr3 = [1, 2, [3, 4, [5, 6]]];
10 arr3.flat(2);
11 // [1, 2, 3, 4, 5, 6]

 

第 11 题:JS 异步解决方案的发展历程以及优缺点。

http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html 阮一峰异步编程

第 12 题:Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?

 1 const promise = new Promise((resolve, reject) => {
 2   console.log(1)
 3   resolve()
 4   console.log(2)
 5 })
 6 
 7 promise.then(() => {
 8   console.log(3)
 9 })
10 
11 console.log(4)

执行结果是:1243
promise构造函数是同步执行的,then方法是异步执行的

Promise new的时候会立即执行里面的代码 then是微任务 会在本次任务执行完的时候执行 setTimeout是宏任务 会在下次任务执行的时候执行

第 13 题:new一个对象

 

 

 

 

 

 

 

 

 


 

 

 

转载于:https://www.cnblogs.com/shuran/p/10669941.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值