前端面試–JS
Q:JS資料類型
JS簡單資料類型:String、Number、Boolean、Null、undefined、Symbol。複雜資料類型Object、Array、Function。
Q:null和undefined的差別
(1)null是一個表示”無”的對象,轉我數值是為0,undefined是一個表示”無”的原始值,轉為數值時為NaN。當聲明的變量還未被初始化時,能量的預設值為undefined
(2)Null用來表示尚未存在的對象,常用來表示函數企圖傳回一個不存在的對象
(3)Undefined表示”缺少值”,就是此處應該有一個值,但是還沒有定義。典型用法是:
a、變量被聲明了,但沒有指派時,就等于undefined
b、調用函數時,應該提供的參數沒有提供,該參數等于undefined
c、對象沒有指派屬性,該屬性的值為undefined
d、函數沒有傳回值時,預設傳回undefined
(4)null表示”沒有對象”,即該處不應該有值。典型用法是:
a、作為函數的參數,表示該函數的參數不是對象
b、作為對象原型鍊的終點
Q:拖拽涉及的事件
Q:垃圾回收
什麼是垃圾回收
JS垃圾回收機制的目的是為了防止記憶體洩漏,記憶體洩漏是指有一些已經不被需要的變量但仍然存在在記憶體中,這樣便會造成記憶體洩漏。垃圾回收機制就是為了回收這些不被需要的變量,并且釋放掉他們所指向的記憶體。
JS垃圾回收的方法
①标記清除
這是最常見的回收方法,也是大部分浏覽器使用的回收方法。
當變量進入執行環境是,就會被标記上“進入環境”,從邏輯上講,被标記上“進入環境”的變量所指向的記憶體是永遠不會被回收的。當某個變量離開執行環境時,就會被标上“離開環境”。被标記為“離開環境”的變量則是可以回收的。
②引用計數
這是一種不太常見的回收方式。引用計數法就是同級引用類型聲明後被引用的次數,當次數為0時,該變量就會被回收。
有可能造成記憶體洩漏的案例
①全局變量造成的記憶體洩漏
②未銷毀的定時器和回調函數造成的記憶體洩漏
③DOM引用造成的記憶體洩漏
https://www.cnblogs.com/pingzi-wq/p/11531185.html
Q:原型鍊
因為每個對象和原型都有原型,對象的原型指向原型對象,而父的原型又指向父的父,這種原型層層連接配接起來的就構成了原型鍊。
Q:JS定時器
setInterval() :按照指定的周期(以毫秒計)來調用函數或計算表達式。方法會不停地調用函數,直到 clearInterval() 被調用或視窗被關閉。
setTimeout() :在指定的毫秒數後調用函數或計算表達式。
Q:數組和對象内置方法
數組内置方法
constructor 所建立對象的函數參考
prototype 能夠為對象加入的屬性和方法
Array.concat( ) 連接配接數組
Array.join( ) 将數組元素連接配接起來以建構一個字元串
Array.length 數組的大小
Array.pop( ) 删除并傳回數組的最後一個元素
Array.push( ) 給數組添加元素
Array.reverse( ) 颠倒數組中元素的順序
Array.shift( ) 将元素移出數組
Array.slice( ) 傳回數組的一部分
Array.sort( ) 對數組元素進行排序
Array.splice( ) 插入、删除或替換數組的元素
Array.toLocaleString( ) 把數組轉換成局部字元串
Array.toString( ) 将數組轉換成一個字元串
Array.unshift( ) 在數組頭部插入一個元素
對象内置方法
屬性
constructor
prototype
執行個體方法
1、toString()傳回目前對象的字元串形式,傳回值為String類型。
2、toLocaleString()傳回目前對象的"本地化"字元串形式,以便于目前環境的使用者辨識和使用,傳回值為String類型。
3、valueOf()傳回指定對象的原始值。
靜态方法
1.Object.assign(target, …sources)
功能:把一個或多個源對象的可枚舉、自有屬性值複制到目标對象中,傳回值為目标對象。
2、Object.create(proto [,propertiesObject])
功能:建立一個對象,其原型為prototype,同時可添加多個屬性。
3、Object.defineProperty(obj, prop, descriptor)
功能:在一個對象上定義一個新屬性或修改一個現有屬性,并傳回該對象。
4、Object.defineProperties(obj, props)
功能:在一個對象上定義一個或多個新屬性或修改現有屬性,并傳回該對象。
8、Object.getOwnPropertyNames(obj)
功能:擷取目标對象上的全部自有屬性名(包括不可枚舉屬性)組成的數組。
9.Object.getPrototypeOf(obj)
功能:擷取指定對象的原型,即目标對象的prototype屬性的值。
11、Object.keys(obj)
功能:擷取目标對象上所有可枚舉屬性組成的數組。
Q:for in和for of的差別
a. for in 便曆出來的是屬性
b. for of 周遊的是value
c. 手動給對象添加屬性後, for in 是可以将新添加的屬性周遊出來 但是for of 不行
d. for in 的屬性是使用[] 不可以使用 “.” eg: data[‘index’] instead of data.index
Q:繼承的方式
1.原型鍊繼承
2.借用構造函數(經典繼承) 複制父類構造函數内的屬性
call繼承是在子類中把父類當做普通的函數去執行,讓父類的this指向子類的執行個體,相當于給子類設定了很多私有的屬性和方法。
3.組合繼承
組合原型鍊繼承和借用構造函數繼承
背後的思路是:使用原型鍊實作對原型方法的繼承,而通過借用構造函數來實作對執行個體屬性的繼承。
4.寄生式繼承
建立一個僅用于封裝繼承過程的函數,該函數在内部以某種形式來做增強對象,最後傳回對象。
可以了解為在原型式繼承的基礎上新增一些函數或屬性。
5.寄生組合式繼承
子類構造函數複制父類的自身屬性和方法,子類原型隻接收父類的原型屬性和方法。
https://www.jianshu.com/p/85899e287694
Q:資料類型檢測
第一種:typeof可以檢測一些基本的資料類型
第二種:valueof可以看到資料最本質内容(原始值)
第三種:instanceof檢測目前執行個體是否隸屬于某各類
雙目運算符 a instanceof b 判斷a的構造器是否為b,傳回值是布爾值
第四種:isArray判斷是否為數組,isNaN()判斷是否為NaN
第五種:Object.prototype.toString
文法 Object.prototype.toString.call([value])
擷取Object.prototype上的toString方法,讓方法的this變為需要檢測的資料類型值,并在Number、String、Array、Function…這些類的原型上都有一個toString方法:這個方法就是把本身的值轉換為字元串
Q:怎麼改變this指向
call、apply、bind
共同點:call和apply第一個參數都為改變this的指針,若為null/undefined,預設指向window
不同點:①call、apply可以自動執行,bind需手動調用
②call、bind都有無數個參數,apply隻有兩個參數,而且第二個參數為數組
Q:判斷this指向
按照this指針的優先級,列出下面常會遇到的四種情況,從上到下依次是優先級從高到低(後面會詳細比較優先級)。
1.函數是和new一起被調用的嗎(new綁定)?如果是,this就是新建構的對象。
var bar = new foo()
2.函數是用call或apply被調用(明确綁定),甚至是隐藏在bind 硬綁定 之中嗎?如果是,this就是明确指定的對象。
var bar = foo.call( obj2 )
3.函數是用環境對象(也稱為擁有者或容器對象)被調用的嗎(隐含綁定)?如果是,this就是那個環境對象。
var bar = obj1.foo()
4.否則,使用預設的this(預設綁定)。如果在strict mode下,就是undefined,否則是global對象。 var bar = foo()
Q:設計模式
(不全)
js設計模式有:單例,工廠,觀察者等。
單例模式:保證一個類隻有一個執行個體。實作方法:判斷實際是否存在,不存在,建立實際,存在直接傳回。
觀察者模式:當改變一個對象會同時改變其他對象時,應該使用觀察者模式。觀察者模式主要作用是解耦。
工廠模式:通過對産品類的抽象使其建立業務主要負責用于建立多類産品執行個體。
Q:new實作
new的實作原理
• 以構造器的prototype屬性為原型,建立新對象;
• 将this(也就是上一句中的新對象)和調用參數傳給構造器,執行;
• 如果構造器沒有手動傳回對象,則傳回第一步建立的新對象,如果有,則舍棄掉第一步建立的新對象,傳回手動return的對象。
通過new建立對象經曆4個步驟
1、建立一個新對象;[var o = {};]
2、将構造函數的作用域賦給新對象(是以this指向了這個新對象);[Person.apply(o)] [Person原來的this指向的是window]
3、執行構造函數中的代碼(為這個新對象添加屬性);
4、傳回新對象。
https://blog.csdn.net/weixin_47047317/article/details/107567674
Q:防抖、節流
防抖:觸發高頻事件後n秒内函數隻會執行一次,如果n秒内高頻事件再次被觸發,則重新計算時間。
節流:高頻事件觸發,但在n秒内隻會執行一次,是以節流會稀釋函數的執行頻率。
防抖和節流都是為了限制事件的執行次數,防止出現事件的響應速度跟不上觸發的速度,導緻出現卡頓延遲的現象。
Q:哪些操作會造成記憶體洩漏
1.意外的全局變量
2.被遺忘的計時器或回調函數
3.脫離 DOM 的引用
4.閉包
第一種情況是我們由于使用未聲明的變量,而意外的建立了一個全局變量,而使這個變量一直留在記憶體中無法被回收。
第二種情況是我們設定了 setInterval 定時器,而忘記取消它,如果循環函數有對外部變量的引用的話,那麼這個變量會被一直留
在記憶體中,而無法被回收。
第三種情況是我們擷取一個 DOM 元素的引用,而後面這個元素被删除,由于我們一直保留了對這個元素的引用,是以它也無法被回收。
第四種情況是不合理的使用閉包,進而導緻某些變量一直被留在記憶體當中。
Q:event loop
事件循環機制
JavaScript是一門單線程非阻塞的腳本語言,單線程意味着,JavaScript代碼在執行的任何時候,都隻有一個主線程來處理所有的任務。而非阻塞則是當代碼需要進行一項異步任務(無法立刻傳回結果,需要花一定時間才能傳回的任務,如I/O事件)的時候,主線程會挂起(pending)這個任務,然後在異步任務傳回結果的時候再根據一定規則去執行相應的回調。
在異步函數中,可以細分為兩種任務,宏任務與微任務。
宏任務有以下幾種:
①I/O
②setTimeout
③setInterval
④setImmediate
⑤requestAnimationFrame
微任務有以下幾種:
①process.nextTick
②MutationObserver
③Promise.then catch finally
js執行順序: 同步函數 -> 微任務 -> 宏任務
async function async1() {
console.log(‘async1 start’);
await async2();
console.log(‘async1 end’);
}
async function async2() {
console.log(‘async2’);
}
console.log(‘script start’);
setTimeout(function() {
console.log(‘setTimeout’);
}, 0)
async1();
new Promise(function(resolve) {
console.log(‘promise1’);
resolve();
}).then(function() {
console.log(‘promise2’);
});
console.log(‘script end’);
Q:promise
(還需了解使用與實作、并行執行與順序執行)
原理
在Promise的内部,有一個狀态管理器的存在,有三種狀态:pending、fulfilled、rejected。
(1) promise 對象初始化狀态為 pending。
(2) 當調用resolve(成功),會由pending => fulfilled。
(3) 當調用reject(失敗),會由pending => rejected
Q:閉包
閉包概念
能夠讀取其他函數内部變量的函數。或簡單了解為定義在一個函數内部的函數,内部函數持有外部函數内變量的引用。
閉包用途
1、讀取函數内部的變量
2、讓這些變量的值始終保持在記憶體中。不會再f1調用後被自動清除。
3、友善調用上下文的局部變量。利于代碼封裝。
原因:f1是f2的父函數,f2被賦給了一個全局變量,f2始終存在記憶體中,f2的存在依賴f1,是以f1也始終存在記憶體中,不會在調用結束後被自動清除。
https://www.cnblogs.com/Renyi-Fan/p/11590231.html
Q:垃圾回收和記憶體洩露
垃圾回收
Javascript具有周期性的自動垃圾收集機制,執行環境會負責管理代碼運作過程中使用的記憶體。
1.标記清除(mark-and-sweep)。常用的垃圾收集方式。當變量進入環境,就将這個變量标記為“進入環境”。離開環境就标記“離開環境”。
工作流程:
1)垃圾收集器在運作時給記憶體中的所有變量加上标記。
2)去掉環境中的變量和被環境中的變量引用的變量标記。
3)後來再加上的變量會被視為準備删除的變量。
4)銷毀帶标記的值并回收他們所占用的空間。
2.引用計數:
不太常用的垃圾收集政策(reference counting)。意為:跟蹤記錄每個值被引用的次數。
工作流程:
1)聲明一個變量并将一個引用類型值賦給該變量,則這個值的引用次數為1。
2)同一個值若再次賦給另一個變量,則改值引用次數加1。
3)包含對這個值引用的變量又取得了另一個值,引用次數減1。
4)當引用次數變為0,則無法在通路這個值,就可以将其占用的記憶體回收回來。
5)垃圾回收器下次再運作時,釋放引用次數為零的值被占用的記憶體。
記憶體洩露
對記憶體使用不當的話會造成記憶體洩露。以下這幾種情況會造成記憶體的洩漏:
1.意外的全局變量引起的記憶體洩漏。
原因:全局變量,不會被回收。
解決:使用嚴格模式避免。
2.閉包引起的記憶體洩漏
原因:閉包可以維持函數内局部變量,使其得不到釋放。
解決:将事件處理函數定義在外部,解除閉包,或者在定義事件處理函數的外部函數中,删除對dom的引用。
3.沒有清理的DOM元素引用
原因:雖然别的地方删除了,但是對象中還存在對dom的引用
解決:手動删除。
4.被遺忘的定時器或者回調
原因:定時器中有dom的引用,即使dom删除了,但是定時器還在,是以記憶體中還是有這個dom。
解決:手動删除定時器和dom。
5.子元素存在引用引起的記憶體洩漏
原因:div中的ul li 得到這個div,會間接引用某個得到的li,那麼此時因為div間接引用li,即使li被清空,也還是在記憶體中,并且隻要li不被删除,他的父元素都不會被删除。
解決:手動删除清空。