首頁 > web前端 > js教程 > JavaScript 知識點整理

JavaScript 知識點整理

高洛峰
發布: 2017-02-08 09:58:16
原創
1066 人瀏覽過

JavaScript是按照ECMAScript標準設計和實現的,後文說的JavaScript語法其實是ES5的標準的實現。
先說說有哪些基礎文法?


最基礎文法有哪些?

基礎語法幾乎所有的語言差異不大,無非資料型別、運算子、控制語句、函數等,簡單列舉。


5種基本資料型別& 1種複雜的資料型別


JavaScript包含5種基本資料型,分別是undefined / null /  lean ,沒有其他的!
JavaScript包含1種複雜的資料類型,就是Object類型,Object類型是所有其他物件的基底類別。
注意:JavaScript不區分浮點數和整數,都是用number來表示。

前面提到的5種基本資料類型,以及這兒的1種複雜資料類型,這就是資料類型的全部了!


基本操作符


這個是常識,知道怎麼回事就好。
常用的操作符包括:算術操作符、關係操作符、布林操作符、賦值操作符等。


控制語句


這就是我們常說的if-else之類的控制語句。
常用的不多:if語句、switch語句、for語句、while語句、for-in語句。


函數


函數就是一小段邏輯的封裝,理論上邏輯越獨立越好。
JavaScript函數相對其他語言來說有很大不同。 JavaScript函數既可以當作參數,也可以當作傳回值。
此外JavaScript函數可以接受任意數量的參數,並且可以透過arguments物件來存取這些參數。

任何一門語言的基礎語法都是相通的,除開一些細節差異,大致就是上面這些了:資料型態、運算子、控制語句、函數、模組等等。

接下來介紹稍微複雜的一些概念。


變數、作用域、記憶體問題

變數


JavaScript變數分為兩種:基本型別和參考型別。其中基本型別就是前面提到的5種基本資料型別,引用型別就是前面提到的Object以及基於它的其他複雜資料型別。
✦ 基本型別:在記憶體中佔據實際大小的空間,賦值的時候,會在記憶體中建立一份新的副本。保存在棧記憶體中。
✦ 引用類型:指向物件的指標而不是物件本身,賦值的時候,只是創建了一個新的指標指向物件。保存在堆記憶體中。

JavaScript 知识点整理

變數記憶體分配

一句話就是,基本型別在記憶體中是實際的值;而引用型別在記憶體中就是指針,指向一個對象,多重引用型別可能同時指向同一個對象。

那麼,如何確定某個變數是哪一種資料型別呢?
確定一個變數是哪一種基本型別用typeof操作符。
確定一個變數是哪一種參考型別用instanceof操作符。
這個別忘了!


作用域


變數是在某個特定的作用域中聲明的,作用域決定了這些變數的生命週期,以及哪些程式碼可以存取其中的變數。
JavaScript作用域只包含全域作用域和函數作用域,不包含區塊層級作用域!

作用域是可以嵌套的,從而形成作用域鏈。由於作用域鏈的存在,可以讓變數的查找向上追溯,即子函數可以存取父函數的作用域=>祖先函數的作用域=>直到全域作用域,這種函數我們也稱為閉包,後文會介紹。

var color = "blue";function changeColor() {    var anotherColor = "red";    function swapColors() {        varotherColor = swapColors() {        varotherColor = swapColors() an    color = tempColor;        // 這裡可以存取color、anotherColor、 tempColor
   }    // 這裡可以存取color、anotherColor,但不能存取tempColor
   swapColors();
}// 這裡只能存取color、changeColor();


}// 這裡只能存取color、changeColor();

點的變數以及嵌套的作用域可向上追溯。 🎜

JavaScript 知识点整理

作用域鏈

作用域的概念看著簡單,實際使用會有不少問題,遇到問題要細心分析。


記憶體問題


JavaScript引擎具有自動垃圾回收機制,不需要太關注記憶體分配和垃圾回收問題。這兒就不展開了!


引用型


前面提過,Object是唯一的複雜資料型別,引用型別都是從Object型別上繼承而來。
✦ Array:陣列類型
✦ Date:日期類型
✦ RegExp:正規表示式類型,這個多學有好處!
✦ 等等...
那問題來了,我們用的最多的函數是什麼資料型別呢?答案是Function類型!
誒,好像發現了點什麼東西?由於Function是引用型,而JavaScript可以往引用型別上加屬性和方法。那麼,函數也可以!這也是JavaScript函數強大且複雜的地方。也就是說:函數也可以擁有自訂方法和屬性!

此外,JavaScript對前面提到的5種基本類型的其中3種也做了引用類型封裝,分別是Boolean、Number、String,但其實使用不多,了解就行。

對了,在所有程式碼執行之前,作用域就內建了兩個對象,分別是Global和Math,其中瀏覽器的Global就是window啦!

到此為止,JavaScript中基礎的概念都差不多介紹了,其中函數和作用域相對來說複雜一些,其他的都比較淺顯。
接下來,我會介紹介紹JavaScript中一些稍微複雜一點的概念:物件導向。


物件導向程式設計


JavaScript本身並沒有類別和介面的概念了,物件導向都是基於原型實現的。
為了簡單,我們只分析物件導向的兩個問題:
✦ 如何定義一個類別?
✦ 如何實現類別的繼承


定義一個類別

不扯其他的,直接告訴你。我們使用建構函式+原型的方式來定義一個類別。

使用建構函式建立自訂類型,然後使用new運算元來建立類別的實例,但是建構函式上的方法和屬性在每個範例上都存在,不能共享,於是我們引入原型來實作方法和屬性的共享。

JavaScript 知识点整理

原型

最後,我們將需要共享的方法和屬性定義在原型上,把專屬於實例的方法和屬性放到建構函式中。到這兒,我們就透過建構函數+原型的方式定義了一個類別。

// 建構子function Person(name, age, job) {    this.name = name;    this.age = age;    this.job = job;    this.friends = ["Shelby", "Cohelby", "Cohell; // 原型Person.prototype = {
   constructor: Person,
   sayName: function() {        return this.name;
   }
}// 實例化 10; ;var person2 = new Person("Greg", 27, "Doctor");

person1.friends.push("Van");
alert(person1.friends);          Count Count Count. (person2.friends);                     //輸出"Shelby,Count"alert(person1.friends === person2.friends);  


實作繼承


前文講瞭如何定義一個類,那麼我們定義一個父類,一個子類。

如何讓子類別繼承父類別呢?不扯別的,直接告訴你。 JavaScript透過原型鏈來實現繼承!

如何建構原型鏈呢?將父類別實例賦值給子類別建構子的原型即可。好繞,但是千萬得記住了!

JavaScript 知识点整理


原型鏈繼承

建構原型鏈之後,子類別就可以存取父類別的所有屬性和方法!

// 父類function SuperType() {    this.property = true;

}

SuperType.prototype.getSuperValue = function() {    return this.property;
}; = false;
}//子類別繼承父類別SubType.prototype = new SuperType();//給子類別新增方法SubType.prototype.getSubValue = function() {    return this.subproperty;
};//重寫入父類別的方法SubType.prototype.getSuperValue = function() {    return false;
};// 實例化var instance = new SubType();console.log(instance.getSuperValue()); //輸出false


物件導向的知識可以用一本書來寫,這兒只是簡單的介紹下最基礎最常用的概念。


函數表達式


JavaScript中有兩種定義函數的方式:函數宣告和函數表達式。
使用函數表達式無須對函數命名,從而實現動態編程,也即匿名函數。有了匿名函數,JavaScript函數有了更強大的用途。

遞歸


遞歸是一種很常見的演算法,經典例子就是階乘。不扯其他的,直接說遞歸的最佳實踐,上碼:

// 最佳實踐,函數表達式var factorial = (function f(num) {    if (num } else {        return num * f(num - 1);
   }
});// 缺點:// factorial存在被修改的可能// 導致return num * factorial(num - 1) 錯誤被修改的可能// 導致return num * factorial(num - 1) 錯誤符號if (num    } else {        return num * factorial(num - 1);
   }
}/對使用缺點:不使用/ if (num    } else {        return num * arguments.callee(num - 1);
}  return num * arguments.callee(num - 1);
}  回函數表達式的方式吧,這才是最佳實踐。

囉嗦一句,好多人覺得遞歸難寫,其實你將其分為兩個步驟就會清晰很多了。

✦ 邊界條件,通常是if-else。

✦ 遞歸呼叫。

按這個模式,找幾個經典的遞歸練練手,就熟悉了。

閉包

很多人常常覺得閉包很複雜,很容易掉到坑里,其實不然。

那麼閉包是什麼呢?如果一個函數可以存取另一個函數作用域中的變量,那麼前者就是閉包。由於JavaScript函數可以回傳函數,自然,建立閉包的常用方式就是在一個函數內部建立另一個函數!

這並沒有什麼神奇的,在父函數中定義子函數就可以建立閉包,而子函數可以存取父函數的作用域。

我們通常是因為被閉包坑了,才會被閉包嚇到,尤其是面試題裡一堆閉包。


閉包的定義前面提了,如何創建閉包也說了,那麼我們說說閉包的缺陷以及如何解決?

/* 我們透過subFuncs傳回函數數組,然後分別呼叫執行 */// 回傳函數的陣列subFuncs,而這些函數對superFunc的變數有引用// 這就是一個典型的閉包// 那麼有什麼問題呢? // 當我們回頭執行subFuncs中的函數的時候,我們得到的i其實一直都是10,為什麼? // 因為當我們回傳subFuncs之後,superFunc中的i=10// 所以當執行subFuncs中的函數的時候,輸出i都為10。 // // 以上,就是閉包最大的坑,一句話理解就是:// 子函數對父函數變數的引用,是父函數運行結束之後的變數的狀態function superFunc() {    var subFuncs = new Array( );    for (var i = 0; i        subFuncs[i] = function() {            return i;🠎
}// 那麼,如何解決上訴的閉包坑呢? // 其實原理很簡單,既然閉包坑的本質是:子函數對父函數變數的引用,是父函數運行結束之後的變數的狀態// 那麼我們解決這個問題的方式就是:子函數對父函數變數的引用,使用運行時的狀態// 如何做呢? // 在函數表達式的基礎上,加上自執行即可。 function superFunc() {    var subFuncs = new Array();    for (var i = 0; i        subFuncs[i] = function(num) { turn function(num) {          return num;
           };
}(i);
   }    return subFuncs;
}


綜上,閉包本身不是什麼複雜的機制,就是子函數可以存取父函數的作用域。

而由於JavaScript函數的特殊性,我們可以返回函數,如果我們將作為閉包的函數返回,那麼該函數引用的父函數變數是父函數運行結束之後的狀態,而不是運行時的狀態,這便是閉包最大的坑。而為了解決這個坑,我們常用的方式就是讓函數表達式自執行。

此外,由於閉包引用了祖先函數的作用域,所以濫用閉包會有記憶體問題。

好像把閉包說得一無是處,那麼閉包有什麼用處呢?

主要是封裝吧...


封裝


閉包可以封裝私有變數或封裝區塊層級作用域。

➙ 封裝區塊級作用域

JavaScript並沒有區塊級作用域的概念,只有全域作用域和函數作用域,那麼如果想要建立區塊級作用域的話,我們可以透過閉包來模擬。
建立並立即呼叫一個函數,就可以封裝一個區塊級作用域。函數可以立即執行其中的程式碼,內部變數執行結束就會立即被銷毀。

function outputNumbers(count) {    // 在函數作用域下,利用閉包封裝區塊級作用域

   ///// 的話,i在外部不可用,便有了類似區塊級作用域

   (function()) 這樣為 (var i = 0; i            alert(i);
       }
   })();

利用閉包封裝區塊層級作用域// 這樣的話,程式碼區塊不會對全域作用域造成污染(function() {    var now = new Date();    if (now.getMonth() == 0 && now.getDate () == 1) {
       alert("Happy new year!");
   }
})();// 是的,封裝區塊層級作用域的核心就是這個:函數表達式+ 自執行! (function() {    //這裡是區塊級作用域})();


➙ 封裝私有變數

JavaScript也沒有私有變數的概念,我們也可以使用閉包來實現公有方法,透過隱藏變數暴露方法的方式來實作封裝私有變數。


(function() {    //私有變數

   var privateVariable = 10;    function privateFunction() {     function() {};    //公有/特權方法

MyObject.prototype.publicMethod = function() {
       privateVariable++;        return privateFunction();
   };
})();

這差不多就是JavaScript的一些基礎語法和稍微高級一些的用法,其實所謂的高級,都是JavaScript「不太成熟」的表現,尤其是面向對象,出於工程化的需要但是JavaScript本身並不完美支持。還好ES6最新標準解決了很多問題,結合Babel用起來也不用太考慮相容性問題,如果你是新手的話,建議你直接去擼ES6+Babel吧。

✦ JavaScript的基礎主要包括:5中基本資料型態、1種複雜的資料型態、運算子、控制語句、函數等。
✦ 了解基本的語法後,你還需要學習學習JavaScript的變數、作用域、作用域鏈。
✦ 常見的引用型別可以邊查邊用。身為過來人,建議多學正則,對你的程式碼功底會有較大的提升。
✦ 物件導向程式設計的部分外面有很多種方式,你只需要記住使用建構子+原型去定義一個類,使用原型鏈去實現繼承即可。更多的擴展,去翻書吧。
✦ 函數表達式引出了幾個比較好玩的東西:遞歸、閉包、封裝。記住遞歸的最佳實踐、閉包的定義及缺陷、閉包的適用場景。

JavaScript作為一門動態語言,和其他語言有較大的差異,這也造成很多人學習JavaScript時會覺得難學。但你現在看看前文,雖然是一個簡略的總結,但JavaScript主要的內容就這些了,所以不要被自己嚇到了。
再補一句,如果你是新手的話,建議你直接去擼ES6+Babel吧。

更多JavaScript 知識點整理相關文章請關注PHP中文網!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板