ECMAScript 6(以下簡稱ES6)是JavaScript語言的下一代標準。 因為當前版本的ES6是在2015年發佈的,所以又稱ECMAScript 2015(簡稱ES2015)。雖然瀏覽器在不斷更新,但並不是所有用戶的電腦瀏覽器都支持ES6,所以在使用的過程中建議還是轉成es5,保證代碼的可 ...
ECMAScript 6(以下簡稱ES6)是JavaScript語言的下一代標準。
因為當前版本的ES6是在2015年發佈的,所以又稱ECMAScript 2015(簡稱ES2015)。雖然瀏覽器在不斷更新,但並不是所有用戶的電腦瀏覽器都支持ES6,所以在使用的過程中建議還是轉成es5,保證代碼的可執行性。至於轉換的方式大家可以用Babel或者Traceur轉碼器。1. let 和 const
在ES6以前,Javascript並沒有塊級作用域的概念,有的是全局作用域和函數作用域,而let的出現就是為了打破局面,let是塊級作用域。const是代表常量,必須在定義的時候初始化,不可改變。1 { 2 var a=5; 3 let b=10; 4 } 5 console.log(a); 6 console.log(b);
控制台就是這樣輸出:
也就是說,var聲明的變數由於不存在塊級作用域所以可以在全局環境中調用,而let聲明的變數由於存在塊級作用域所以不能在全局環境中調用。
再來看一個經典例子(閉包):
1 var a=[]; 2 //執行for迴圈 3 for(var i=0;i<10;i++){ 4 a[i]=function(){ //因為這個是定義,並沒有調用方法,不會執行 5 console.log(i); 6 }; 7 } 8 //for迴圈之後,此時 i = 10;再次執行a[6]();因為 i 一直被引用,所以不會回收,進入到 a[i] 的方法裡面, 列印的是 i ,也就是10 9 a[6](); //輸出10
如果使用 let
1 var a=[]; 2 for(let i=0;i<10;i++){ 3 a[i]=function(){ 4 console.log(i); 5 }; 6 } 7 a[6](); //列印6
a[6]函數(閉包)這個執行環境中,它會首先尋找該執行環境中是否存在 i,沒有找到,因為 i 是塊級作用域,就沿著作用域鏈繼續向上到了其所在的代碼塊執行環境,找到了i=6,於是輸出了6,即a[6]();的結果為6。這時,閉包被調用,所以整個代碼塊中的變數i和函數a[6]()被銷毀。
const 是定義常量:const a = 14; 此後變數 a 的值無法更改覆蓋。
2. 字元串拼接
1 //傳統字元串拼接 2 var s1 = '生物膜系統組裝又拆分,變幻莫測;'; 3 var s2 = '你的好多細胞在分裂,'; 4 var str = '孩子們:請聽我說!'+s2+'有絲,減數,哪管白天和黑夜。'+ 5 '染色體,細胞核時隱時現,'+s1+'核糖體在mRNA上穿梭忙碌,'+'幾千種酶各司其職,將活化能狠狠打折。'; 6 console.log(str); 7 8 // 字元模板的寫法 9 var s1 = '染色體,細胞核時隱時現,'; 10 var s2 = '你的好多細胞在分裂,'; 11 var str = `孩子們:請聽我說!${s2}有絲,減數,哪管白天和黑夜。${s1}生物膜系統組裝又拆分,變幻莫測;核糖體在mRNA上穿梭忙碌,幾千種酶各司其職,將活化能狠狠打折。`; 12 console.log(str);
es6使用 ` ` 包裹字元串,即使斷開,也可以用這個符號包裹起來合併成一個字元串。
3. 解構賦值
1 // 以前我們給變數賦值,只能直接指定值 2 var a = 1; 3 var b = 2; 4 var c = 3; 5 console.log(a,b,c); // 1 2 3 6 7 // 現在用解構賦值的寫法就變得簡單了,只要模式匹配上了就行了,如下 8 // 註意數組是有順序的 9 var [a,b,c] = [11,22,33]; 10 console.log(a,b,c); // 11 22 33 11 12 var [b,a,c] = [11,22,33]; 13 console.log(a,b,c); // 22 11 33 14 15 // 當然解構賦值還有嵌套比較複雜的寫法,如下 16 let [foo,[[bar],[baz]]] = [111,[[222],[333]]]; 17 console.log(foo,bar,baz); // 111 222 333 18 19 let [head,...foot] = [1,2,3,4]; 20 console.log(head,foot); // 1 [2,3,4] 21 22 // 如果解構不成功,變數的值就等於undefined,如下 23 var [bar3,foo3] = [1000]; 24 console.log(bar3,foo3); // 1000 undefined 25 26 // 另一種情況是不完全解構,即等號左邊的模式,只匹配一部分的等號右邊的數組。這種情況下,解構依然可以成功 27 let [x,y] = [10000,20000,30000]; 28 console.log(x,y); // 10000 20000 29 30 // 預設值可以引用解構賦值的其他變數,但該變數必須已經聲明 31 let [a=1,b=a] = [2,3]; 32 console.log(a,b); // 2 3 33 34 // 對象的解構也可以指定預設值 35 var {x,y=5} = {x:1}; 36 console.log(x,y); // 1 5 37 38 //對象的解構賦值解構不僅可以用於數組,還可以用於對象(json) 39 //對象的解構與數組有一個重要的不同。數組的元素是按次序排列的,變數的取值由它的位置決定; 40 //而對象的屬性沒有次序,變數必須與屬性同名,才能取到正確的值 41 var {a,b} = {a:'apple',b:'banana'}; 42 console.log(a,b); // apple banana 43 var {b,a} = {a:'apple',b:'banana'}; 44 console.log(a,b); // apple banana 45 46 // 如果變數名與屬性名不一致,必須寫成下麵這樣 47 let obj = {first:'hello',last:'world'}; 48 // first ---> f,那麼此時f就是first,而不是undefined了,有點類似別名的概念 49 let {first:f,last} = obj; 50 console.log(f,last); // hello world 51 52 //1.也就是說,對象的解構賦值的內部機制,是先找到同名屬性,然後再賦給對應的變數。 真正被賦值的是後者,而不是前者 53 //2.v是匹配的模式,n才是變數。真正被賦值的是變數n,而不是模式v。 54 //註意,採用這種寫法時,變數的聲明和賦值是一體的 55 // v ---> n,那麼此時n就是vue,而不是undefined了 56 var {v:n} = {v:'vue',r:'react'}; 57 console.log(n); // vue 58 console.log(v); // Uncaught ReferenceError: v is not defined 59 console.log(r); // Uncaught ReferenceError: r is not defined
4. 複製數組
1 // 數組的淺拷貝,引用之間的拷貝,沒有實現數組的真正複製 2 var arr1 = [1, 2, 3]; 3 var arr2 = arr1; 4 arr2.push(4); 5 console.log(arr1, arr2); //[1, 2, 3, 4] [1, 2, 3, 4] 6 7 // 複製數組深拷貝,傳統做法 8 var arr1 = [1,2,3]; 9 var arr2 = []; 10 //通過for迴圈遍歷之後將arr1數組的每一項賦值給arr2數組的每一項, 就實現了數組的深拷貝,這時候我再去操作arr2的數組的時候,arr1就不會受影響了 11 for(var i=0;i<arr1.length;i++){ 12 arr2[i] = arr1[i]; 13 } 14 // 數組尾部添加 15 arr2.push(4); 16 console.log(arr1,arr2); 17 18 // ES6實現的數組的深拷貝方法1 19 var arr1 = [1,2,3]; 20 var arr2 = Array.from(arr1); 21 // 數組尾部添加 22 arr2.push(100); 23 console.log(arr1,arr2); // [1, 2, 3] [1, 2, 3, 100] 24 25 // ES6實現的數組的深拷貝方法2 26 var arr1 = [1,2,3]; 27 // 超引用拷貝數組 28 var arr2 = [...arr1]; 29 // 數組尾部添加 30 arr2.push(1000); 31 console.log(arr1,arr2); // [1, 2, 3] [1, 2, 3, 1000] 32 33 function show(...args){ 34 // 此時這個形式參數就是一個數組,我們可以直接push東西進來,如下 35 args.push(5); 36 console.log(args); 37 } 38 // 調用 39 show(1,2,3,4); // [1, 2, 3, 4, 5]
5. 增加了Map對象,傾向於後臺
1 var map = new Map(); 2 // 設置 3 // map.set(name,value); 4 map.set('a','apple'); 5 map.set('b','banana'); 6 // 獲取 7 // map.get(name); 8 console.log(map.get('a') + ' ' + map.get('b')); 9 // 刪除之前map對象 10 console.log(map); 11 // 刪除 12 // map.delete(name); 13 map.delete('a'); 14 // 刪除之後map對象 15 console.log(map); 16 17 // 註意for..in是不能迴圈map對象的,不報錯也無任何反應,所以下一代碼無任何輸出,稍微註意下 18 for(var name in map){ 19 console.log(name); 20 } 21 22 // 實體 map對象的迴圈輸出 23 for(var name of map){ 24 //迴圈出來的結果就是:a,apple b,banana 迴圈key,value 25 console.log(name); 26 } 27 28 //迴圈出來的結果就是: a,apple b,banana 迴圈key,value 29 for(var [key,value] of map.entries()){ 30 console.log(key,value); 31 } 32 33 //只迴圈key 34 for(var key of map.keys()){ 35 console.log(key); 36 } 37 38 //只迴圈value 39 for(var val of map.values()){ 40 console.log(val); 41 }
6. for-of迴圈 ,上一例子也說到了for-of迴圈map對象輸出
1 //for of一個arr對象 2 var arr = ['紅樓夢','西游記','三國演義','水滸傳','火影']; 3 //只迴圈key 0 1 2 3 4 輸出key值,也就是下標索引 4 for(var key of arr.keys()){ 5 console.log(key); 6 } 7 //只迴圈value,註意數組是沒有.values() 直接 var value of arr ,輸出 紅樓夢,西游記,三國演義,水滸傳,火影 8 for(var value of arr){ 9 console.log(value); 10 } 11 //迴圈key,value 12 for(var [key,value] of arr.entries()){ 13 console.log(key,value); 14 } 15 16 //for in迴圈與for of迴圈的區別 17 var arr = ['apple','banana','orange','pear']; 18 for(var i in arr){ 19 // i列印出來的就是arr數組對應的索引 20 // 0 1 2 3 21 console.log(i); 22 } 23 for(var i of arr){ 24 // i值列印出來的就是我們想要的數組具體的值 25 // apple banana orange pear 26 console.log(i); 27 } 28 29 //for of不能迴圈json 30 var json = {'a':'apple','b':'banana','c':'orange','d':'pear'}; 31 for(var name in json){ 32 // a b c d 33 console.log(name); 34 // apple 35 console.log(json.a); 36 // pear 37 console.log(json['d']); 38 } 39 // 註意for..of可以迴圈arr,但是不可以迴圈json,會報錯,特別註意下 40 for(var name of json){ 41 Uncaught TypeError: undefined is not a function 42 console.log(json); 43 }
7. 箭頭函數:引入箭頭函數有兩個方面的作用:更簡短的函數並且不綁定this
。
箭頭函數表達式的語法比函數表達式更短,並且不綁定自己的this,arguments,super或 new.target。這些函數表達式最適合用於非方法函數,並且它們不能用作構造函數,不能使用new。
箭頭函數的寫法 function(){ } 變成 ()=>{ }
1 var a = ()=>{ 2 return 1; 3 }
等價於
1 function a(){ 2 return 1; 3 }
箭頭函數不綁定arguments,取而代之用rest參數…解決
1 function A(a){ 2 console.log(arguments); //[object Arguments] [1, 2, 3] 3 } 4 5 var B = (b)=>{ 6 console.log(arguments); //錯誤:ReferenceError: arguments is not defined 7 } 8 9 var C = (...c)=>{ //...c即為rest參數 10 console.log(c); //[3, 1, 2] 11 } 12 A(1,2,3); 13 B(2,1,3); 14 C(3,1,2);
箭頭函數會捕獲其所在上下文的 this 值,作為自己的 this 值
1 var obj = { 2 a: 10, 3 b: function(){ 4 console.log(this.a); //輸出10 5 }, 6 c: function() { 7 return ()=>{ 8 console.log(this.a); //輸出10,捕獲了上面obj的this作為自己的this 9 } 10 } 11 } 12 obj.b(); 13 obj.c()();
所謂箭頭函數的 this 捕獲的是所在的上下文,比如下麵這個例子:b
是一個箭頭函數,然後它的 this
是指向window
,這是為什麼呢,因為箭頭函數捕獲的是obj{}
這個對象的環境,然後這個環境的this
指向的是window
,就相當於上一條的例子:在c
方法裡面return
的那個箭頭函數捕獲的是c:function(){}
這個環境的this
,而這個環境的this
是obj
1 var obj = { 2 a: 10, 3 b: () => { 4 console.log(this.a); //undefined 5 console.log(this); //window 6 }, 7 c: function() { 8 console.log(this.a); //10 9 console.log(this); //obj{...} 10 } 11 } 12 obj.b(); 13 obj.c();
對於函數的this
指向問題:
- 箭頭函數的
this
永遠指向其上下文的this
,任何方法都改變不了其指向,如call(), bind(), apply()
- 普通函數的
this
指向調用它的那個對象
8. 對象的簡潔語法
1 //傳統對象_單體模式寫法 key-value模式 2 var person = { 3 name:'krry', 4 age:21, 5 showName:function(){ 6 return this.name; 7 }, 8 showAge:function(){ 9 return this.age; 10 } 11 }; 12 // 調用 13 console.log(person.showName()); // krry 14 console.log(person.showAge()); // 21 15 16 //ES6_單體模式寫法 不需要寫key 17 var name = 'krry'; 18 var age = 21; 19 var person = { 20 name, 21 age, 22 showName(){ 23 return this.name; 24 }, 25 showAge(){ 26 return this.age; 27 } 28 }; 29 // 調用 30 console.log(person.showName()); // krry 31 console.log(person.showAge()); // 21
9. 類和繼承(class和extends)
1. 傳統面向對象的寫法:
1 function Person(name,age){ // 類、構造函數 2 this.name = name; 3 this.age = age; 4 } 5 Person.prototype.showName = function(){ 6 return this.name; 7 }; 8 Person.prototype.showAge = function(){ 9 return this.age; 10 }; 11 var p1 = new Person('allen',28); 12 var p2 = new Person('xiaoxiaoyou',101); 13 console.log(p1.showName()); // allen 14 console.log(p2.showAge()); // 101 15 console.log(p1.showName == p2.showName); //true 註意不是調用方法,沒有括弧,所以才true 16 console.log(p1.constructor == Person); // true 構造方法相等
2. ES6面向對象寫法:
1 class Person{ 2 // 構造器 3 constructor(name,age){ 4 this.name = name; 5 this.age = age; 6 } 7 showName(){ 8 return this.name; 9 } 10 showAge(){ 11 return this.age; 12 } 13 } 14 var p1 = new Person('aaa',18); 15 var p2 = new Person('bbb',20); 16 console.log(p1.name); // aaa 17 console.log(p1.showName()); // aaa 18 console.log(p2.showAge()); // 20 19 console.log(p1.showAge == p2.showAge); // true 20 console.log(p1.constructor == Person); // true
3. 面向對象給class賦值預設值:
1 class Person{ 2 // 構造器 3 constructor(name='default',age=0){ 4 this.name = name; 5 this.age = age; 6 } 7 showName(){ 8 return this.name; 9 } 10 showAge(){ 11 return this.age; 12 } 13 } 14 15 var p1 = new Person(); 16 console.log(p1.name); // 構造器裡面給的預設值 default 17 console.log(p1.age); // 構造器裡面給的預設值 0
4. 傳統寫法原型繼承extends
1 //傳統寫法原型繼承 2 function Person(name,age){ // 類、構造函數 3 this.name = name; 4 this.age = age; 5 } 6 Person.prototype.showName = function(){ 7 return this.name; 8 }; 9 Person.prototype.showAge = function(){ 10 return this.age; 11 }; 12 // 工人類 13 function Worker(name,age){ 14 // 屬性繼承過來 15 Person.apply(this,arguments); 16 } 17 // 原型繼承 18 Worker.prototype = new Person(); 19 var p1 = new Person('allen',28); 20 var w1 = new Person('worker',1000); 21 console.log(w1.showName()); // 確實繼承過來了 result:worker
5. ES6中面向對象實現類繼承
1 class Person{ 2 // 構造器 3 constructor(name,age){ 4 this.name = name; 5 this.age = age; 6 } 7 showName(){ 8 return this.name; 9 } 10 showAge(){ 11 return this.age; 12 } 13 } 14 class Worker extends Person{ 15 constructor(name,age,job='啦啦啦'){ 16 // 繼承超父類的屬性 17 super(name,age); 18 this.job = job; 19 } 20 showJob(){ 21 return this.job; 22 } 23 } 24 var p1 = new Person('aaa',18); 25 var w1 = new Person('www',36); 26 var w2 = new Worker('wwwwwwww',90); 27 console.log(w1.showName()); // www 28 console.log(w2.showJob()); // 預設給的值 ‘啦啦啦’
10. 模塊化 export 和 import
import 導入模塊、export 導出模塊
可以直接在任何變數或者函數前面加上一個 export
關鍵字,就可以將它導出。
在一個文件中:
1 export const sqrt = Math.sqrt; 2 export function square(x) { 3 return x * x; 4 } 5 export function diag(x, y) { 6 return sqrt(square(x) + square(y)); 7 }
然後在另一個文件中這樣引用:
1 import { square, diag } from 'lib'; 2 console.log(square(11)); // 121 3 console.log(diag(4, 3));
總結:
1 //mod.js 2 // 第一種模塊導出的書寫方式(一個個的導出) 3 // 導出普通值 4 export let a = 12; 5 export let b = 5; 6 // 導出json 7 export let json = { 8 a, 9 b 10 }; 11 // 導出函數 12 export let show = function(){ 13 return 'welcome'; 14 }; 15 // 導出類 16 export class Person{ 17 constructor(){ 18 this.name = 'jam'; 19 } 20 showName(){ 21 return this.name; 22 } 23 } 24 25 //index.js 26 //導出模塊如果用default了,引入的時候直接用,若沒有用default,引入的時候可以用{}的形式 27 // 導入模塊的方式 28 import { 29 a, 30 b, 31 json, 32 show, 33 Person 34 } from './mod.js'; 35 console.log(a); // 12 36 console.log(b); // 5 37 console.log(json.a); // 12 38 console.log(json.b); // 5 39 console.log(show()); // welcome 40 console.log(new Person().showName()); // jam 41 42 //mod1.js 43 // 第二種模塊導出的書寫方式 44 let a = 12; 45 let b = 5; 46 let c = 10; 47 export { 48 a, 49 b, 50 c as cc // as是別名,使用的時候只能用別名,特別註意下 51 }; 52 53 //index1.js 54 // 導入模塊的方式 55 import { 56 a, 57 b, 58 cc // cc是導出的,as別名 59 } from './mod1.js'; 60 console.log(a); // 12 61 console.log(b); // 5 62 console.log(cc); // 10 63 64 //mod2.js 65 // 第三種模塊導出的書寫方式 ---> default 66 // default方式的優點,import無需知道變數名,就可以直接使用,如下 67 // 每個模塊只允許一個預設出口 68 var name = 'jam'; 69 var age = '28'; 70 export default { 71 name, 72 age, 73 default(){ 74 console.log('welcome to es6 module of default...'); 75 }, 76 getName(){ 77 return 'bb'; 78 }, 79 getAge(){ 80 return 2; 81 } 82 }; 83 84 //