提到 ECMAScript,可能不少 Web 開發人員會以爲比較陌生。可是提到 JavaScript,你們應該都比較熟悉。實際上,ECMAScript 是標準化組織 ECMA發佈的腳本語言規範。如今你們常見的 JavaScript、微軟的 JScript 以及 Adobe 的 ActionScript 等語言都是遵循這個規範的,屬於 ECMAScript 語言的變體。每一個 ECMAScript 規範的變體語言均可能增長本身額外的功能特性。理解 ECMAScript 規範自己,就能夠對不少 JavaScript 語言中的複雜特性有比較深刻的瞭解。程序員
初次聽到ES五、ES6,我有點兒懵了。好吧!我認可我不是一個合格的程序員。因此,我開始從新認識ES五、ES6。web
1、什麼是ES? json
ES全稱爲:ECMAScript,是一種由Ecma國際(前身爲歐洲計算機制造商協會,英文名稱是European Computer Manufacturers Association)經過ECMA-262標準化的腳本程序設計語言,至今爲止有六個版本。這種語言在萬維網上應用普遍,它每每被稱爲JavaScript或JScript,但實際上後二者是ECMA-262標準的實現和擴展。後端
2、ECMAScript關鍵字的完整列表:數組
Undefined
、Null
、Boolean
、Number
、String
和 Object
。在此,我就不對每一個類型進行贅敘了。
命名屬性有本身的特性(attribute)來定義該屬性自己的行爲。對於命名數據屬性來講,特性 [[Value]]
表示該屬性的值,能夠是任何 ECMAScript 語言中定義的類型的值;[[Writable]]
表示該屬性的值是否爲只讀的;[[Enumerable]]
表示該屬性是否能夠被枚舉。能夠被枚舉的屬性能夠經過 for-in
循環來獲取到;[[Configurable]]
表示該屬性是否能夠被配置。若是 [[Configurable]]
的值爲 true
,則該屬性能夠被刪除、能夠被轉換爲訪問器屬性、還能夠改變除了 [[Value]]
以外的其餘特性的值。對於命名訪問器屬性來講,這類屬性沒有[[Value]]
和 [[Writable]]
特性,取而代之的是進行屬性值獲取和設置的 [[Get]]
和 [[Set]]
特性。若是 [[Get]]
和 [[Set]]
特性的值不是 undefined
,那麼就必須是一個函數。屬性的獲取和設置是經過調用這兩個函數來完成的。命名訪問器屬性一樣有[[Enumerable]]
和 [[Configurable]]
特性,其含義與命名數據屬性的對應特性的含義相同。命名屬性能夠在 ECMAScript 代碼中進行處理。瀏覽器
內部屬性的做用是定義 ECMAScript 中的對象在不一樣狀況下的行爲。不一樣類型的對象所包含的內部屬性也不盡相同。每一個對象都有內部屬性[[Prototype]]
,用來引用另一個對象。被引用的對象的 [[Prototype]]
屬性又能夠引用另一個對象。對象之間經過這種引用關係組成一個鏈條,稱爲原型鏈條(prototype chain)。ECMAScript 經過原型鏈條的方式來實現屬性的繼承。當嘗試獲取一個對象中的命名數據屬性時,若是在當前對象中沒有相應名稱的屬性,會沿着原型鏈條往上查找,直到找到該屬性或到達原型鏈條的末尾;當設置命名數據屬性時,若是當前對象中不存在相應名稱的屬性,會在當前對象中建立新的屬性。命名訪問器屬性則始終是繼承的。當設置一個命名訪問器屬性的值時,所設置的是原型鏈條上定義該屬性的對象上的值。服務器
內部屬性 [[Class]]
用來聲明對象的類別,其做用相似於 Java 語言中對象的類名。經過 Object.prototype.toString
函數能夠獲取到[[Class]]
屬性的值。當須要判斷一個對象是否爲數組時,可使用代碼 Object.prototype.toString.apply(obj) === '[object Array]'
。app
var obj = {}; Object.defineProperty(obj, 'val', {}); // 建立一個新屬性,特性爲默認值 obj.val = 1; Object.defineProperty(obj, 'CONSTANT', {value : 32, writable : false}); // 建立一個只讀屬性 obj.CONSTANT = 16; // 對屬性的修改是無效的,可是不會拋出錯誤 Object.defineProperty(obj, "newVal", {enumerable: true}); for (var key in obj) { console.log(key); // 能夠枚舉出 newVal } var initValue = 0; Object.defineProperty(obj, "initValue", { get : function() { return initValue; }, set : function(val) { if (val > 0) { initValue = val; } } });
var obj = {val : 1}; obj.newVal = "Hello"; Object.seal(obj); Object.defineProperty(obj, 'anotherVal', {}); // 拋出 TypeError 錯誤
數組
數組是 ECMAScript 中很是重要的一個內置對象。在 ECMAScript 代碼中能夠看到大量對數組的使用。Array
對象用來表示數組。在 ECMAScript 規範第三版中並無爲 Array
對象提供比較多的實用函數來對數組進行操做。不少 JavaScript 框架對 ECMAScript 規範中的Array
對象進行加強。ECMAScript 規範第五版中對 Array
對象進行了加強,所以不少功能能夠直接依靠運行環境的實現。框架
Array
對象自己是一個構造函數,能夠用來建立新的數組實例。當 Array
對象自己做爲一個函數來使用時,其做用至關於做爲構造函數來使用。所以「Array(1,2,3)
」的結果與「new Array(1,2,3)
」是相同的。新建立的 Array
對象實例的內部屬性 [[Prototype]]
的值是內置的Array
原型對象,即 Array.prototype
。經過 Array.isArray
函數能夠判斷一個對象是否爲數組。dom
var array = [1, 2, 3, 4, 5]; array.indexOf(3); // 值爲 2 array.lastIndexOf(4); // 值爲 3 array.every(function(value, index, arr) { return value % 2 === 0; }); // 值爲 false array.some(function(value, index, arr) { return value % 2 === 0; }); // 值爲 true array.forEach(function(value, index, arr) { console.log(value); }); array.map(function(value, index, arr) { return value * 2; }); // 值爲 [2, 4, 6, 8, 10] array.filter(function(value, index, arr) { return value % 2 === 0; }); // 值爲 [2, 4] array.reduce(function(preValue, value, index, arr) { return preValue + value; }); // 值爲 15 array.reduceRight(function(preValue, value, index, arr) { return preValue * value; }); // 值爲 120
實際上,Array.prototype
中的函數並不限制只能對數組對象來使用。這些函數自己是通用的。比較典型的是在函數中對 arguments
對象的處理。arguments
對象自己不是數組類型的,可是同樣可使用 Array.prototype
的函數來進行處理。
在 ECMAScript 代碼中,常常會須要與 JSON 格式的數據進行交換。JSON 也一般被用來做爲客戶端與服務器端之間的數據傳輸格式。這主要是由於在 ECMAScript 代碼中處理 JSON 格式很是天然。JSON 格式數據通過解析以後,能夠直接當成 ECMAScript 中的對象來使用。在使用 JSON 格式時的一個重要問題是如何在 ECMAScript 中的對象與文本形式之間進行互相轉換。從服務器端經過 HTTP 協議獲取的 JSON 文本須要通過解析以後,才能在 ECMAScript 代碼中來使用;當須要向服務器端發送數據時,須要先把 ECMAScript 中的對象轉換成文本格式。在 ECMAScript 規範第三版中並無對 JSON 格式數據的轉換進行規範,大多數程序都依靠 JavaScript 框架來提供相關的支持。
var jsonStr = '{"a":1, "b":2, "c":3}'; JSON.parse(jsonStr); JSON.parse(jsonStr, function(key, value) { return typeof value === 'number' ? value * 2 : value; }); // 結果爲 {a:2, b:4, c:6} JSON.parse(jsonStr, function(key, value) { return typeof value === 'number' && value % 2 === 0 ? undefined : value; }); // 結果爲 {a:1, b:3}
var user = { name : 'Alex', password : 'password', email : 'alex@example.org' }; JSON.stringify(user); JSON.stringify(user, ['name']); // 輸出結果爲「{"name":"Alex"}」 JSON.stringify(user, function(key, value) { if (key === 'email') { return '******'; } if (key === 'password') { return undefined; } return value; }); // 輸出結果爲「{"name":"Alex","email":"******"}」 JSON.stringify(user, null, 4);
ECMAScript 代碼的執行由運行環境來完成。不一樣的運行環境可能採起不一樣的執行方式,但基本的流程是相同的。如瀏覽器在解析 HTML 頁面中遇到 <script>
元素時,會下載對應的代碼來運行,或直接執行內嵌的代碼。在代碼中經過 eval
函數也能夠指定一段須要執行的代碼。代碼的基本執行方式是從上到下,順序執行。在調用函數以後,代碼的執行會進入一個執行上下文之中。因爲在一個函數的執行過程當中會調用其餘的函數,執行過程當中的活動執行上下文會造成一個堆棧結構。在棧頂的是當前正在執行的代碼。當函數返回時,會退出當前的執行上下文,而回到以前的執行上下文中。若是代碼執行中出現異常,則可能從多個執行上下文中退出。
var name = "alex"; function outer() { var age = 30; function inner(salutation) { return "Age of " + salutation + name + " is " + age; } return inner("Mr."); } outer();
4、ES5中新增的Array方法
ES5中新增的很多東西,好比數組這塊,咱們可能就不須要去有板有眼地循環了。for
ES5中新增了寫數組方法,以下:
瀏覽器支持
對於讓人失望不少次的IE6-IE8瀏覽器,Array原型擴展能夠實現以上所有功能,例如forEach
方法:
// 對於古董瀏覽器,如IE6-IE8 if (typeof Array.prototype.forEach != "function") { Array.prototype.forEach = function () { /* 實現 */ }; }
下面,我就選取其中一個方法進行示範:
forEach
是Array新方法中最基本的一個,就是遍歷,循環。例以下面這個例子:forEach
[1, 2 ,3, 4].forEach(alert);
等同於下面這個傳統的for
循環:
var array = [1, 2, 3, 4]; for (var k = 0, length = array.length; k < length; k++) { alert(array[k]); }
Array在ES5新增的方法中,參數都是function
類型,默認有傳參,這些參數分別是?見下面:
[1, 2 ,3, 4].forEach(console.log); // 結果: // 1, 0, [1, 2, 3, 4] // 2, 1, [1, 2, 3, 4] // 3, 2, [1, 2, 3, 4] // 4, 3, [1, 2, 3, 4]
顯而易見,forEach
方法中的function
回調支持3個參數,第1個是遍歷的數組內容;第2個是對應的數組索引,第3個是數組自己。
所以,咱們有:
[].forEach(function(value, index, array) { // ... });
對比jQuery中的$.each
方法:
$.each([], function(index, value, array) { // ... });
會發現,第1個和第2個參數正好是相反的,你們要注意了,不要記錯了。後面相似的方法,例如$.map
也是如此。
如今,咱們就可使用forEach
賣弄一個稍顯完整的例子了,數組求和:
var sum = 0; [1, 2, 3, 4].forEach(function (item, index, array) { console.log(array[index] == item); // true sum += item; }); alert(sum); // 10
再下面,更進一步,forEach
除了接受一個必須的回調函數參數,還能夠接受一個可選的上下文參數(改變回調函數裏面的this
指向)(第2個參數)。
array.forEach(callback,[ thisObject])
例子更能說明一切:
var database = { users: ["張含韻", "江一燕", "李小璐"], sendEmail: function (user) { if (this.isValidUser(user)) { console.log("你好," + user); } else { console.log("抱歉,"+ user +",你不是本家人"); } }, isValidUser: function (user) { return /^張/.test(user); } }; // 給每一個人法郵件 database.users.forEach( // database.users中人遍歷 database.sendEmail, // 發送郵件 database // 使用database代替上面標紅的this ); // 結果: // 你好,張含韻 // 抱歉,江一燕,你不是本家人 // 抱歉,李小璐,你不是本家
若是這第2個可選參數不指定,則使用全局對象代替(在瀏覽器是爲window
),嚴格模式下甚至是undefined
.
另外,forEach不會遍歷純粹「佔着官位吃空餉」的元素的,例以下面這個例子:
var array = [1, 2, 3]; delete array[1]; // 移除 2 alert(array); // "1,,3" alert(array.length); // but the length is still 3 array.forEach(alert); // 彈出的僅僅是1和3
綜上所有規則,咱們就能夠對IE6-IE8進行仿真擴展了,以下代碼:
// 對於古董瀏覽器,如IE6-IE8 if (typeof Array.prototype.forEach != "function") { Array.prototype.forEach = function (fn, context) { for (var k = 0, length = this.length; k < length; k++) { if (typeof fn === "function" && Object.prototype.hasOwnProperty.call(this, k)) { fn.call(context, this[k], k, this); } } }; }
5、ES6新特性
ES6(ECMAScript 6)是即將到來的新版本JavaScript語言的標準。
若是你會C#或者Java,你確定知道lambda表達式,ES6中新增的箭頭操做符=>便有殊途同歸之妙。它簡化了函數的書寫。操做符左邊爲輸入的參數,而右邊則是進行的操做以及返回的值Inputs=>outputs。
咱們知道在JS中回調是常常的事,而通常回調又以匿名函數的形式出現,每次都須要寫一個function,甚是繁瑣。當引入箭頭操做符後能夠方便地寫回調了。請看下面的例子。
var array = [1, 2, 3]; //傳統寫法 array.forEach(function(v, i, a) { console.log(v); }); //ES6 array.forEach(v = > console.log(v));
ES6中添加了對類的支持,引入了class關鍵字(其實class在JavaScript中一直是保留字,目的就是考慮到可能在之後的新版本中會用到,如今終於派上用場了)。JS自己就是面向對象的,ES6中提供的類實際上只是JS原型模式的包裝。如今提供原生的class支持後,對象的建立,繼承更加直觀了,而且父類方法的調用,實例化,靜態方法和構造函數等概念都更加形象化。
下面代碼展現了類在ES6中的使用。
//類的定義 class Animal { //ES6中新型構造器 constructor(name) { this.name = name; } //實例方法 sayName() { console.log('My name is '+this.name); } } //類的繼承 class Programmer extends Animal { constructor(name) { //直接調用父類構造器進行初始化 super(name); } program() { console.log("I'm coding..."); } } //測試咱們的類 var animal=new Animal('dummy'), wayou=new Programmer('wayou'); animal.sayName();//輸出 ‘My name is dummy’ wayou.sayName();//輸出 ‘My name is wayou’ wayou.program();//輸出 ‘I'm coding...’
對象字面量被加強了,寫法更加簡潔與靈活,同時在定義對象的時候可以作的事情更多了。具體表如今:
這樣一來,對象字面量與前面提到的類概念更加吻合,在編寫面向對象的JavaScript時更加輕鬆方便了。
//經過對象字面量建立對象 var human = { breathe() { console.log('breathing...'); } }; var worker = { __proto__: human, //設置此對象的原型爲human,至關於繼承human company: 'freelancer', work() { console.log('working...'); } }; human.breathe();//輸出 ‘breathing...’ //調用繼承來的breathe方法 worker.breathe();//輸出 ‘breathing...’
字符串模板相對簡單易懂些。ES6中容許使用反引號 ` 來建立字符串,此種方法建立的字符串裏面能夠包含由美圓符號加花括號包裹的變量${vraible}。若是你使用過像C#等後端強類型語言的話,對此功能應該不會陌生。
//產生一個隨機數 var num=Math.random(); //將這個數字輸出到console console.log(`your num is ${num}`);
自動解析數組或對象中的值。好比若一個函數要返回多個值,常規的作法是返回一個對象,將每一個值作爲這個對象的屬性返回。但在ES6中,利用解構這一特性,能夠直接返回一個數組,而後數組中的值會自動被解析到對應接收該值的變量中。
var [x,y]=getVal(),//函數返回值的解構 [name,,age]=['wayou','male','secrect'];//數組解構 function getVal() { return [ 1, 2 ]; } console.log('x:'+x+', y:'+y);//輸出:x:1, y:2 console.log('name:'+name+', age:'+age);//輸出: name:wayou, age:secrect
如今能夠在定義函數的時候指定參數的默認值了,而不用像之前那樣經過邏輯或操做符來達到目的了。
function sayHello(name){ //傳統的指定默認參數的方式 var name=name||'dude'; console.log('Hello '+name); } //運用ES6的默認參數 function sayHello2(name='dude'){ console.log(`Hello ${name}`); } sayHello();//輸出:Hello dude sayHello('Wayou');//輸出:Hello Wayou sayHello2();//輸出:Hello dude sayHello2('Wayou');//輸出:Hello Wayou
不定參數是在函數中使用命名參數同時接收不定數量的未命名參數。這只是一種語法糖,在之前的JavaScript代碼中咱們能夠經過arguments變量來達到這一目的。不定參數的格式是三個句點後跟表明全部不定參數的變量名。好比下面這個例子中,…x表明了全部傳入add函數的參數。
//將全部參數相加的函數 function add(...x){ return x.reduce((m,n)=>m+n); } //傳遞任意個數的參數 console.log(add(1,2,3));//輸出:6 console.log(add(1,2,3,4,5));//輸出:15
拓展參數則是另外一種形式的語法糖,它容許傳遞數組或者類數組直接作爲函數的參數而不用經過apply。
var people=['Wayou','John','Sherlock']; //sayHello函數原本接收三個單獨的參數人妖,人二和人三 function sayHello(people1,people2,people3){ console.log(`Hello ${people1},${people2},${people3}`); } //可是咱們將一個數組以拓展參數的形式傳遞,它能很好地映射到每一個單獨的參數 sayHello(...people);//輸出:Hello Wayou,John,Sherlock //而在之前,若是須要傳遞數組當參數,咱們須要使用函數的apply方法 sayHello.apply(null,people);//輸出:Hello Wayou,John,Sherlock
能夠把let當作var,只是它定義的變量被限定在了特定範圍內才能使用,而離開這個範圍則無效。const則很直觀,用來定義常量,即沒法被更改值的變量。
for (let i=0;i<2;i++)console.log(i);//輸出: 0,1 console.log(i);//輸出:undefined,嚴格模式下會報錯
咱們都知道for in 循環用於遍歷數組,類數組或對象,ES6中新引入的for of循環功能類似,不一樣的是每次循環它提供的不是序號而是值。
var someArray = [ "a", "b", "c" ]; for (v of someArray) { console.log(v);//輸出 a,b,c }