JavaScript基本面試題

JS的幾條基本規範:

1、不要在同一行聲明多個變量

2、請使用===/!==來比較true/false或者數值

3、使用對象字面量替代new Array這種形式

4、不要使用全局變量

5、Switch語句必須帶有default分支

6、函數不應該有時候有返回值,有時候沒有返回值

7、For循環必須使用大括號

8、IF語句必須使用大括號

9、for-in循環中的變量應該使用var關鍵字明確限定作用域,從而避免作用域污染。

JS引用方法:

// 行內引入
<body>
    <input type="button" onclick="alert('行內引入')" value="按鈕"/>
    <button onclick="alert(123)">點擊我</button>
</body>

// 內部引入
<script>
    window.onload = function() {
        alert("js內部引入!")
    }
</script>

// 外部引入
<body>
    <div></div>
    <script type="text/javascript" src="./js/index.js"></script>
</body>

注意:

1、不推薦寫行內或者HTML中插入<script>,因爲瀏覽器解析順序的緣故,如果解析到死循環之類的JS代碼,會卡住頁面。

2、建議在onload事件之後,即等HTML、CSS渲染完畢再執行代碼

JS的基本數據類型

Undefined、Null、Boolean、Number、String

新增:Symbol

數組操作

在JavaScript中,用的較多的之一無疑是數組操作,這裏過一遍數組的一些用法:

map:遍歷數組,返回回調返回值組成的新數組

forEach:無法break,可以用try/catch中throw new Error來停止

filter:過濾

some:有一項返回true,則整體爲true

every:有一項返回false,則整體爲false

join:通過指定連接符生成字符串

push/pop:末尾推入和彈出,改變原數組,返回推入/彈出項 【有誤】

unshift/shift:頭部推入和彈出,改變原數組,返回操作項  【有誤】

sort(fn)/reverse:排序與反轉,改變原數組

concat:連接數組,不影響原數組,淺拷貝

slice(start,end):返回截斷後的新數組,不改變原數組

splice(start,number,value...):返回刪除元素組成的數組,value爲插入項,改變原數組

indexof/lastIndexOf(value,fromIndex):查找數組項,返回對應的下標

reduce/reduceRight(fn(prev,cur),defaultPrev):兩兩執行,prev爲上次化簡函數的return值,cur爲當前值(從第二項開始)

JS有哪些內置對象

Object是JavaScript中所有對象的父對象

數據封裝對象:Object、Array、Boolean、Number和String

其他對象:Function、Arguments、Math、Date、RegExp、Error

get請求傳參長度的誤區

誤區:我們經常說get請求參數的大小存在限制,而post請求的參數大小是無限制的

實際上HTTP協議從未規定GET/POST的請求長度限制是多少。對get請求參數的限制是來源與瀏覽器或web服務器,瀏覽器或web服務器限制了url的長度。爲了明確這個概念,我們必須再次強調下面幾點:

1、HTTP協議未規定GET和POST的長度限制

2、GET的最大長度顯示是因爲瀏覽器和web服務器限制了URL的長度

3、不同的瀏覽器和WEB服務器,限制的最大長度是不一樣的

4、要支持IE,則最大長度爲2083byte,若只支持Chrome,則最大長度8182byte

補充get和post請求在緩存方面的區別

get請求類似於查找的過程,用戶獲取數據,可以不用每次都與數據庫連接,所以可以使用緩存

post不同,post做的一般是修改和刪除的工作,所以必須與數據庫交互,所以不能使用緩存。因此get請求適合於請求緩存。

閉包

什麼是閉包?

函數A裏面包含了函數B,而函數B裏面使用了函數A的變量,那麼函數B被稱爲閉包。

又或者:閉包就是能夠讀取其他函數內部變量的函數

function A() {
    var a=1;
    function B() {
        console.log(a);
    }
    return B();
}

閉包的特徵

函數內再嵌套函數

內部函數可以引用外層的參數和變量

參數和變量不會被垃圾回收制回收

對閉包的理解

使用閉包主要是爲了設計私有的方法和變量。閉包的優點是可以避免全局變量的污染,缺點是閉包會常駐內存,會增大內存使用量,使用不當很容易造成內存泄露。在js中,函數即閉包,只有函數纔會產生作用域的概念

閉包的最大用處有兩個,一個是可以讀取函數內部的變量,另一個就是讓這些變量始終保持在內存中

閉包的另一個用處,是封裝對象的私有屬性和私有方法

閉包的好處

能夠實現封裝和緩存等

閉包的壞處

就是消耗內存、不正當使用會造成內存溢出的問題

使用閉包的注意點

由於閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露

解決方法:在退出函數之前,將不使用的局部變量全部刪除

//閉包的經典問題
for(var i=0;i<3; i++) {
    setTimeout(function() {
        console.log(i);
    },1000);
}

這段代碼輸出答案:3個3

解析:首先,for循環是同步代碼,先執行三遍for,i變成了3;然後再執行異步代碼setTimeout,這時候輸出的i,只能是3個3了

有什麼辦法依次輸出0,1,2

第一種方法

// 使用let
for(let i=0; i<3; i++) {
    setTimeOut(function () {
        cosole.log(i)
    },1000);
}

在這裏,每個let和代碼塊結合起來形成塊級作用域,當setTimeout()打印時,會尋找最近的塊級作用域的i,所以依次打印出0,1,2

如果這樣不明白,我們可以執行下邊這段代碼

for(let i=0;i<3;i++) {
    console.log("定時器外部:"+i);
    setTimeout(function() {
        console.log(i);
    },1000);
}

此時瀏覽器依次輸出的是:

定時器外部:0

定時器外部:1

定時器外部:2

即代碼還是先執行for循環,但是當for結束執行到了setTimeout的時候,它會做個標記,這樣到了console.log(i)中,i就能找到這個塊中最近的變量定義

第二種方法

// 使用立即執行函數解決閉包問題
for(let i=0;i<3;i++) {
    (function(i) {
        setTimeout(function() {
            console.log(i) 
        },1000);
    })(i)
}

JS作用域及作用域鏈

作用域

在JavaScript中,作用域分爲全局作用域和函數作用域

全局作用域:

代碼在程序的任何地方都能被訪問,window對象的內置屬性都擁有全局作用域

函數作用域:

在固定的代碼片段才能被訪問

例子:

作用域有上下級關係,上下級關係的確定就看函數是在哪個作用域下創建的。如上,fn作用域下創建了bar函數,那麼“fn作用域”就是“bar作用域”的上級。

作用域最大的用處就是隔離變量,不同作用域下同名變量不會有衝突。

變量取值:到創建這個變量的函數的作用域中取值

作用域鏈

一般情況下,變量取值到創建這個變量的函數的作用域中取值。

但是如果在當前作用域中沒有查到值,就會向上級作用域去查,直到查到全局作用域,這麼一個查找過程形成的鏈條就叫做作用域鏈。

原型和原型鏈

原型和原型鏈的概念

每個對象都會在其內部初始化一個屬性,就是prototype(原型),當我們訪問一個對象的屬性時,如果這個對象內部不存在這個屬性,那麼他就會去prototype裏找這個屬性,這個prototype又會有自己的prototype,於是就這樣一直找下去

原型和原型鏈的關係

instance.constructor.prototype = instance._proto_

原型和原型鏈的特點

JavaScript對象是通過引用來傳遞的,我們創建的每個新對象實體中並沒有一份屬於自己的原型副本。當我們修改原型時,與之相關的對象也會繼承這一改變

當我們需要一個屬性的時候,JavaScript引擎會先看當前對象中是否有這個屬性,如果沒有的話就會查找他的Prototype對象是否有這個屬性,如此遞推下去,一直檢索到Object內建對象

組件化和模塊化

組件化

爲什麼要組件化開發

有時候頁面代碼量太大,邏輯太多或者同一個功能組件在許多頁面均有使用,維護起來相當複雜,這個時候,就需要組件化開發來進行功能拆分、組件封裝,以達到組件通用性,增強代碼可讀性,維護成本也能大大降低

組件化開發的優點

很大程度上降低系統各個功能的耦合性,並且提高了功能內部的聚合性。這對前端工程化及降低代碼的維護來說,是有很大的好處的,耦合性的降低,提高了系統的伸展性,降低了開發的複雜度,提升開發效率,降低開發成本

組件化開發的原則

專一

可配置性

標準型

複用性

可維護性

模塊化

爲什麼要模塊化

早期的JavaScript版本沒有塊級作用域、,沒有類、沒有包、也沒有模塊,這樣會帶來一些問題,如複用、依賴、衝突、代碼組織混亂等,隨着前端的膨脹,模塊化顯得非常迫切

模塊化的好處

避免變量污染,命名衝突

提高代碼複用率

提高了可維護性

方便依賴關係管理

模塊化的幾種方法

  • 函數封裝

var myModule = {
    var1:1,
    var2:2,
    fn1:function() {
    },
    fn2:function() {
    }
}

總結:這樣避免了變量污染,只要保證模塊名唯一即可,同時同一模塊內的成員也有了關係

缺陷:外部可以隨意修改內部成員,這樣就會產生意外的安全問題

  • 立即執行函數表達式(IIFE)
var myModule = (function() {
    var var1 = 1;
    var var2 = 2;
    
    function fn1() {
    
    }
    function fn2() {

    }
    return {
        fn1:fn1,
        fn2:fn2
    };
})();

總結:這樣在模塊外部無法修改我們沒有暴露出來的變量、函數

缺點:功能相對較弱,封裝過程增加了工作量,仍會導致命名空間污染可能、閉包是有成本的

圖片的預加載和懶加載

  • 預加載:提前加載圖片,當用戶需要查看時可直接從本地緩存中渲染
  • 懶加載:懶加載的主要目的是作爲服務器前端的優化,減少請求數或延遲請求數

兩種技術的本質:兩者的行爲是相反的,一個是提前加載,一個是遲緩甚至不加載。預加載則會增加服務器前端壓力,懶加載對服務器有一定的緩解壓力作用。

mouseover和mouseenter的區別

mouseover:

當鼠標移入元素或其子元素都會觸發事件,所以有一個重複觸發,冒泡的過程。對應的移除事件是mouseout

mouseenter:

當鼠標移除元素本身(不包含元素的子元素)會觸發事件,也就是不會冒泡,對應的移除事件是mouseleave

解決異步回調地獄

promise、generator、async/await

對this對象的理解

this總是指向函數的直接調用者(而非間接調用者)

如果有new關鍵字,this指向new出來的那個對象

在事件中,this指向觸發這個事件的對象,特殊的是,IE中的attachEvent中的this總是指向全局對象Window

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章