每日一題


來源於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一個對象

 

 

 

 

 

 

 

 

 


 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM