1. 程式人生 > 其它 >前端必須掌握的JavaScript基礎知識

前端必須掌握的JavaScript基礎知識

做為前端三劍客的JavaScript,可以說是重中之重了,是前端開發必不可缺的基礎,因為有了JavaScript,才使靜態的頁面產生了一些動態的效果,你對JavaScript瞭解有多少?本文整理了個人學習中一些Javascript基礎知識,以便查漏補缺。

1. 說一下你瞭解的主要瀏覽器及其核心

2. js中資料型別分為幾種,都有哪些

  • 簡單資料型別: numberstringBooleannullundefinedsymbol

  • 複雜資料型別: objectarrayfunction

    注意: 簡單資料型別是沒有屬性和方法的且簡單資料型別的值不可改變,儲存複製的是值本身,儲存在棧記憶體;引用資料儲存與複製的是一個地址,改變其中一個另一個也會改變

3. 棧記憶體與堆記憶體的瞭解

  • 棧記憶體 棧記憶體儲存的都是一些比較簡單的資料和堆記憶體的地址,舉個例子

圖中可以看出當改變了第一個num的值,num1的值不會因為num的值改變而改變,也再次印證了簡單資料型別儲存複製的是值本身

  • 堆記憶體 堆記憶體儲存的是一些比較複雜的資料,資料儲存在堆記憶體,資料的地址儲存在棧記憶體裡面,舉個例子

圖中可以看出當改變了obj的name屬性,obj1的name屬性也跟著改變了,原因是 引用型別複製的是引用地址,obj和obj1指向的是堆記憶體中同一塊空間,無論改變obj還是obj1 其實都是改變的同一個資料

3. typeof返回值有哪些

number

stringBooleanundefinedobjectfunction

  • typeof typeof 返回 string

  • typeof null 、 typeof 物件 、 typeof 陣列 都返回object

  • 返回false的情況: 0 "" null false NaN undefined 不成立的表示式

4. 如何檢測一個物件是不是陣列

  • 第一種方法: instanceof 運算子用來測試一個物件在其原型鏈中是否存在一個建構函式的 prototype 屬性。

分析過程:

圖中可知 arr是Array構造出來的例項,arr.__proto__

屬性指向了構造它的建構函式的原型物件Array.prototype,由此得知 arr物件的原型鏈上有Array建構函式的prototype屬性。而arr.constructor指向了構造它的建構函式,因為arr例項沒有constructor屬性,就去原型鏈找,原型鏈上有constructor ,由此得知 arr.constructor === Array

注意: 但是此方法有漏洞,用此方法判斷不一定準確,因為我們可以改變一個物件的this指向,這裡就不做過多敘述了

  • 第二種方法: Object.prototype.toString 借用Object原型上的方法來實現檢測陣列

分析過程:

toString方法是Object原型上的方法,像functionArray作為Object的例項,重寫了toString的方法。而我們通過原型鏈的知識可以知道,我們在呼叫toString方法時,並不能直接呼叫Object原型上的toString方法,呼叫的是重寫後的方法。所以,我們想得到物件具體型別的話,應該使用call借調Object原型上的toString方法,這點MDN文件也有說明

  • 第三種方法: Array.isArray此方法接收一個引數,用於確定傳遞的值是否是一個 Array

分析過程:

此方法為ES5新增的方法,可以直接判斷引數是不是一個數組型別,MDN文件說明

5. 自增自減運算子和邏輯運算子

  • ++運算子: 寫到後面叫做後自增,寫到前面叫做先自增
let a = 10
let b = ++a + a++
console.log(a) // 12
console.log(b) // 22
//分析: ++a 是a先加1賦值給自己,此時a是11,再加上11,所以b等於11 + 11 = 22,最後a++,a變成了12

let a = 5
let b = a++ + ++a + a++
console.log(b)  // 19
console.log(a) // 8

let a = 10
let b = a-- + ++a
console.log(b) // 20
console.log(a) // 10 
  • --運算子: 和自增運算子同,就不多說了

  • 邏輯運算子

    • && : 假前真後,全真才為真 有一個假就是假

    • || : 真前假後,全假才為假 有一個真就是真

    • ! : 取反 轉換為布林值

6. 氣泡排序

  • 初級版本

    • 讓陣列中每個值都兩兩進行比較一趟的結果,最大的數會在陣列的最後面
    let arr = [23, 46, 45, 39, 66, 82]
    for (let i = 0; i < arr.length - 1; i++) {
        if (arr[i] > arr[i + 1]) {
            let temp = arr[i]
            arr[i] = arr[i + 1]
            arr[i + 1] = temp
        }
    }
    console.log(arr)
    //分析:陣列從arr[0]兩兩和後面數相比,一共比五次。所以讓length - 1,讓i最大取值到4就可以了。當i取值到4的時候 arr[4]和arr[5]相比,也就是arr.length - 2 和 arr.length - 1相比 
    
    • 讓兩兩比較的結果多次執行,就會一次一次的把最大的數往後排,於是外面套一個for迴圈控制比多少趟
    for (j = 0; j < arr.length - 1; j++) {
        for (let i = 0; i < arr.length - 1; i++) {
          if (arr[i] > arr[i + 1]) {
                let temp = arr[i]
                  arr[i] = arr[i + 1]
                  arr[i + 1] = temp
          }
          }
    } 
    
  • 高階版本

    //第0趟  比較5次  多比了0次 
    //第1趟  比較4次  多比了1次  找到了1個最大的數
    //第2趟  比較3次  多比了2次  找到了2個最大的數
    //思路:遍歷第一圈的時候,兩兩相比 一共比了5次,第二圈的時候由於已經找到了1個最大的數,此圈少比一次就可以了。但是for迴圈此時還是走了5圈,所以此時是多比了一次的 ,以此類推
    let arr = [23, 46, 45, 39, 66, 82]
    for (j = 0; j < arr.length - 1; j++) {
        for (let i = 0; i < arr.length - 1 - j; i++) {  // 讓length再減j
            if (arr[i] > arr[i + 1]) {
                let temp = arr[i]
                    arr[i] = arr[i + 1]
                    arr[i + 1] = temp
            }
            }
    } 
    
  • 終極版本 如果在排序過程中,發現數組已經排好序了,後面的次數就沒必要排了

    • 假設成立法
    //判斷陣列中的成績是否都及格了 
    let arr = [80,100,90,65,54]
    //第一種思路:
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] >= 60) {
            console.log('都及格了')   
        }else {
            console.log('不及格')
        }
    }
    //第二種思路:一旦找到小於60的 就代表並不是都及格了
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] < 60) {
            console.log('並未都及格')
    } 
    

    思路:排序過程中,假設陣列是有順序的那麼就不用再次排序了。也就是說只要找到後一項比前一項大,那麼假設就不成立

    for (let i = 0; i < arr.length; i++) {
        if (arr[i] > arr[i + 1]) {
            console.log('假設失敗')
    } 
    
    • 優化氣泡排序
    let arr = [23, 46, 45, 39, 66, 82]
    for (j = 0; j < arr.length - 1; j++) {
        let flag = true
        for (let i = 0; i < arr.length - 1 - j; i++) {
        	if (arr[i] > arr[i + 1]) {
                flag = false
                let temp = arr[i]
            	arr[i] = arr[i + 1]
            	arr[i + 1] = temp
        	} else if (flag == true){
                break
            }
    	}
    } 
    

7. 簡述下淺拷貝與深拷貝

  • 淺拷貝: 拷貝的是物件的一層屬性,如果物件裡面還有物件,則只會拷貝地址,修改其中一個會相互影響,適合拷貝簡單資料型別
let obj = {
    name: 'zs',
    age: 18,
    money: 1000
}
function Copy(obj) {
    let newObj = {}
    for (let k in obj) {
        newObj[k] = obj[k]
    }
    return newObj
}
console.log(Copy(obj)) 
  • 深拷貝: 拷貝物件的多層物件,如果物件裡面還有物件,使用遞迴的方式去實現
let obj = {
    name: 'zs',
    age: 18,
    money: 1000,
    smoke: {
        brand: 'suyan',
        num: 20
    }
}
function Copy(obj) {
    let newObj = {}
    for (let k in obj) {
        newObj[k] = typeof obj[k] === 'object'? Copy(obj[k]) : obj[k]
    }
    return newObj
}
console.log(Copy(obj))  // 修改obj不會影響到newObj 

8. 說一下你對原型和原型鏈的理解

函式都有prototype屬性,這個屬性是一個物件,我們稱之為原型物件;每一個物件都有__proto__屬性,該屬性指向了原型物件,原型物件也是物件,也有__proto__屬性,這樣一層一層形成了鏈式結構,我們稱之為原型鏈

9. 閉包的理解

相互巢狀關係的兩個函式,當內部函式引用外部函式的變數的時候就形成了閉包,閉包將會導致原有的作用域鏈不釋放,造成記憶體洩露。有些地方說內部函式被儲存到外部的時候形成閉包其實是不具體的,儲存到外部只是為了方便呼叫內部的這個函式,而函式巢狀的原因則是因為需要區域性變數,如果是全域性變數就達不到閉包的目的了

  • 閉包的優點: 形成私有空間,避免全域性汙染;持久化記憶體,儲存資料

  • 閉包的缺點: 持久化記憶體導致的記憶體洩露,解決辦法 儘量避免函式的巢狀;執行完的變數賦值為null,讓垃圾回收機制回收釋放記憶體

經典案例:點選li獲取下標

<ul>
  <li>111</li>
  <li>222</li>
  <li>333</li>
  <li>444</li>
  <li>555</li>
</ul>
  var lis = document.querySelectorAll('li')
  for (var i = 0; i < lis.length; i++) {
    (function (j) {
      lis[j].onclick = function () {
        console.log(j)
      }
    })(i)
  } 

10. call、apply、bind方法的區別

  • call和apply方法都可以呼叫函式,方法內的第一個引數可以修改this指向

  • call方法可以有多個引數,除了第一個引數標識this指向,其他引數作為函式的實參傳遞給函式; apply方法最多有兩個引數,第一個引數標識this指向,第二個引數是一個數組或者偽陣列,數組裡面的每一項作為函式的實參傳遞給函式

  • bind方法不能自動呼叫函式,它會建立一個副本函式,並且繫結新函式的this指向bind返回的新函式

11. 偽陣列有哪些

  • 函式引數列表 arguments

  • DOM 物件列表 和 childNodes 子節點列表

  • jquery物件 比如$("div")

12. 偽陣列和真陣列有什麼區別,偽陣列如何轉換為真陣列呢

區別

  • 偽陣列其實是一個物件,真陣列是Array

  • 偽陣列擁有length屬性,但長度不可以改變,真陣列長度是可以改變的

  • 偽陣列不具備真陣列的方法,比如 push 、 slice等等

轉換

  • call借調陣列方法
  • ES6新語法 Array.from方法從一個類似陣列或可迭代物件建立一個新的,淺拷貝的陣列例項
  • ES6新語法 擴充套件運算子

這裡有個注意的點,使用自己定義的偽陣列的時候,擴充套件運算子無法轉換成真陣列,百度了下才知道自己定義的偽陣列由於缺少遍歷器Iterator會報錯

13. 瞭解過陣列的降維(扁平化)嗎

  • 借調陣列原型上的concat方法

    let arr = [1,2,3,[4,5]]
    Array.prototype.concat.apply([], arr) 
    
  • 使用陣列的concat方法和擴充套件運算子

    let arr = [1,2,3,[4,5]]
    [].concat(...arr) 
    

注意: 以上兩種方法只能實現一層巢狀,如果是多層的巢狀就用下面兩個方法

  • 利用Array.some方法判斷陣列中是否還存在陣列,如果存在用展開運算子配合concat連線陣列

    let arr = [1,2,3,[4,[5,6]]]
    while (arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr);
    }
    console.log(arr); 
    
  • ES6中的flat函式實現陣列的扁平化

    let arr = [1,2,3,[4,[5,6]]]
    arr.flat( Infinity )
    //flat方法的infinity屬性,可以實現多層陣列的降維 
    

14. var const let 有哪些不同

  • var宣告的變數存在變數提升,let const無

  • var可以重複宣告同名變數, let const 不可以,會報錯 has already been declared

  • let const 宣告變數有塊級作用域,var沒有

  • const定義的變數是常量不能修改,但是如果是物件或者陣列可以修改屬性,增加屬性

15. this指向問題

  • 函式呼叫模式, this指向window

  • 建構函式呼叫模式, this指向新建立的例項物件

  • 方法呼叫模式, this指向呼叫方法的物件

  • 上下文呼叫模式, call和apply方法中, this指向方法內的第一個引數;bind方法中, bind建立的新函式的this繫結為bind方法中新的函式

  • 在事件處理函式中,this指向觸發事件的當前元素

  • 定時器中,this指向window

  • 箭頭函式中沒有this指向問題,它的this和外層作用域的this保持一致

  • 匿名函式中的this總是指向window

16. 你是如何理解面向物件的,它和麵向過程有什麼區別

  • 面向物件是一種軟體開發的思想,就是把程式看作一個物件,將屬性和方法封裝中,以提高程式碼的靈活性、複用性、可擴充套件性。面向物件有三大特性:封裝 繼承 多型。封裝指的是把屬性或者方法儲存在物件中的能力,繼承指的是由另一個類得來的屬性和方法的能力,多型指的是編寫能以多種方法執行的函式或方法的能力。面向物件開發優點是易維護 易擴充套件 降低工作量,縮短開發週期,缺點是效能低

  • 面向過程是一種以過程為中心的程式設計思想,就是把解決問題分為一個一個的步驟,先幹什麼後幹什麼,然後用函式把這些步驟一步一步實現,使用的時候一個一個依次呼叫就可以了。面向過程的優點就是效能比面向物件高,因為類呼叫時需要例項化,開銷比較大,比較消耗資源;比如微控制器、嵌入式開發、 Linux/Unix等一般採用面向過程開發,效能是最重要的因素,缺點就是沒有面向物件那樣易維護、易擴充套件、易複用

17. 陣列去重的方法

//第一種方法
const arr = [1,2,3,3,4,5,5,5,7]
const newArr = []
arr.forEach(item =>{
    if(!newArr.includes(item)) {
        newArr.push(item)
	}
})
console.log(newArr)
//第二種方法 es6新增一個set方法 set有一個特徵,內部的值不允許重複
const arr = [1,2,3,3,4,5,5,5,7]
const set = new Set(arr)
const newArr = [...set]  // ... 展開運算子 可以展開陣列或者物件 

最後

為了讓大家快速精通JavaScript,在這裡免費分享給大家一份Javascript學習指南。

Javascript學習指南文件涵蓋了javascript 語言核心、詞法結構 、型別、值和變數 、表示式和運算子 、語句、物件 、陣列 、函式 、類和模組 、 正則表示式的模式匹配、 javascript的子集和擴充套件 、伺服器端javascript /客戶端javascript 、web瀏覽器中的javascript 、window物件 、指令碼化文件、指令碼化css 、事件處理等22章知識點。內容豐富又詳細,拿下網際網路一線公司offfer的小夥伴都在看。

每個知識點都有左側導航書籤頁,看的時候十分方便,由於內容較多,下面列舉的部分內容和圖片。

物件

  • 建立物件
  • 屬性的查詢和設定
  • 刪除屬性
  • 檢測屬性
  • 列舉屬性
  • 屬性getter和setter
  • 屬性的特性

陣列

  • 建立陣列
  • 陣列元素的讀和寫
  • 稀疏陣列
  • 陣列長度
  • 陣列元素的新增和刪除
  • 陣列遍歷
  • 多維陣列

函式

  • 函式定義
  • 函式呼叫
  • 函式的實參和形參
  • 作為值的函式
  • 作為名稱空間的函式
  • 閉包
  • 函式屬性、方法和建構函式

類和模組

  • 類和原型
  • 類和建構函式
  • javascript中java式的類繼承
  • 類的擴充
  • 類和型別
  • javascript中的面向物件技術
  • 子類

正則表示式的模式匹配

  • 正則表示式的定義
  • 用於模式匹配的string方法
  • regexp物件

javascript的子集和擴充套件

  • javascript的子集
  • 常量和區域性變數
  • 解構賦值
  • 迭代
  • 函式簡寫
  • 多catch 從句
  • e4x: ecmascript for xml


web瀏覽器中的javascript

  • 客戶端javascript
  • 在html裡嵌入javascript
  • javascript程式的執行
  • 相容性和互用性
  • 可訪問性
  • 安全性
  • 客戶端框架


window物件

  • 計時器
  • 瀏覽器定位和導航
  • 瀏覽歷史
  • 瀏覽器和螢幕資訊
  • 對話方塊
  • 錯誤處理
  • 作為window物件屬性的文件元素

如果你有其他語言的程式設計經歷,這份文件會有助你瞭解JavaScript是一門高階的、動態的、弱型別的程式語言,非常適合面向物件和函式式的程式設計風格。

我在這裡將這份完整版的JS學習指南電子版文件提供出來,感興趣的朋友都可以找我拿一份學習!(純免費的一個分享,希望能給大家帶來實質性的幫助)

快速入手通道:【點選這領取Javascript學習指南電子版】

你的支援,我的動力;祝各位前程似錦,offer不斷!!!