前端JS面試題簡約版

禿頭_08發表於2020-11-01

1. javascript原型與原型鏈:

原型:

  1. js中每一個函式都有一個proto屬性,而且對應的是自身的原型,被稱為隱式原型
  2. 函式中除了proto屬性之外還有一個prototype屬性,被稱為顯示原型

原型鏈:

當一個物件呼叫自身不存在的屬性和方法時就會去自己的proto的前輩prototype物件上去找,如果沒找到就會去prototype的前輩上去找 直到找到或者返回undefiend 這個查詢的過程就是原型鏈

最頂層的原型物件:object.prototype

object.prototype最常用的兩種方法(tostringvalueof



2. 作用域和自由變數:

變數作用域:

就是一個變數可以使用的範圍。

作用域分為3種:

  1. 全域性作用域: js中首先有一個最外層的作用域,全域性作用域
  2. 函式作用域:js中可以通過函式來建立一個獨立作用域稱為函式作用域,函式可以巢狀,所以作用域也可以巢狀;
  3. 塊級作用域(ES6新增的):大括號,比如:if{},for(){},while(){}…

看圖:
在這裡插入圖片描述

es6作用域,只適用於const,let:

在這裡插入圖片描述

自由變數:

自由變數的概念:

  1. 一個變數在當前作用域沒有定義,但是被使用了
  2. 向上級作用域,一層一層找,直到找到為止
  3. 如果全域性作用域都沒找到,則報錯XXXX is not defined

在這裡插入圖片描述
作用域鏈:

自由變數的向上級作用域一層一層查詢,直到找到為止,最高找到全域性作用域,就形成了作用域鏈。

變數提升:

var宣告的變數,function宣告的函式存在變數提升
let const 不會變數提升

  1. javascript中宣告並定義一個變數時,會把宣告提前,以下會先列印出undefined,再列印出10
console.log(a)
var a = 10
console.log(a)

相當於:

var a
console.log(a)//undefined
a = 10
console.log(a) //10

  1. 函式宣告也是,以下函式相當於把整個fn提到作用域的最上面,所以呼叫fn時會正常列印jack
fn('jack')//jack
function fn (name){
console.log(name)
}

  1. 不過函式表示式不行,以下是一個函式表示式,JavaScript會把var fn提到作用域最上面,沒有吧函式提上去,所以會報錯。
fn("jack");//報錯
var fn = function(name) {
console.log(name);
};



3. 閉包:

閉包概念:

當一個函式的返回值是另外一個函式,而返回的那個函式如果呼叫了其父函式的內部變數,且在外部被執行,就產生了閉包

因為作用域的關係閉包有兩種表現:

  1. 函式作為引數被傳遞
  2. 函式作為返回值被返回
// 函式作為返回值
function create() {
    const a = 100
    return function () {
        console.log(a)
    }
}

const fn = create()
const a = 200
fn() // 100

// 函式作為引數被傳遞
function print(fn) {
    const a = 200
    fn()
}
const a = 100
function fn() {
    console.log(a)
}
print(fn) // 100

所有的自由變數的查詢,是在函式定義的地方,向上級作用域查詢 不是在執行的地方!!!

閉包的三大特性:

函式套函式
內部函式可以直接訪問外部函式的引數或變數
變數不會被垃圾回收機制回收 (垃圾回收機制的演算法稱為標記 清除,除標記的物件或引用外全部清除)

閉包的優點和缺點:

優點
變數長期駐紮在記憶體中
避免全域性變數的汙染
私有成員的存在

缺點
增大記憶體使用量
可能造成記憶體洩漏
解決方法是,在退出函式之前,將不使用的區域性變數全部刪除

閉包的應用場景:

函式防抖:函式防抖是指在函式被高頻觸發時當停止觸發後延時n秒再執行函式
函式節流: 原理----函式被高頻出發時延時n秒後才會再次執行, 而節流會規定的事件在規定的時間內觸發一次事件



4.this指向:

普通函式中:this是window, call apply bind可以修改this,
物件方法中:this指向當前物件(因為當前物件執行了方法)。
箭頭函式中:this始終是父級上下文中的this
es6 class中this指向當前例項本身。
call apply bind中呼叫, this指向被傳入的物件



5.js非同步之巨集任務(marcroTask)和微任務(microTask)

console.log(100);

setTimeout(()=>{
    console.log(200);
})

setTimeout(()=>{
    console.log(201);
})

Promise.resolve().then(()=>{
    console.log(300);
})

console.log(400);

// 100 400 300 200 201
// 為什麼300比200先列印

什麼是巨集任務和微任務

巨集任務: setTimeout,setInterval,Ajax,DOM事件
微任務:Promise async/await
微任務比巨集任務更早執行

非同步和同步區別:

非同步不會阻塞程式的執行,
同步會阻塞程式的執行,

非同步場景比如:

ajax請求------動態載入 -----事件繫結

事件迴圈:

事件迴圈 基於同步非同步 頁面載入的時候把同步和非同步分開 同步進入主執行緒 非同步進入event Table佇列中 當同步執行完之後會到非同步的執行緒看有沒有非同步任務有的話就拉走執行重複上步,步驟



6.資料型別:

基本資料型別(值型別): Number、String、Boolean、Undefined、Null、Symbol(es6新增獨一無二的值)
引用資料型別: Object。包含Object、Array、 function、Date

棧堆儲存:

值型別棧儲存: 主要針對(Number、String、Boolean)三種資料。
引用型別儲存把值儲存在堆記憶體中,堆記憶體是從下往上儲存。生成唯一記憶體地址。然後在棧記憶體中把地址賦值給變數。

引用型別堆疊儲存: 主要針對Object、Array這兩種引用資料以及null,
棧記憶體是從上往下儲存的。之所以如此劃分記憶體主要考慮到特別大的物件進行值傳遞時的效率問題。

變數計算-型別轉換:

  1. 字串拼接
const a=100+200; // 200
const b=100+'200' // '100200'
const c='true'+'100' // 'true100'

  1. == 與 ===

===:三等表示全等,判斷左右兩邊物件或值是否型別相同且值相等。
==:二等表示值相等。判斷操作符兩邊物件或值是否相等型別可以不同

3.if語句中的判斷:

if條件是單個值時,如果是truly值,條件成立, 如果是falsely值,條件不成立



7.淺拷貝與深拷貝

在這裡插入圖片描述
深淺拷貝概念:
深淺拷貝 淺拷貝只是拷貝物件的指標,並不是拷貝物件本身 然後建立一個新物件 新物件和原物件是共享一塊記憶體的 深拷貝是層層拷貝 把原物件的本事都拷貝出 再建立新物件 新物件和原物件不共享記憶體 修改新物件不會影響原物件

實現深拷貝的方案:

可以使用 for in 、 Object.assign 、 擴 展 運 算 符、遞迴等遞迴函式實現深淺拷貝

比如使用遞迴方案

將要拷貝的資料 obj 以引數的形式傳參
宣告一個變數 來儲存我們拷貝出來的內容
判斷 obj 是否是引用型別資料,如果不是,則直接賦值即可( 可以利用 obj instanceof Type 來進行判斷),
由於用 instanceof 判斷array 是否是object的時候,返回值為true, 所以我們在判斷的時候,直接判斷obj 是否是Array 就可避免這個問題
根據判斷的不同型別,再給之前的變數賦予不同的型別: [ ] : { }
迴圈obj 中的每一項,如果裡面還有複雜資料型別,則直接利用遞迴再次呼叫copy函式
最後 將 這個變數 return 出來即可



8.js資料型別判斷

  1. typeof判斷基本資料型別:
    typeof 對於基本資料型別判斷是沒有問題的,但是遇到引用資料型別(如:Array)是不起作用

2.instanceof判斷引用資料型別:
instanceof 判斷 new 關鍵字建立的引用資料型別不考慮 null 和 undefined(這兩個比較特殊)以物件字面量建立的基本資料型別

  1. constructor
    constructor constructor 似乎完全可以應對基本資料型別和引用資料型別 但果宣告瞭一個建構函式,並且把他的原型指向了 Array 的原型,所以這種情況下,constructor 也顯得力不從心

4.toString(返回資料型別) 完美的解決方案
使用Object.prototype.toString.call()的方式來判斷一個變數的型別是最準確的方法



9.for···in和for···of的區別

  1. 遍歷陣列角度來說,for···in遍歷出來的是key(即下標),for···of遍歷出來的是value(即陣列的值);
  2. 遍歷物件的話 for···in 遍歷出來是key for···of遍歷直接報錯。
  3. for···of也能遍歷物件 但是得配合object.keys使用


10.null和undefined區別

null: null表示沒有物件,可能將來要賦值一個物件,即該處不應該有值

作為函式的引數,表示該函式的引數不是物件

Object.getPrototypeOf(Object.prototype)
// null

undefined:表示缺少值,即此處應該有值,但沒有定義

1.定義了形參,沒有傳實參,顯示undefined
2.物件屬性名不存在時,顯示undefined

var i;
i // undefined

function f(x){console.log(x)}
f() // undefined

var  o = new Object();
o.p // undefined

var x = f();
x // undefined


11.DOM

JavaScript操作網頁的介面,全稱為“文件物件模型(Document Object Model)。 有這幾個概念:文件、元素、節點
1.整個文件是一個文件節點
2.每個標籤是一個元素節點
3.包含在元素中的文字是文字節點
4.元素上的屬性是屬性節點
5.文件中的註釋是註釋節點

什麼是虛擬dom,和 diff 演算法:

虛擬DOM:

虛擬 DOM 的最終目標是將虛擬節點渲染到檢視上但是如果直接使用虛擬節點覆蓋舊節點的話,會有很多不必要的 DOM操作:比如有一個ul標籤 然後裡面有很多的li 其中的一個li需要變化 就用新的ul去替換舊的ul 這樣會造成效能上的浪費

Diff演算法:

用 JS 物件結構表示 DOM 樹的結構;然後用這個樹構造一個真正的 DOM 樹,插到文件中狀態改變的時候,重新構造一個新的物件樹。然後用新的樹和舊的樹做比較,記錄兩棵樹差異 把記錄的差異應用到正真的DOM樹上 然後更新頁面

DOM 節點操作

  1. 新增節點
  2. 查詢子節點
  3. 查詢父節點
  4. 刪除節點
1. 建立新節點  (document呼叫)

createDocumentFragment() //建立一個DOM片段
createElement() //建立一個具體的元素
createTextNode() //建立一個文字節點

2. 新增、移除、替換、插入  (父元素呼叫)
appendChild() //新增
removeChild() //移除
replaceChild() //替換
insertBefore() //插入

3. 查詢 (document呼叫)
getElementsByTagName() //通過標籤名稱 一組元素
getElementsByName() //通過元素的Name屬性的值 一組元素
getElementById() //通過元素Id,唯一性單個元素

4. 關係  (元素呼叫前三個父元素呼叫 )
childNodes // 獲取所有的子節點
children  // 獲取所有的子元素節點 (常用)
firstElementChild   獲取第一個元素
lastElementChild  獲取最後一個元素 


12.事件繫結、事件流 、自定義事件

什麼是事件?

使用者與web頁面發生某些特定互動的瞬間 事件就產生了

DOM事件:
分為3種級別 DOM0 DOM1 DOM2

DOM0級別事件分兩種:
一 是直接在標籤內直接新增執行語句
二 是定義執行函式

DOM2級別事件:
第一個引數:事件名稱
第二個引數:執行函式
第三個引數:指定冒泡還是捕獲 預設是false,冒泡

DOM3級事件:
跟DOM2差不多 只不過多新增了些鍵盤事件、滑鼠事件

DOM事件兩種型別

事件捕獲、事件冒泡
事件捕獲是網景時提出的,事件捕獲就是由外往內,從事件發生的頂點開始,逐級往下查詢,一直到目標元素。
事件冒泡是IE提出的 事件冒泡就是由內往外,從具體的目標節點元素觸發,逐級向上傳遞,直到根節點
在這裡插入圖片描述

事件流:

事件流描述的是從頁面接收事件的順序,包括三個階段:事件捕獲階段、目標階段和事件冒泡階段。

自定義事件可以通過new Event()來定義

var eve = new Event('test'); //通過new Event 建立事件
dom.addEventListener('test', function () { //註冊事件
    console.log('test dispatch');
});
setTimeout(function () {
    dom.dispatchEvent(eve);  //觸發事件
}, 1000);

13.js中事件委託

概念:

事件委託說白了就是利用事件冒泡,就是把子元素的事件都繫結到父元素上。如果子元素阻止了事件冒泡,那麼委託也就沒法實現了。

好處:

可以大量節省記憶體佔用,減少事件註冊,比如在ul上代理所有li的click事件就非常棒

例子:

<ul id="myLinks">
  <li id="goSomewhere">Go somewhere</li>
  <li id="doSomething">Do something</li>
  <li id="sayHi">Say hi</li>
</ul>

如果老方法 就得每一個li都新增點選事件 ,這樣會對記憶體消耗非常大

    var item1 = document.getElementById("goSomewhere");
    var item2 = document.getElementById("doSomething");
    var item3 = document.getElementById("sayHi");
 
    item1.onclick = function() {
      location.href = "http://www.baidu.com";
    };
    item2.onclick = function() {
      document.title = "事件委託";
    };
    item3.onclick = function() {
      alert("hi");
    };

這個時候我們就可以用事件委託的方式 把 li 的點選事件放在父標籤上

    var item1 = document.getElementById("goSomewhere");
    var item2 = document.getElementById("doSomething");
    var item3 = document.getElementById("sayHi");
 
    document.addEventListener("click", function (event) {
      var target = event.target;
      switch (target.id) {
        case "doSomething":
          document.title = "事件委託";
          break;
        case "goSomewhere":
          location.href = "http://www.baidu.com";
          break;
        case "sayHi": alert("hi");
          break;
      }
    })



14.原生axios建立步驟

建立 標準瀏覽器 物件,也就是建立一個非同步呼叫物件
建立一個新的 HTTP 請求,並指定該 HTTP 請求的方法、URL 及驗證資訊
設定響應 HTTP 請求狀態變化的函式
傳送 HTTP 請求
獲取非同步呼叫返回的資料
使用 JavaScript和 DOM 實現區域性重新整理

Ajax優點:

頁面區域性重新整理,使用者體驗好。
非同步通訊,更加快的響應能力。

Ajax缺點:

對搜尋引擎的支援比較弱。
破壞了程式的異常機制。



15.jsonp 的原理

主要是利用動態建立 script 標籤請求後端介面地址,然後傳遞 callback引數,後端接收 callback,然後處理資料,返回 callback 函式呼叫的形式,callback 中的引數就是 json。



16.sessionStorage localStorage cookie的區別:

1:localStorage
localStorage的生命週期是永久的,除非使用者在瀏覽器提供的UI上清除localStorage資訊,否則這些資訊將永久存在, 而且它僅在客戶端儲存,不參與伺服器通訊

  1. sessionStorage
    sessionStorage僅在當前會話下有效,不參與伺服器通訊,原生介面可以接受,也可以在此封裝起來對object和array有更好的支援

不同瀏覽器無法共享兩種資訊 相同瀏覽器不同頁面可以共享localStorage 相同瀏覽器不同頁面或者標籤不能共享sessionStorage

localStorage適用於儲存長期資料 sessionStorage儲存一次性的敏感資料

3.cookie(HTTP Cookie)通過name=value的方式儲存
cookie的儲存資料大小隻有4kb左右,有個數限制,一般不能超過20個。,cookie會參與伺服器端通訊,每次都會攜帶在HTTP頭中,如果儲存過多會有效能問題。cookie也是不安全的,但是可以設定cookie的生命週期,使之不會永遠有效。
cookie的優點:具有極高的擴充套件性和可用性.
cookie的缺點.: 長度和數量有限制,每一個域中最多隻能儲存20條,每個不能超過4kb,,否則就會被攔截掉。如果cookie被攔截 那麼就可以獲取所有的session資訊,加密也沒用。

17.apply、call、bind

call/apply改變了函式的this上下文後馬上執行該函式
bind則是返回改變了上下文後的函式,不執行該函式
bind使用的時候需要加() 括號

18.js設計模式

分為 -----單例模式----- 工廠模式-----觀察者、訂閱者模式

  1. 單例模式:

概念:保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點

專案中應用:在vue腳手架專案開發中,我們需要對axios進行請求攔截,響應攔截,多次呼叫封裝號的axios例項也僅設定一次,封裝後的axios就是要一個單例。

  1. 工廠模式:

工廠模式是我們最常用的例項化物件模式了,是用工廠方法代替new操作的一種模式。

  1. 觀察者、訂閱者模式:

vue.js 則是採用資料劫持結合釋出者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個屬性的setter,getter,在資料變動時釋出訊息給訂閱者,觸發相應的監聽回撥。

19.JS中的Promise

promise是什麼?

所謂 Promise,就是一個物件,用來傳遞非同步操作的訊息。它代表了某個未來才會知道結果的事件(通常是一個非同步操作),並且這個事件提供統一的 API,可供進一步處理

promise還有三個狀態:

1.pending------初始狀態
2.Fulfilled------ 操作成功
3.Rejected-----操作失敗

為什麼有Promises這個東西?

同步的方式寫非同步的程式碼,用來解決回撥地獄問題。
此外,promise物件提供統一的介面,使得控制非同步操作更加容易。

Promise物件的狀態不受外界影響
promise的狀態是不可逆的,一經改變,不會再變
promise有兩個引數一個是resolve 一個是reject

resolve----將promise物件從未成功變為成功,成功呼叫,將非同步操作的結果作為引數返回
reject------將promise物件從成功變為失敗。失敗呼叫,將非同步操作的結果作為引數返回

相關文章