ES6對函數的改動

ES6 functions改動

    ES6函數的改變不算太大,都是一些其餘語言早就有的功能,而Javascript一直比較欠缺的,好比函數參數默認值,任意參數的表示法,最大的變化應該是支持箭頭函數(其餘語言稱之爲LAMBDA表達式),一種對匿名函數的一種簡寫方式,如下來探討一下函數在ES6中的一些改變:
數組

1. 默認參數(default parameters)
2. 任意參數(rest parameters ...)
3. spread 操做符(...)
4. new.target 元屬性
5. 箭頭函數( => )
6. 其餘一些改動(miscellaneous)

1.默認參數

    ES6以前一直是經過其餘方法來模擬默認參數的,例如邏輯或||符號,ES6版本真正意義上支持這種便利的寫法。app

// ES5模擬默認參數
function person(name, age) {
    name = name || "James";
    age = age || "18";
    console.log(name + " " + age);
}
// 通常狀況下這種寫法是沒問題的,當邏輯或前面的值爲falsy值,整個表達式返回後面的值
// 例如:
  person("Louis"); // ok
person(); // ok
person(undefined, 20); // ok
person("baby", 0); // "baby 18" error, 0爲falsy值

上面能夠看出這種寫法是有必定問題的,各類JS庫給出了另外一種寫法函數

function person(name, age) {
    if (typeof name === "undefined") {
        name = name || "James"; 
    }
    if (typeof age === "undefined") {
        age = age || "18";
    }
    console.log(name + " " + age);
}
person(undefined, 0); // ok "James 0"

ES6寫法性能

function person(name = "James", age = 18) {
    console.log(name + " " + age);
}

// 1各類寫法 默認參數出如今中間
function getRequest(url, timeout = 2000, callback) {
    // do something
}
gerRequest("/foo", undefined, function() {

});

// 2默認參數表達式
let value = 5;
function getValue() {
    return value++;
}
function add(first, second = getValue()) {
    return first + second;
}
add(3); // 8
add(3); // 9
add(1, 1); // 2

// 3後面參數引用前面參數
function add(first, second = first) {
    return first + second;
}
add(2); // 4
add(3, 4); // 7

默認參數TDZ(暫時死區)狀況:優化

// 上面的第三種寫法,若寫成第一個參數引用第二個參數
function add(first = second, second) {
    return first + second;
}
add(1, 1); // ok 2
add(undefined, 4); // THROW AN ERROR 第二個參數未聲明就引用就會拋出錯誤
// 就至關於
let first = second; // error
let second = 4;

2.任意參數

    ES6任意參數用 ... 表示,任意參數和arguments之間的差異
ES5使用arguments參數來實現對象屬性拷貝:ui

function pick(object) {
    var result = Object.create(null); // 建立一個對象
    // 從第二個參數開始
    for (var i = 1, len = arguments.length; i < len; i++) {
        result[arguments[i]] = object[arguments[i]];
    }
    return result;
}
// arguments將object也計入,因此除開第一個參數要減1
var book = {
    title: "understanding ES6",
    author: "Nicholas C.Zakes",
    year: 2016
};
var o = pick(book, "author", "year");
o.author; // "Nicholas C.Zakes"
o.year; // 2016

上面的pick函數看上去不夠直觀,由於除第一個參數外不知道要添加幾個參數,使用新語法this

function pick(object, ...keys) {
    var result = Object.create(null); // 建立一個對象
    for (var i = 0, len = keys.length; i < len; i++) {
        result[keys[i]] = object[keys[i]];
    }
    return result;
}
// keys將object不計入在其內
var book = {
    title: "understanding ES6",
    author: "Nicholas C.Zakes",
    year: 2016
};
var o = pick(book, "author", "year");
o.author; // "Nicholas C.Zakes"
o.year; // 2016

使用rest parameters注意事項

1.要將任意參數放到函數的最後,不能放在中間位置

2.不能用於對象字面量setter中url

function pick(object, ...keys, last) { //... }
// 語法錯誤

let object = {
    set name(...value) {
        // do something
    }
};
// 語法錯誤

3.spread操做符

spread操做符和rest parameters同樣,都使用 ... 表示,spread操做符容許咱們將數組中的參數一個一個傳入函數中
例如:prototype

// Math.max()函數, 通常能夠加入任意個參數
Math.max(12, 13, 14, 15); // 15

// 以數組的形式
var arr = [1, 2, 3, 4];
Math.max.apply(null, arr); // 4

// 使用 "..."
Math.max(...arr); // 4
// 還能夠加入其它的一些參數
Math.max(...arr, 5, 10); // 10

將一個數組去重:調試

var arr = [1, 2, 2, 4, 4];
// 使用Set將重複的去掉,而後將set對象轉變爲數組
var mySet = new Set(arr); // mySet {1, 2, 4}

// 方法1,使用Array.from轉變爲數組
// var arr = Array.from(mySet); // [1, 2, 4]

// 方法2,使用spread操做符
var arr = [...arr]; // [1, 2, 4]

// 方法3, 傳統forEach
var arr2 = [];
mySet.forEach(v => arr2.push(v));

4.new.target元屬性

    函數內部有兩個方法 [[call]][[construct]] (箭頭函數沒有這個方法),當使用new 操做符時, 函數內部調用 [[construct]], 建立一個新實例,this指向這個實例; 不使用new 操做符時, 函數內部調用 [[call]]

    判斷一個函數是否使用new操做符,ES5的方法:

function Person(name) {
    if (this instanceof Person) {
        this.name = name;
    } else {
        throw new Error("You must use new operator");
    }
}
var p = new Person("James"); // ok
var p = Person("James"); // error
// 可是能夠經過其餘方式繞過這種錯誤
var notPerson = Person.call(p, "Nicholas"); // works

ES6 經過new.target 來判斷是否使用new,元屬性 是指一個提供目標相關額外信息(好比new)的非對象屬性。

function Person(name) {
    if (typeof new.target !== "undefined") {
        this.name = name;
    } else {
        throw new Errow("You must use new operator");
    }
}
var p = new Person("James"); // ok
var notPerson = Person.call(p, "Louis"); // error

5.箭頭函數

箭頭函數有如下幾個方面的特色:

  1. this, super, arguments和arguments的值由最近一個包含它的非箭頭函數定義。(No this, superm arguments and new.target bindings);
  2. 箭頭函數內部沒有 [[construct]]方法, 所以不能看成構造器,使用new操做符;
  3. 不存在原型(No prototype);
  4. 不能改變this, 在整個箭頭函數生命週期this值保持不變;
  5. 不存在arguments對象,不過包含它的函數存在,箭頭函數依靠命名參數和rest parameters;
  6. 不能擁有重複的命名參數,ES5只有嚴格模式下才不容許
1.箭頭函數語法
// 語法很簡單
let sum = (n1, n2) => n1 + n2;
// 至關於
let sum = function(n1, n2) {
    return n1 + n2;
};

let getTempItem = id => ({ id: id, name: "Temp" });
// 至關於
let getTempItem = function(id) {
    return {
        id: id,
        name: "Temp"
    };
};
2.沒有this綁定
let PageHandler = {
    id: "123456",
    init: function() {
        document.addEventListener("click", function(event) {
            this.doSomething(event.type); // error
        }, false);
    },
    doSomething: function(type) {
        console.log("Handling " + type + " for " + this.id);
    }
};
// init函數中的this.doSomething,this指向的是函數內部document對象,
// 而不是PageHandler對象

使用箭頭函數改寫:

let PageHandler = {
  id: "123456",
  init: function() {
      document.addEventListener("click", event => this.doSomething(evnet.type)
        }, false);
    },
  doSomething: function(type) {
      console.log("Handling " + type + " for " + this.id);
  }
};
// 此處箭頭函數this指向包含它的函數,即init,init爲PageHandler的方法,
// this指向PageHandler對象實例
3.不能使用new
var MyType = () => {};
var obj = new MyType(); // Error
4.沒有arguments對象

箭頭函數沒有arguments對象,可是能夠使用包含函數中的arguments對象

function createArrowFunctionReturningFirstArg() {
    // arguments 爲 createArrowFunctionReturningFirstArg中的對象
    return  () => arguments[0]; 
}
var arrowFunction = createArrowFunctionReturningFirstArg(10);
arrFunction(); // 10

6.其餘(misllanceous)

其餘的一些變化:

  1. 添加name屬性用來判斷函數名,用於調試;
  2. 規範塊級別函數(block-level functions),當執行流結束塊級別函數退出,塊級別函數提高變量到塊頂部;
  3. 對尾部調用(Tail call, 值一個函數返回另外一個函數對象)性能進行優化,尤爲是都遞歸函數性能提高很大

總結
  1. ES6對默認參數的支持;
  2. 任意參數和spread操做符
  3. 箭頭函數

    整體來講,這些改動都是爲編寫程序提供了極大的便利,不用再使用workaround來解決語法存在的問題,總體來說,更加符合語言的書寫習慣。

相關文章
相關標籤/搜索