1. 程式人生 > >ES6對比ES3\ES5

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](),
  1. for中 callbacks[i]=function(){} 語句執行時 不能執行函式體(函式帶()才是執行函式)。函式內部不變,函式體內儲存表示式而非值,形成閉包
  2. callbacks[x]() 相當於執行return i * 2(此時 i==3)。
  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 {}