ES6對比ES3\ES5
20180812
變數提升
- 提升是瀏覽器解析JavaScript的結果。在執行JavaScript程式碼前,所有變數都會被“提升”,提升到函式作用域的頂部。
function getClothing(isCold){
if(isCold){
var freezing = "Grab a jacket!";
} else{
var hot = "It's a shorts kind of day.";
console.log(freezing)
}
}
變數提升後相當於
function getClothing(isCold){
var freezing,hot;
......
}
常量
- ES3:無常量概念
- ES5:給物件定義屬性,繫結在window上,並設屬性為只讀 Object.defineProperty()
- ES6:const定義
//ES5
Object.defineProperty(window, "PI5", {
value: 3.1415926,
writable: false,
})
console.log(window.PI5);
//ES6
const PI6 = 3.1415926;
console.log(PI6);
變數宣告
- var:要麼為全域性作用域,要麼為本地作用域,也就是整個函式作用域。
- let:作用域為塊。變數可以重新賦值,但是不能在同一作用域內重新宣告。
- const:作用域為塊。變數必須賦初始值,但是不能在同一作用域內重新宣告,也無法重新賦值。
if(isCold){
let freezing = 'Grab a jacket!';
} else{
let hot = 'Its a shorts kind of day';
console.log(freezing); // not defined
}
- 使用 let 和 const 宣告的變數僅在它們所宣告的塊中可用.
- 如果在程式碼塊(用花括號 { } 表示)中使用 let 或 const 宣告變數,那麼該變數會陷入暫時性死區,直到該變數的宣告被處理。這種行為會阻止變數被訪問,除非它們被聲明瞭。
箭頭函式
- 箭頭函式與普通函式區別在於this的繫結。
- ES3\ES5:function a(){}
- ES6:(parameters)=>{statements}; ()中引數,若只有一個引數,省略()。 {}中表達式直接作為返回值時省略。
——————————————————————–
陣列遍歷evens.map()
-ES3\ES5:使用evens.map(function(){})
-ES6:使用箭頭函式
{
//ES3\ES5
var evens = [1, 2, 3, 4, 5];
var odds = evens.map(function(v) {
return v + 1
});
console.log(evens, odds);
};
{
//ES6
let evens = [1, 2, 3, 4, 5];
let odds = evens.map(v => v + 1); //箭頭函式
console.log(evens, odds);
};
——————————————————————–
this指向問題
普通函式:this的指向是該函式被呼叫的物件。
箭頭函式:this指向的是定義時this的指向。
普通函式:
- this總是代表它的直接呼叫者(js的this是執行上下文),例如 obj.func ,那麼func中的this就是obj;
- 在預設情況(非嚴格模式下,未使用 ‘use strict’),沒找到直接呼叫者,則this指的是 window;
- 在嚴格模式下,沒有直接呼叫者的函式中的this是undefined;
- 使用call,apply,bind(ES5新增)繫結的,this指的是 繫結的物件;
箭頭函式:
- 箭頭函式沒有自己的this,它的this是繼承而來;
- 預設指向在定義它時所處的物件(宿主物件),而不是執行時的物件。 定義它的時候,可能環境是window;
- 箭頭函式可以方便地讓我們在 setTimeout ,setInterval中方便的使用this;
{
//ES3\ES5宣告一個類,用函式作為類構造器.
var factory = function(){
this.a = 'a';
this.b = 'b';
this.c = {
a:'a+',
b:function(){ //普通函式宣告
return this.a //this的指向是該函式被呼叫的物件,b()由c呼叫
}
}
}
};
使用 new factory().c.b()
訪問c中b函式。b()由c呼叫,故b()函式體中this指向c。
{
// ES6使用箭頭函式,避免this指向不確定
let factory=function(){ //建構函式中this指向factory的例項
this.a = 'a';
this.b = 'b';
this.c = {
a:'a+',
b:()=>{ //箭頭函式宣告,箭頭函式this指向是定義時this的指向。b在定義這個函式時this指向factory例項
return this.a
}
}
}
}
使用new factory().c.b()
訪問。因b使用箭頭函式宣告,故this指向:定義時this的指向(即指向factory例項)。
作用域
- 函式名與執行函式
- 函式名只是一個標識(指向函式的指標),而()才是執行函式。
- 當一個函式被呼叫完成之後,其執行上下文環境將被銷燬,其中的變數也會被同時銷燬。
//ES5
const callbacks = []
for (var i = 0; i <= 2; i++) { // var 變數提升
callbacks[i] = function() { //沒有執行函式,函式內部不變,函式體內儲存表示式而非值,形成閉包。
return i * 2 //對變數的引用,而非對值的引用。 所以其「值」只有在執行時才能確定
}
}
//記憶體回收機制:當一個函式被呼叫完成之後,其執行上下文環境將被銷燬,其中的變數也會被同時銷燬。垃圾回收callbacks[]。
//暫不回收i(變數被引用著所以不會被回收)
//資料以表格的形式顯示,接收一個強制的引數(必須是一個數組或者是一個物件)
//執行return i*2,表示式求值i=3
console.table([
callbacks[0](),
callbacks[1](),
callbacks[2](),
- for中
callbacks[i]=function(){}
語句執行時 不能執行函式體(函式帶()才是執行函式)。函式內部不變,函式體內儲存表示式而非值,形成閉包。 callbacks[x]()
相當於執行return i * 2(此時 i==3)。- for結束後,callbacks[i]使用完成,由記憶體回收機制回收;而i不被回收(變數被callbacksx引用著所以不會被回收)。
——————————————————————–
- 通過使用let宣告的變數,儲存當前塊作用域的值。
const callbacks2 = []
//let宣告的變數,塊作用域。 每迴圈一次,生成一個新的作用域
for (let j = 0; j <= 2; j++) {
callbacks2[j] = function() {
return j * 2 //該處閉包,取決於當前的塊作用域,儲存當前塊作用域的值,供後面使用
}
}
console.table([
callbacks2[0](),
callbacks2[1](),
callbacks2[2](),
]);
——————————————————————–
- 作用域鏈
- 全域性變數預設掛載在window物件下。
- 當在函式中使用變數時,首先在本函式內部查詢該變數,後找其父級函式,最後直到window。
- 常見的window的屬性和方法有: alert, location, document, parseInt, setTimeout, setInterval等, window的屬性預設可以省略window字首。
- 作用域隔離
ES3\ES5:通過‘立即執行函式’。
//ES3\ES5
((function(){
var foo = function(){
return 1
}
console.log("foo()===1",foo()==1);
((function(){
var foo = function(){ //與函式體外foo不衝突
return 2
}
console.log("foo()===2",foo()==2)
})());
console.log("foo()===1",foo()==1);
})());
ES6:使用{}指定作用域,隔離作用域。
{
function foo(){
return 1
}
console.log("foo()===1",foo()==1);
{
function foo(){
return 2
}
console.log("foo()===2",foo()==2);
}
console.log("foo()===1",foo()==1);
}
預設引數
基本使用
- ES3\ES5:x=x||1 判斷是否為undefined
- ES6:function f(x,y=7,z=42){}
{
//ES3\ES5:預設引數寫法
function f(x,y){
/*if(x===undefined){
x=7;
}
if(y===undefined){
y=42;
}*/
x = x || 7;
y = y || 42;
return x + y;
}
console.log(f(1,3));
};
{
// ES6
function f(x,y = 7,z = 42){
return x + y + z;
}
console.log(f(1));
};
必選引數檢查:通過函式checkParameter()丟擲異常throw new Error();並通過try…catch捕獲異常。
function checkParameter(){
throw new Error('can\'t be empty');
}
function f(x = checkParameter(), y = 7, z = 42){
return x + y + z;
}
try{
f()
}catch(e){
console.log(e)
}finally{
}
——————————————————————–
可變引數
ES3\ES5 將引數陣列化:Array.prototype.slice.call(arguments)
//ES3\ES5
function f(x){
var a = Array.prototype.slice.call(arguments); //arguments偽陣列,通過Array.prototype.slice.call()轉化為陣列
var sum = 0;
a.forEach(function(item){
sum += item * 1;
})
return sum;
}
console.log(f(1,2,3));
ES6:function f(…a){}; …a擴充套件運算子,a為可變引數列表陣列
//ES6
function f(...a){ //...a擴充套件運算子,a為可變引數列表陣列
var sum=0;
a.forEach(item=>{
sum+=item*1 //轉換為數字
});
return sum
}
console.log(f(2,3,4));
——————————————————————–
合併陣列
ES3\ES5:.concat() 拼接陣列
ES6:利用擴充套件運算子 var other=[1,2,…params];
{
//ES5 合併陣列
var params=['hello',true,7];
var other=[1,2].concat(params);
console.log(other);
};{
//ES6 利用擴充套件運算符合並陣列
var params=['hello',true,7];
var other=[
1,2,...params
];
console.log(other);
}
物件代理
ES3\ES5:var Person = function(){}; 內部宣告區域性作用域,通過this.get = function(){}和this.set = function(key, value){}訪問內部。
//ES3\ES5 資料保護
var Person = function() { //建構函式
//內部宣告,區域性作用域。 若無this訪問,則取不到data
var data = {
name: 'es3',
sex: 'male',
age: 15
}
this.get = function(key) {
return data[key];
}
this.set = function(key, value) {
if (key !== 'sex') {
data[key] = value;
}
}
}
//宣告一個例項
var person = new Person();
//讀取:通過api方式
console.table({name: person.get('name'), sex: person.get('sex'), age: person.get('age')});
//修改
person.set('name', 'es3-cname');
console.table({name: person.get('name'), sex: person.get('sex'), age: person.get('age')});
person.set('sex', 'female'); //資料保護
console.table({name: person.get('name'), sex: person.get('sex'), age: person.get('age')
});
ES5 直接宣告物件: Object.defineProperty(Person, ‘sex’, {writable: false,value: ‘male’}); 設定只能讀不能寫。
//ES5 直接宣告物件,而不用建構函式
var Person = {
name: 'es5',
age: 15
};
//保護資料:設定只能讀不能寫
Object.defineProperty(Person, 'sex', {
writable: false,
value: 'male'
});
console.table({name: Person.name, sex: Person.sex, age: Person.age});
Person.name = 'es5-cname';
console.table({name: Person.name, sex: Person.sex, age: Person.age});
try {
//不能為只讀屬性賦值
Person.sex = 'female';
console.table({name: Person.name, sex: Person.sex, age: Person.age});
} catch (e) {
console.log(e);
} finally {}
ES6:let person = new Proxy(Person, {}); 以Proxy作為Person代理。person作為使用者操作物件,保護Person。
//ES6
let Person = {
name: 'es6',
sex: 'male',
age: 15
};
//Proxy作為Person代理。person作為使用者操作物件,保護Person。
//target:Person;key:讀取的屬性
let person = new Proxy(Person, {
get(target, key) {
return target[key]
},
set(target, key, value) {
if (key != 'sex') {
target[key] = value;
}
}
});
console.table({name: person.name, sex: person.sex, age: person.age});
//對代理物件person操作
try {
person.sex = 'female';
} catch (e) {
console.log(e);
} finally {}