最全JavaScript基礎總結

monkeySoft發表於2020-07-20

JavaScript介紹

什麼是JavaScript?

Javascript是一門物件導向的,跨平臺的指令碼語言。

JavaScript有什麼特點?

  • 解釋性指令碼語言
  • 執行在瀏覽器(瀏覽器核心帶有js直譯器,Chrome v8引擎)
  • 弱型別語言(鬆散型)
  • 事件驅動(動態)
  • 跨平臺

JavaScript有什麼用途?

  • 嵌入動態文字於HTML頁面
  • 對瀏覽器事件做出響應
  • 讀寫HTML元素
  • 在資料被提交到伺服器之前驗證資料
  • 檢測訪客的瀏覽器資訊(可以使用js程式碼判斷瀏覽器型別)
  • 控制cookies,包括建立和修改等
  • 基於Node.js技術進行伺服器端程式設計

JavaScript作用域?

作用域:變數的作用範圍,主要有以下幾種:

  • 全域性變數

作用範圍為整個程式的執行範圍,在函式體外部定義的變數就是全域性變數,在函式體內部不使用var定義的也是全域性變數。

  • 區域性變數

作用範圍是某個函式體內部,在函式體內部通過var關鍵字定義的變數或者形參,都是區域性變數,當區域性變數與全域性變數重名時,在函式體內部區域性變數優先於全域性變數。

  • 變數提升

變數的宣告會提升至當前作用域的最頂端,但不會提升賦值

暫存性死區

在相同的函式或塊作用域內重新宣告同一個變數會引發SyntaxError;

在宣告變數或常量之前使用它, 會引發ReferenceError. 這就是暫存性死區。

這裡主要存在兩個坑點:

  • switch case中case語句的作用域
switch (x) {
  case 0:
    let foo;
    break;
    
  case 1:
    let foo; // TypeError for redeclaration.
    break;
}

會報錯是因為switch中只存在一個塊級作用域, 改成以下形式可以避免:

let x = 1;

switch(x) {
  case 0: {
    let foo;
    break;
  }  
  case 1: {
    let foo;
    break;
  }
}
  • 與詞法作用域結合的暫存死區

function test(){
   var foo = 33;
   if (true) {
      let foo = (foo + 55); // ReferenceError
   }
}
test();

在if語句中使用了let宣告瞭foo, 因此在(foo+55)中引用的是if塊級作用域中的foo, 而不是test函式中的foo; 但是由於if中的foo還沒有宣告完。

在這一行中,if塊的“foo”已經在詞法環境中建立,但尚未達到(並終止)其初始化(這是語句本身的一部分)

因此它仍處於暫存死區

變數生命週期

全域性變數的生命週期直至瀏覽器解除安裝頁面才會結束。
區域性變數只在函式的執行過程中存在,而在這個過程中會為區域性變數在棧或堆上分配相應的空間,以儲存它們的值,然後再函式中使用這些變數,直至函式結束

執行環境執行棧

執行環境執行棧(也稱執行上下文–execution context)。

當JavaScript直譯器初始化執行程式碼時,它首先預設進入全域性執行環境,從此刻開始,函式的每次呼叫都會建立一個新的執行環境,每一個執行環境都會建立一個新的環境物件壓入棧中。

當執行流進入一個函式時,函式的環境物件就會被壓入一個環境棧中(execution stack)。在函式執行完後,棧將其環境彈出,把控制權返回給之前的執行環境。

嚴格模式

除了正常執行模式,ECMAscript 5 新增了第二種執行模式:"嚴格模式"(strict mode)。顧名思義,這種模式使得Javascript在更嚴格的條件下執行。其主要有下面這幾個目的:

  • 消除Javascript語法的一些不合理、不嚴謹之處,減少一些怪異行為;
  • 消除程式碼執行的一些不安全之處,保證程式碼執行的安全;
  • 提高編譯器效率,增加執行速度;
  • 為未來新版本的Javascript做好鋪墊。

"嚴格模式"體現了Javascript更合理、更安全、更嚴謹的發展方向,包括IE 10在內的主流瀏覽器,都已經支援它。

同樣的程式碼,在非嚴格模式下可以執行,但是嚴格模式下可能將不能執行。

如何進入嚴格模式

<script>
     "use strict"
    console.log("已經進入嚴格模式");
</script> 

嚴格模式行為變更

  • 全域性變數宣告時 必須加var
<script>

    "use strict"
               
    a = 10;//報錯 因為 a沒有被var 宣告

     //Uncaught ReferenceError: a is not defined; 引用錯誤: a 沒有被宣告
                         
</script>
  • this 無法指向全域性物件
<script>
        "use strict"
         // console.log("已經進入嚴格模式");
        function a(){
            this.b = 10; //報錯 , 因為this指向了window物件;

            //Uncaught TypeError: Cannot set property 'b' of undefined; 型別錯誤 : 不能給undefined設定屬性b;

        }
        window.a();
</script>
  • 函式內重名屬性
<script>
    "use strict";
    function a(b,b,c){ //報錯
            console.log(b,b,c); // 正常模式下 2,2,3

            // Uncaught SyntaxError: Duplicate parameter name not allowed in this context   ;語法錯誤:在此上下文中不允許重複的引數名稱

    }
    a(1,2,3)
</script>
  • arguments物件

arguments物件不允許被動態改變;

<script>
    function fn1(a) {
        a = 2;
        return [a, arguments[0]];
    }
    console.log(fn1(1)); // 正常模式為[2,2]
    function fn2(a) {
        "use strict";
        a = 2;
        return [a, arguments[0]];
    }
    console.log(fn2(1)); // 嚴格模式為[2,1]
</script>

arguments物件不允許被自呼叫

<script>
        "use strict";
        var f = function() { return arguments.callee; };
        f(); // 報錯

        //Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

        //型別錯誤:“caller ”,“arguments.callee ”,不能在嚴格模式中使用;

        //caller返回撥用當前函式的函式的引用  (正在執行的函式的屬性)
       // callee返回正在執行的函式本身的引用 (arguments的屬性)

</script>

this

this是js的關鍵字,他是根據執行上下文(執行環境)動態指向當前呼叫的物件;
誰呼叫,就指誰;

call()、apply()、bind() 可以改變this的指向

js的物件

js語言中一切皆為物件,比如數字、字串、陣列、Math、Object、函式

js中物件的本質:屬性和方法的集合(無序,所以物件沒有length屬性)。

用官方一點的語言來解釋物件:

什麼是物件,其實就是一種型別,即引用型別。而物件的值就是引用型別的例項。在 ECMAScript 中引用型別是一種資料結構,用於將資料和功能組織在一起。它也常被稱做為類,但 ECMAScript6以前卻沒有這種東西。雖然 ECMAScript 是一門物件導向的語言,卻不具備傳統面嚮物件語言所支援的類等基本結構。

特點

封裝、繼承、多型

  • 封裝:

1、寫物件

2、用物件

把一些相關的物件和屬性放到一起,用一個變數抽象出來,那麼這就完成了這個物件的封裝

  • 繼承:

子承父業

子物件可以使用父物件的一些屬性和方法

  • 多型:

過載,重寫

過載就是根據不同的引數型別,引數個數實現不同的功能

重寫就是父類的方法不好用,我自己重新定義一個方法名相同的不同方法

定義方式

  • 字面量
var obj = {
    鍵值對
    key:value
}
  • new運算子
var obj = new Object()

  • 建構函式
function Person(name,age,job){
    this.name= name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    }
}

var person1 = new Person('monkey',30,'web');
var person2 = new Person('zhu',25,'h5');

  • 工廠模式

工廠模式抽象了建立具體物件的過程。由於在ECMAScript中無法建立類,開發人員就發明了一種函式,用函式來封裝以特定介面建立物件的細節,如下面的例子:

function createPerson(name,age,job){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){
        alert(this.name);
    }
    return o;
}

var person1 = createPerson('monkey',30,'web');
var person2 = createPerson('zhu',25,'h5');

  • ES6 class語法糖
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

常用方法

內建物件

String

字串的兩種建立方式(常量和建構函式),常用api有:

  • charAt() 返回在指定位置的字元
  • indexOf() 檢索字串,返回下標
  • lastIndexOf() 從後向前搜尋字串。
  • charCodeAt() 返回在指定的位置的字元的 Unicode 編碼。
  • fromCharCode() 從字元編碼建立一個字串。
  • concat() 連線字串。
  • match() 找到一個或多個(正規表示式的)匹配。
  • replace() 替換與正規表示式匹配的子串。
  • search() 檢索與正規表示式相匹配的值。
  • slice() 提取字串的片斷,並在新的字串中返回被提取的部分。
  • split() 把字串分割為字串陣列。
  • substr() 從起始索引號提取字串中指定數目的字元。
  • substring() 提取字串中兩個指定的索引號之間的字元。
  • toLowerCase() 把字串轉換為小寫。
  • toUpperCase() 把字串轉換為大寫。
  • trim() 去掉字串前後空格(ES5)
  • startsWith() 字串是否以某個字元開頭(ES6)
  • endsWith() 字串是否以某個字元結尾(ES6)
  • includes() 字串是否包含某個字元(ES6)
  • repeat() 重複某個字串幾次(ES6)
Math

Math的常用屬性有:

  • Math.E 返回算術常量 e,即自然對數的底數(約等於2.718)。
  • Math.LN2 返回 2 的自然對數(約等於0.693)。
  • Math.LN10 返回 10 的自然對數(約等於2.302)。
  • Math.LOG2E 返回以 2 為底的 e 的對數(約等於 1.414)。
  • Math.LOG10E 返回以 10 為底的 e 的對數(約等於0.434)。
  • Math.PI 返回圓周率(約等於3.14159)。
  • Math.SQRT1_2 返回返回 2 的平方根的倒數(約等於 0.707)。
  • SQRT1_2.SQRT2 返回 2 的平方根(約等於 1.414)。

常用api有:

  • abs(x) 返回數的絕對值。
  • sin(x) 返回數的正弦。
  • cos(x) 返回數的餘弦。
  • tan(x) 返回角的正切。
  • ceil(x) 對數進行上舍入。
  • floor(x) 對數進行下舍入。
  • round(x) 把數四捨五入為最接近的整數。
  • max(x,y) 返回 x 和 y 中的最高值。
  • min(x,y) 返回 x 和 y 中的最低值。
  • pow(x,y) 返回 x 的 y 次冪。
  • sqrt(x) 返回數的算術平方根。
  • random() 返回 0 ~ 1 之間的隨機小數,包含0不包含1
Date

常用api有:

  • getDate() 從 Date 物件返回一個月中的某一天 (1 ~ 31)。
  • getDay() 從 Date 物件返回一週中的某一天 (0 ~ 6)。
  • getMonth() 從 Date 物件返回月份 (0 ~ 11)。
  • getFullYear() 從 Date 物件以四位數字返回年份。
  • getYear() 請使用 getFullYear() 方法代替。
  • getHours() 返回 Date 物件的小時 (0 ~ 23)。
  • getMinutes() 返回 Date 物件的分鐘 (0 ~ 59)。
  • getSeconds() 返回 Date 物件的秒數 (0 ~ 59)。
  • getMilliseconds() 返回 Date 物件的毫秒(0 ~ 999)。
  • getTime() 返回 1970 年 1 月 1 日至今的毫秒數。
  • getTimezoneOffset() 返回本地時間與格林威治標準時間 (GMT) 的分鐘差。
  • parse() 返回1970年1月1日午夜到指定日期(字串)的毫秒數。
  • setDate() 設定 Date 物件中月的某一天 (1 ~ 31)。
  • setMonth() 設定 Date 物件中月份 (0 ~ 11)。
  • setFullYear() 設定 Date 物件中的年份(四位數字)。
  • setYear() 請使用 setFullYear() 方法代替。
  • setHours() 設定 Date 物件中的小時 (0 ~ 23)。
  • setMinutes() 設定 Date 物件中的分鐘 (0 ~ 59)。
  • setSeconds() 設定 Date 物件中的秒鐘 (0 ~ 59)。
  • setMilliseconds() 設定 Date 物件中的毫秒 (0 ~ 999)。
  • setTime() 以毫秒設定 Date 物件。

js陣列

上學時,班上的同學會進行分組,如下圖,一豎排是一組

一列就是一個陣列,一個陣列裡面有很多個元素(多個同學),因為js是弱型別語言,所以陣列也是弱型別,同一個陣列變數裡可以有各種不同型別的元素。

定義方式

  • 字面量
var arr = [];
  • new運算子
var arr = new Array();
//建構函式的方式
var arr = new Array(10);//一個引數指陣列長度為10
var arr = new Array(10,20,30);//多個引數指定義陣列元素

常用方法

  • concat():連線兩個或更多的陣列,並返回結果。
  • join():把陣列的所有元素放入一個字串。元素通過指定的分隔符進行分隔。
  • pop():刪除並返回陣列的最後一個元素
  • push():向陣列的末尾新增一個或更多元素,並返回新的長度。
  • shift():刪除並返回陣列的第一個元素
  • unshift():向陣列的開頭新增一個或更多元素,並返回新的長度
  • reverse():顛倒陣列中元素的順序
  • slice():從某個已有的陣列返回選定的元素
  • sort():對陣列的元素進行排序
  • splice():刪除元素,並向陣列新增新元素
  • toString():把陣列轉換為字串,並返回結果

ES5新增陣列方法

  • indexOf()

判斷一個元素是否存在於陣列中,若不存在返回-1,若存在就返回它第一次出現的位置

  • lastIndexOf()

lastIndexOf() 方法可返回一個指定的字串值最後出現的位置,在一個字串中的指定位置從後向前搜尋

  • forEach()

迴圈遍歷陣列中的每一個元素,這個函式包含三個形參,分別為:item, index, array, 用不到時可以不寫

  • map()

返回一個新陣列,新陣列是原陣列的對映,不改變原陣列,新陣列的元素值是每次函式return的返回值,若不寫return,接收的新陣列的元素值將全為空

  • filter()

過濾元素,返回一個新陣列,新的陣列由每次函式返回值為true對應的元素組成;
原陣列不受影響

  • some()

return返回的值只要有一項為true,最終的返回值就為true,不會繼續遍歷後邊的元素,若沒有一項滿足返回值為true的,就返回false,原陣列不受影響;

  • every()

對陣列的每一項執行給定的函式,假如該函式每一項都返回true,最後結果才為true;只要有一項返回值為false,最後結果就是false。且後邊的元素都不會再繼續執行函式;原陣列不受影響

  • reduce()

reduce() 方法接收一個函式作為累加器,陣列中的每個值(從左到右)開始縮減,最終計算為一個值。reduce() 可以作為一個高階函式,用於函式的 compose

reduce() 對於空陣列是不會執行回撥函式的

  • reduceRight()

reduceRight() 方法的功能和 reduce() 功能是一樣的,不同的是 reduceRight() 從陣列的末尾向前將陣列中的陣列項做累加。

reduceRight() 對於空陣列是不會執行回撥函式的

ES6新增陣列方法

  • find()

傳入一個回撥函式,找到陣列中符合當前搜尋規則的第一個元素,返回它,並且終止搜尋

  • findIndex()

傳入一個回撥函式,找到陣列中符合當前搜尋規則的第一個元素,返回它的下標,並且終止搜尋

  • fill()

用新元素替換掉陣列內的元素,可以指定替換下標範圍。

格式:arr.fill(value, start, end)

  • copyWithin()

選擇陣列的某個下標,從該位置開始複製陣列元素,預設從0開始複製。也可以指定要複製的元素範圍。

格式:arr.copyWithin(被替換的起始位置,選取替換值的起始位置,選取替換值的結束位置)

  • Array.from()

將類似陣列的物件(array-like object)和可遍歷(iterable)的物件轉為真正的陣列

  • Array.of()

用於將一組值,轉換為陣列

  • entries()

返回迭代器:返回鍵值對

  • values()

返回鍵值對的value

  • keys()()

返回鍵值對的key

  • includes()

判斷陣列中是否存在該元素,引數:查詢的值、起始位置,可以替換 ES5 時代的 indexOf 判斷方式。indexOf 判斷元素是否為 NaN,會判斷錯誤。

ES6新增

let/const

塊級作用域:一種普遍存在於各個語言中的作用域範圍;

擴充套件運算子 ...

三個點號,功能是把陣列或類陣列物件展開成一系列用逗號隔開的值

var foo = function(a, b, c) {
console.log(a);
console.log(b);
console.log(c);
}

var arr = [1, 2, 3];

//傳統寫法
foo(arr[0], arr[1], arr[2]);

//使用擴充套件運算子
foo(...arr);
//1
//2
//3

rest運算子

rest運算子也是三個點號,不過其功能與擴充套件運算子恰好相反,把逗號隔開的值序列組合成一個陣列

//主要用於不定引數,所以ES6開始可以不再使用arguments物件
var bar = function(a, ...args) {
    console.log(a);
    console.log(args);
}

bar(1, 2, 3, 4);
//1
//[ 2, 3, 4 ]

模板字串

ES6中存在一種新的字串, 這種字串是 以 (波浪線上的那個字元 > 反引號)括起來表示的;
通常我們想要拼接一個帶有標籤的字串, 是用這樣的方式:

bianliang + " <strong>這是一個文字" + obj.name + "</strong> " + bianliang

但是有了ES6字串一切都變得非常簡單了;

`  ${bianliang} <strong>這是一個文字${obj.name}</strong>${bianliang} `

用 ${ } 擴住變數讓拼接變得非常容易;

=>箭頭函式

原來的寫法

var test = function(x){
    return x+2;
}

使用箭頭函式:

var test = x =>x+2;

var  函式名 = 引數 => 運算規則;

箭頭函式this指向的固定化,並不是因為箭頭函式內部有繫結this的機制,實際原因是箭頭函式根本沒有自己的this,導致內部的this就是外層程式碼塊的this。正是因為這個,所以箭頭函式也不能做建構函式。主要有兩個缺陷:

  • 箭頭函式是不能new的,它的設計初衷就跟建構函式不太一樣
  • 箭頭函式如果要返回一個JSON物件,必須用小括號包起來 var test = ()=>({id:3, val=20})

解構賦值

var [a,b,c] = [1,2,3];
var {a,b,c} = {
    a:1,
    b:2,
    c:3
}

var username = "zhangsan";
var age = 18;
var obj = {username,age};

Set和Map結構

想當初設計JS的時候,由於有SUN公司人員的參與 再加上當時如日中天的JAVA及其優秀的設計,才使得JS語法及記憶體設計跟JAVA會如此的接近。但JAVA很多優秀的內容,JS不知道為了什麼目的並沒有引入,例如Set和Map集合

Set集合

Set集合,本質上就是對陣列的一種包裝 例如:

let imgs = new Set();
imgs.add(1);
imgs.add(1);
imgs.add(5);
imgs.add("5");
imgs.add(new String("abc"));
imgs.add(new String("abc"));

// 列印的結果: 1  5  '5'  'abc'  'abc'

Set集合是預設去重複的,但前提是兩個新增的元素嚴格相等 所以5和"5"不相等,兩個new出來的字串不相等

關於遍歷的方法 由於Set集合本質上還是一個map,因此會有以下幾種奇怪的遍歷方法

var imgs = new Set(['a','b','c']); 

 //根據KEY遍歷
for(let item of imgs.keys()){
    console.log(item);
} //a //b //c
//根據VALUE遍歷
for(let item of imgs.values()){
    console.log(item);
} //a //b //c
//根據KEY-VALUE遍歷
for(let item of imgs.entries()){
    console.log(item);
 } //['a','a'] //['b','b'] //['c','c']
//普通for...of迴圈(for...of跟for-in的區別很明顯,就是直接取值,而不再取下標了)
for(let item of imgs){  
  console.log(item);
} //a //b //c

SET集合沒有提供下標方式的訪問,因此只能使用for來遍歷。

// 下面展示了一種極為精巧的陣列去重的方法

var newarr = [...new Set(array)];

Map集合

Map集合,即對映

let map = new Map();
 
map.set("S230", "張三");
map.set("S231", "李四");
map.set("S232", "王五");

獲取某一個元素 map.get("s232"); //王五

//迴圈遍歷,配合解構賦值

for(let [key,value] of map){    
    console.log(key,value);  
}

BOM

BOM(Browser Object Model 瀏覽器物件模型),結構如下圖所示:

window是全域性瀏覽器內建頂級物件,表示瀏覽器中開啟的視窗(沒有應用於window物件的公開標準,不過所有瀏覽器都支援該物件)。

在客戶端 JavaScript 中,Window 物件是全域性物件,所有的表示式都在當前的環境中計算。

也就是說,要引用當前視窗根本不需要特殊的語法,可以把那個視窗的屬性作為全域性變數來使用。

例如,可以只寫 document,而不必寫 window.document。

同樣,可以把當前視窗物件的方法當作函式來使用,如只寫 alert(),而不必寫 Window.alert()。

除了上面列出的屬性和方法,Window 物件還實現了核心 JavaScript 所定義的所有全域性屬性和方法。

全域性變數

全域性變數預設是掛在window下的,例如:

var  a = 123;

alert(window.a)//123

window下的子物件

location:

  • window.location.href 當前頁面的 URL,可以獲取,可以修改(頁面跳轉)。
  • window.location.hostname web 主機的域名
  • window.location.pathname 當前頁面的路徑和檔名
  • window.location.port web 主機的埠 (80 或 443)
  • window.location.protocol 所使用的 web 協議(http:// 或 https://)
  • window.location.search 請求引數(?後面的內容)
  • window.location.reload() 重新整理頁面的方法。

一般情況下給reload()傳遞一個true,讓他重新整理,並不使用快取。快取的東西一般為js檔案,css檔案等。用這個方法可以讓自己不能動的頁面動起來了。重新整理當前頁面。

window.navigator:

  • navigator.appName 返回獲取當前瀏覽器的名稱。
  • navigator.appVersion 返回 獲取當前瀏覽器的版本號。
  • navigator.platform 返回 當前計算機的作業系統。

以上屬性已經在逐漸被拋棄了。

一個新的屬性將替代這些屬性。

navigator.userAgent 返回瀏覽器資訊(可用此屬性判斷當前瀏覽器)

判斷當前瀏覽器型別的,示例:

function isBrowser() {
    var userAgent = navigator.userAgent;
    //微信內建瀏覽器
    if(userAgent.match(/MicroMessenger/i) == 'MicroMessenger') {
        return "MicroMessenger";
    }
    //QQ內建瀏覽器
    else if(userAgent.match(/QQ/i) == 'QQ') {
        return "QQ";
    }
    //Chrome
    else if(userAgent.match(/Chrome/i) == 'Chrome') {
        return "Chrome";
    }
    //Opera
    else if(userAgent.match(/Opera/i) == 'Opera') {
        return "Opera";
    }
    //Firefox
    else if(userAgent.match(/Firefox/i) == 'Firefox') {
        return "Firefox";
    }
    //Safari
    else if(userAgent.match(/Safari/i) == 'Safari') {
        return "Safari";
    }
    //IE
    else if(!!window.ActiveXObject || "ActiveXObject" in window) {
        return "IE";
    }
    else {
        return "未定義:"+userAgent;
    }
}

history:

  • history.go(1) 引數可寫任意整數,正數前進,負數後退
  • history.back() 後退
  • history.forward() 前進

screen: 螢幕

  • window.screen.width 返回當前螢幕寬度(解析度值)
  • window.screen.height 返回當前螢幕高度(解析度值)

window下的彈框方法:

  • alert() 彈出一個提示框
  • prompt() 彈出一個輸入框
  • confirm() 彈出一個確認框

DOM

DOM(Document Object Model 文件物件模型),DOM定義了表示和修改文件所需的物件、行為和屬性,以及這些物件之間的關係。

獲取DOM節點

  • document.getElementById(id名)
  • getElementsByTagName(標籤名)

得到的是一個集合(不止一個,是一堆)

  • getElementsByName( ) 通過Name值獲取元素,返回值是集合,通常用來獲取有name的input的值

不是所有的標籤都有name值,低版本的瀏覽器會有相容問題

  • getElementsByClassName(class名稱)

IE8以下不能用

  • document.querySelector () (ES5)>>>> 一旦匹配成功一個元素,就不往後匹配了
  • document.querySelectorAll () (ES5)>>>> 強大到超乎想象;匹配到所有滿足的元素, 支援IE8+

屬性獲取和操作

  • getAttribute( )獲取元素的屬性值
  • setAttribute( )設定元素的屬性
  • removeAttribute( )刪除屬性

小編更精心準備了前端知識體系,及JavaScript知識體系,需要的小夥伴關注小編公眾號【小猴子的web成長之路】,回覆知識體系獲取。

相關文章