2019面試筆記

一.js原始類型:

在js中,存在着6種原始值:es6

* boolean 
* number 
* string 
* undefined 
* null 
* symbol

注意: 雖然typeof null輸出的是object,可是null並非object類型,由於早期的bug;面試

number類型是浮點數類型,因此纔會有js精度問題出現。0.1+0.2 !== 0.3。 原始類型存儲的都是值,是沒有函數能夠調用的。
  1. symbol類型: symbol類型的對象永遠不相等,即使建立他們的時候傳入了相同的值,能夠藉助此特性解決屬性名的衝突問題。
    for...in..拿不到symbol類型的屬性值,並且要用[]的方式來讀取symbol類型的屬性值,用點的方式讀不到。

二.對象(Object)類型:

在js中,除了原始類型其餘的就是對象類型了。
Q1:對象類型和原始類型的不一樣之處?ajax

答:對象類型和原始類型不一樣的是,原始類型存貯的是值,對象那個類型存儲的是地址(指針)。當建立了一個對象類型的時候,計算機回在內存
中開闢一個空間賴存放值,可是咱們須要找到這個空間,這個控件會擁有一個地址(指針)。當咱們將變量賦值給另一個變量時,複製的是本來變量的地址(指針)。

Q2:函數參數是對象會發生什麼?算法

答:函數參數是對象的話,在函數內部可能會改變這個參數對象,生成新對象。

三.typeof VS instanceof

Q1:typeof是否能正確判斷類型?編程

答:對於原始類型來講,typeof除了null均可以正確顯示類型,typeof null顯示的是object是錯誤的。對於其餘的,除了function會返回function,其餘都會返回object。

Q2. instanceof能正確判斷對象的原理是什麼?數組

答:instanceof內部機制是經過原型鏈來判斷對象類型的。 
  eg: const Person = function () {} 
      const p1 = new Person() p1
      instanceof Person // true 
      var str = 'hello world' str instanceof String // false

四.類型轉換:

1.轉Boolean: 在條件判斷時,除了undefined,null,0,-0,NaN,'',false,其餘全部值都轉化爲true,包括全部對象。
2.對象轉原始類型: 對象在轉換類型的時候,會調用內置的[[ToPrimitive]]函數,對於該函數來講,promise

算法邏輯通常來講以下:
  * 若是已是原始類型了,就不須要換磚了
  * 調用x.valueof(),若是轉換爲基礎類型,就返回轉換的值
  * 調用x.toString(),若是轉化爲基礎類型,就返回轉換的值 
  * 若是都沒有返回原始類型,就會報錯 1)四則運算符: 加法運算符特色: 
  *運算中若是一方爲字符串,那麼就會把另外一方葉轉換爲字符串,1 + '1' // '11' 
  *若是一方不是字符串或者數字,那麼會將它轉化爲數字或者字符串:true+ true // 2 ; 4 + [1,2,3] // '41,2,3' 
注意: + 'a'是快速將'a'轉化成number類型的寫法;'a' + + 'b' // 'aNaN' 那麼對於除了加法運算符其餘運算符來講,只要一方是數字,那麼另外一方就會被轉化爲數字。反正最終都是要轉化爲數字來運算。
      4 * '3' // 12 4 * [] // 0 4 * [1,2] // NaN 2)比較運算符 大於小於運算都會轉化爲數字進行運算。 
關於 ==: 
*undefined 等於 null 
*字符串和數字比較時,字符串轉數字 
*數字和布爾類型比較時,布爾轉數字
*字符串和布爾比較時,二者轉數字

五.this

this永遠指向最後調用它的那個對象。
1.改變this指向:瀏覽器

* 使用es6箭頭函數 
* 在函數內部使用 _this = this 
* 使用 apply,call,bind 
* new 實例化一個對象

2.箭頭函數: 箭頭函數的this始終指向函數定義時的this,而非執行時(意味着若是箭頭函數被非箭頭函數包含,this綁定的就是最近一層非箭頭函數的this)。閉包

箭頭函數中沒有this綁定,必須經過查找做用域鏈來決定其值。

3.apply,call,bind區別
apply和call只是傳入的參數不一樣。call接受的是若干個參數列表,而apply接受的是一個包含多個參數的數組。
bind和apply,call區別:併發

bind建立了一個新的函數,須要手動去調用它;

六.new的過程:

  • 建立一個空對象obj;
  • 將新建立的空對象的隱式原型指向其構造函數的顯示原型;
  • 使用call改變this的指向;
  • 若是無返回值或者返回一個非對象值,則將obj返回做爲新對象;若是返回值時一個新對象的華那麼直接返回該對象。

七. == VS ===

Q1: ==和 === 有什麼區別?

答: 對於 == 來講,若是雙方的類型不同的華,就會進行類型轉換。
    流程: 
      * 首先判斷二者類型是否相同 
      * 判斷是否爲 undefined == null //true 
      * string轉number比較 * boolean轉number比較
      * object轉原始類型比較 可是,=== 的比較只須要判斷二者類型和值是否相同。

八.閉包:

Q1: 什麼是閉包?

答:函數A內部有一個函數B,函數B能夠訪問到函數A中的變量,那麼函數B就是閉包。

Q2:經典面試題:循環中使用閉包解決var定義函數的問題:
答:第一種:使用閉包的方式: for (var i = 1; i <=5 ; i++) { ;(function(j) { setTimeout(function timer() { console.log(j) }, j * 1000) })(i)}

第二種:用let定義的方式: for (let i=1 ; i <=5 ; i++) { setTimeout(function timer() { console.log(i) }, i * 1000) } Q3:閉包的優缺點? 閉包做用(優勢):能夠讀取到函數內部的變量,可讓這些變量的值始終保持在內存中,不會被垃圾回收掉。

閉包缺點:由於閉包會使得函數中變量保存在內存中,因此會增大內存使用量,使用不當很容易形成內存泄漏。浪費內存。
清除閉包:退出函數以前,將不使用的局部變量刪除。

九.深淺拷貝:

1.堆和棧:棧(stack)爲自動分配的內存空間,它由系統自動釋放;而堆(heap)則是動態分配的內存,大小不定也不會自動釋放。
2.基本數據類型存放在棧中,數據大小肯定,內存空間大小能夠分配,是直接按值存放的,因此能夠直接訪問。基本類型的比較是值的比較。
3.引用類型存放在堆中,變量實際上存放的是棧1內存的指針,引用類型的值可變。引用類型的比較是引用的比較。因此比較兩個引用類型,是看其的引用是否指向同一個對象。
Q1.淺拷貝?

答:淺拷貝只複製一層對象的屬性,並不包括對象裏面的爲引用類型的數據。

Q2.深拷貝?

答: 深拷貝遞歸複製了對象的全部層級屬性。

Q3.深拷貝,淺拷貝以及賦值的區別?

答:淺拷貝,深拷貝和原數據都不是指向同一對象,而賦值對象和原對象是指向同一對象。
第一層數據爲基本類型,改變會使原數據改變,深拷貝以及淺拷貝不會。 原數據中包含子對象,改變子對象會使賦值對象以及淺拷貝對象改變,不會使深拷貝對象改變。

Q4.如何實現淺拷貝?

答:1.能夠經過Object.assign來解決。(Object.assign()方法用於將全部可枚舉的值從一個或多個源對象複製到目標對象。它將返回目標對象。)
    eg: 
      let a={ age: 1 } 
      let b=O bject.assign({}, a) 
      a.age=2 console.log(b.age) // 1 
    2.能夠經過展開運算符...來實現淺拷貝 
    eg: 
      let a={ age: 1} 
      let b={ ...a } 
      a.age=2 console.log(b.age) // 1

Q5.如何實現深拷貝?

答:1.該問題能夠經過JSON.parse(JSON.stringify(object))來解決。 
    eg: let a={age: 1, jobs: { first: 'FE' } } 
        let b=J SON.parse(JSON.stringify(a)) 
        a.jobs.first='native' 
        console.log(b.jobs.first) // FE
    可是該方法也是有侷限性的: 
      * 會忽略undefined 
      * 會忽略symbol 
      * 不能序列化函數 
      * 不能解決循環引用的對象

十:原型和原型鏈:

js是基於原型的繼承。
1.使用構造函數建立對象後,新對象與構造函數沒有關係了,新對象的[[prototype]]屬性指向的是構造函數的原型對象。
2.構造函數、原型和實例的關係:

* 構造函數都有一個屬性prototype,這個屬性是一個對象(Object的實例)
* 原型對象裏面有個constractor屬性,該屬性指向原型對象所屬的構造函數。當原型對象被修改過,constractor就不必定指向原來的構造函數了。
* 實例對象都有一個_proto_屬性,該屬性也指向構造函數的原型對象,它是一個非標準屬性,不能夠用於編程,它用於瀏覽器本身使用。

3.prototype和_proto_的關係:

* prototype是構造函數的屬性;
* _proto_是實例對象的屬性;
* 這二者都會指向同一個對象。
總結:*函數也是對象,對象不必定是函數;
      * 對象的本質:無序的鍵值對集合,鍵值對當中的值能夠是任意數據類型的值;
      *對象就是一個容器,這個容器當中放的是屬性和方法。

4.原型鏈:

原型鏈就是能夠理解爲有限的實例對象和原型之間組成的有限鏈,就是用來實現屬性共享和繼承的。

5.屬性搜索:

* 在訪問對象的某個成員的時候在對象中查找是否存在;
* 若是當前對象中沒有就在構造函數的原型對象中查找;
* 若是原型對象中沒有就在原型對象的原型上找;
* 直到找到Object的原型對象的原型是null爲止。
eg:
  var arr = []
  原型鏈:arr -> Array.prototype -> Object.prototype -> null

Q1:什麼是原型?

答: 原型也是一個對象,而且這個對象中包含了不少函數。原型的constract屬性指向構造函數,構造函數又經過prototype屬性指回原型,到那時並非全部函數都具備這個屬性。

十一:js繼承:

1.原型鏈繼承:將父類的實例做爲子類的原型。

eg:Cat.prototype = new Animal()
缺點:子類沒法給父類傳參,子類要新增屬性和方法的話,必需要在new Parent()這樣的語句以後執行,不然會被覆蓋。

2.構造繼承:使用父類的構造函數來加強子類的實例,等因而在子類的構造函數內部執行Parent.call(this)。

eg:function Cat(name) {
  Animal.call(this);
  this.name = name || 'Tom';
}
var cat = new Cat();
缺點:沒有原型,每次建立一個子類實例都要執行一遍Parent函數。

3.組合式繼承:結合原型鏈繼承和構造繼承,組合式繼承在使用過程當中會被調用兩次:一次是建立子類的時候,一次是在子類構造函數的內部。

eg: function Cat(name){
    Animal.call(this);
    this.name = name || 'Tom';
  }
  Cat.prototype = new Animal();

3.class繼承:

class繼承的核心在於使用extends代表繼承自哪一個父類,而且在子類構造函數中必須調用super,由於這段代碼能夠當作Parent.call(this,value).
eg:
class Parent {
  constructor(value) {
    this.val = value
  }
  getValue() {
    console.log(this.val)
  }
}
class Child extends Parent {
  constructor(value) {
    super(value)
    this.val = value
  }
}
let child = new Child(1)
child.getValue() // 1
child instanceof Parent // true

十二:var、let及const區別:

1.var和let區別:

* let有塊級做用域的說法,也就是let聲明的變量只能在這個塊內部使用,別的地方不能使用。var聲明的變量能夠在全局使用,var在函數內部聲明的變量只能在函數內部使用。
* let沒有變量提高,var有變量提高
* let聲明的變量存在「暫時性死區」,而var聲明的變量沒有
* let不容許在同一個快內重複聲明一個變量。所以也不能在函數內部重複聲明一個變量。
* var聲明的變量會掛載到window上,而let聲明的對象不會掛載到window上。
  1. const特性:

    • const聲明一個只讀的常量。一旦聲明,常量的值就不能改變;
    • const聲明的變量必須初始化,否則會報錯;
    • const聲明的變量也存在暫時性死區;
    • const聲明的變量只在所聲明的塊級做用域內有效;
    • const聲明的常量也與let同樣不可重複聲明。

Q1.什麼是提高?

答:就是變量能夠在神明以前使用,而且值爲undefined。

Q2. 什麼是暫時性死區?

答:在let命令聲明以前,該變量都素hi不可用的。

十三.模塊化:

Q1.爲何要使用模塊化?

答:使用模塊化能夠帶來如下好處:
  * 解決命名衝突;
  * 提供複用性;
  * 提升代碼可維護性。

Q2.實現模塊化的方式?

CommonJS  AMD  CMD

1.require/exports:

遵循CommonJS/AMD,只能在運行時肯定模塊的依賴關係及輸入/輸出的變量,沒法進行靜態優化。

2.import/export:

遵循ES6規範,支持編譯時靜態分析,便於JS引入宏和類型檢驗,動態綁定。

十4、JS異步編程:

1. 併發(concurrency)和並行(parallelism)區別:
    Q1:併發與並行的區別?
        答:併發是宏觀概念,我分別有任務A和任務B,在一段時間內經過任務間的切換完成了這個任務。
            並行是微觀概念,假設CPU中存在兩個核心,那麼我就能夠同時完成任務A和任務B。同時完成多個任務的狀況能夠稱之爲並行。
2.回調函數(callback):
    Q1.什麼是回調函數?
        答:ajax(url,() => {
            //處理邏輯;
        })

    Q2.回調函數有什麼缺點?
        答:1)回調函數有一個致命的弱點就是容易寫出回調地獄(callback hell)。
            eg:
                ajax(url, () => {
                    // 處理邏輯
                    ajax(url1, () => {
                        // 處理邏輯
                        ajax(url2, () => {
                            // 處理邏輯
                        })
                    })
                })
        回調地獄根本問題:
        * 嵌套函數存在耦合性,一旦有所改動,就會牽一髮而動全身;
        * 嵌套函數一多,就很難處理錯誤。
        2)回調函數不能使用trycatch捕獲錯誤,不能直接return。
    Q3.如何解決「地獄回調」?
        答:* function拆解
            * promise
            * generater
            * async/await
    3. Generator:
    4. promise:
        Q1:Promise的特色?
            答:對象的狀態不受外界影響,只有三種狀態:pending resolved  rejected
                狀態一旦改變,就不會再變,任什麼時候候均可以獲得這個結果。
        Q2. Promise的優缺點?
            答:缺點:* promise沒法取消,一旦新建就會當即執行,沒法中途取消;
                    * 若是不設置回調函數,promise內部會拋出錯誤,不會反映到外部;
                    * 當處於pending狀態時,沒法得知目前進展到哪個階段(剛剛開始仍是即將完成)。
                優勢:能夠解決回調地獄問題。
        Q3. 什麼是Promise鏈?
            答:就是在Promise構造函數裏面經過then來一步一步使用promise,由於then方法返回的也是一個全新的promise實例。
        Q4. Promise構造函數執行和then函數執行有什麼區別?
            答:then方法每次都返回一個promise實例。
    5. Async/Await:
        Q1.async的特色?
            答:一個函數若是加上async,那麼該函數就會返回一個promise。async就是將函數返回值使用Promise.resolve()包裹了一下,和then中處理返回值同樣,而且await只能配套在async函數內部使用。
        Q2.優缺點?
            答: 優勢:處理then的調用鏈可以更清晰的寫出代碼,而且可以優雅的解決回調地獄問題。
                缺點:由於await將一部代碼改形成了同步代碼,若是多個異步代碼沒有依賴性卻使用了await會致使性能上的下降。
        Q3.await原理是什麼?
            答:await內部實現了generator。
        實現async/await:
            async function doIt() {
                console.time("doIt");
                const time1 = 300;
                const time2 = await step1(time1);
                const time3 = await step2(time2);
                const result = await step3(time3);
                console.log(`result is ${result}`);
                console.timeEnd("doIt");
            }
            
            doIt();
    6.經常使用定時器函數: setTimeout() setInterval()  requestAnimationFrame()
        requestAnimationFrame自帶函數節流功能,基本能夠保證在16.6毫秒內只執行一次,而且該函數的延時效果是精準的。若是有循環定時器的需求,requestAnimationFrame是最好的選擇。
相關文章
相關標籤/搜索