在js中,存在着6種原始值:es6
* boolean * number * string * undefined * null * symbol
注意: 雖然typeof null輸出的是object,可是null並非object類型,由於早期的bug;面試
number類型是浮點數類型,因此纔會有js精度問題出現。0.1+0.2 !== 0.3。 原始類型存儲的都是值,是沒有函數能夠調用的。
在js中,除了原始類型其餘的就是對象類型了。
Q1:對象類型和原始類型的不一樣之處?ajax
答:對象類型和原始類型不一樣的是,原始類型存貯的是值,對象那個類型存儲的是地址(指針)。當建立了一個對象類型的時候,計算機回在內存 中開闢一個空間賴存放值,可是咱們須要找到這個空間,這個控件會擁有一個地址(指針)。當咱們將變量賦值給另一個變量時,複製的是本來變量的地址(指針)。
Q2:函數參數是對象會發生什麼?算法
答:函數參數是對象的話,在函數內部可能會改變這個參數對象,生成新對象。
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永遠指向最後調用它的那個對象。
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建立了一個新的函數,須要手動去調用它;
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屬性指回原型,到那時並非全部函數都具備這個屬性。
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
1.var和let區別:
* let有塊級做用域的說法,也就是let聲明的變量只能在這個塊內部使用,別的地方不能使用。var聲明的變量能夠在全局使用,var在函數內部聲明的變量只能在函數內部使用。 * let沒有變量提高,var有變量提高 * let聲明的變量存在「暫時性死區」,而var聲明的變量沒有 * let不容許在同一個快內重複聲明一個變量。所以也不能在函數內部重複聲明一個變量。 * var聲明的變量會掛載到window上,而let聲明的對象不會掛載到window上。
const特性:
Q1.什麼是提高?
答:就是變量能夠在神明以前使用,而且值爲undefined。
Q2. 什麼是暫時性死區?
答:在let命令聲明以前,該變量都素hi不可用的。
Q1.爲何要使用模塊化?
答:使用模塊化能夠帶來如下好處: * 解決命名衝突; * 提供複用性; * 提升代碼可維護性。
Q2.實現模塊化的方式?
CommonJS AMD CMD
1.require/exports:
遵循CommonJS/AMD,只能在運行時肯定模塊的依賴關係及輸入/輸出的變量,沒法進行靜態優化。
2.import/export:
遵循ES6規範,支持編譯時靜態分析,便於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是最好的選擇。