1. 程式人生 > >ECMAScript6(ES6)標準之函式擴充套件特性箭頭函式、Rest引數及展開操作符

ECMAScript6(ES6)標準之函式擴充套件特性箭頭函式、Rest引數及展開操作符

ES6擴充套件了很多語法糖語法
其中對於函式我們又可以使用一種叫做“箭頭函式”的寫法
同時引入了Rest引數
利用“…”可以獲取多餘引數
這樣就我們就不要使用arguments物件了
下面我來詳細地談一談

函式預設引數

ES6沒有出現之前
面對預設引數會讓很多人感到痛苦
大家會採用各種hack
比如:arr = arr || []
現在要容易得多

function foo(name = 'payen'){
    console.log('my name is ' + name);
}
foo(); //my name is payen

只需要在引數列表中像賦值一樣
就可以使用預設引數

Rest引數

當一個函式的最後一個引數有“…”這樣的字首
它就會變成一個引數的陣列
為了讓大家更好的理解
先來回憶一下我們以前ES6之前使用的arguments

function foo(){
    var args = Array.prototype.slice.call(arguments);
    console.log(args);
}
foo(1, 2, 3);// [1, 2, 3]

這裡我先使用Array原型上的slice把arguments類陣列
變成了真正的陣列
然後列印這個引數陣列
現在我們使用ES6的Rest引數
程式碼可已修改為這樣

function
foo(...args){
console.log(args); } foo(1, 2, 3);// [1, 2, 3]

確實要簡潔很多
注意Rest引數與arguments有以下幾點區別

  • Rest引數是未指定變數名的引數陣列
    arguments是全部引數的集合
  • Rest引數是真正的陣列
    arguments物件只是有length屬性的類陣列

展開操作符

上面我們提到了“…”操作符
包括我之前寫到的解構賦值語法
都用到了它
它是ES6的新特性
然而它的強大不只如此
“…”叫做展開操作符
允許一個表示式在某處展開,用於
存在多個引數(函式呼叫)、多個元素(陣列)、多個變數(解構賦值)
下面我就把除了Rest引數以外的用法總結一下
(解構賦值的應用也不談了,忘了的同學戳這裡:

傳送門,目錄下的“1.3特殊用法”)

函式呼叫

想一想我們在ES6之前
如何將陣列拆分作為引數傳入函式

var arr = [1, 2, 3];
function demo(x, y, z){
    console.log(x, y, z);
}
demo.apply(null, arr);// 1 2 3

利用apply來達到目的
看起來怪怪的
而現在我們可以這樣做

let arr = [1, 2, 3];
function demo(x, y, z){
    console.log(x, y, z);
}
demo(...arr);// 1 2 3    《--

陣列字面量

ES6之前我們想要拼接陣列
可以使用concat()

var arr1 = [1, 2, 3];
var arr2 = [4, 5, 6];
var arr = arr1.concat(arr2);
console.log(arr);// [1, 2, 3, 4, 5, 6]

利用展開操作符就不需要呼叫這個方法了

var arr1 = [1, 2, 3];
var arr2 = [4, 5, 6];
var arr = [...arr1, ...arr2];
console.log(arr);// [1, 2, 3, 4, 5, 6]

展開物件

展開陣列是如此簡單易用
那麼物件呢?

let person = {
    name: 'payen',
    sex: 'male',
};
person = {...person, age: 19};
console.log(person);

把這段程式碼放在你的編輯器了
你會發現它根本不能執行
因為ES6根本沒有這樣的語法
不過它是ES7的提案之一
可以讓我們以更加簡潔的形式
將一個物件的可列舉屬性複製到另一物件上
(ps:這一特性可以通過Babel及其外掛實現)
(ps:React在JSX語法已經採用這種寫法)

箭頭函式

箭頭函式是一種更簡單的函式宣告方式
它永遠是匿名函式
我們一般的函式寫法

var sum = function(a, b){
    return a + b;
};

箭頭函式

let sum = (a, b) => {
    return a + b;
};

當“箭頭”後面是表示式的時候
還可以簡化(省略“{ }”大括號 和 return關鍵字)

let sum = (a, b) => a + b;

略去function關鍵字
“=>”的前面是“( )”和內部的引數列表
“=>”的後面是程式碼塊

這個胖胖的箭頭被人為 單調乏味冗長 的function關鍵字的簡寫
(不過我覺得現在所有英文單詞中function這個詞我敲得最快(+﹏+)~)
不過它的作用可不僅僅是略微提高一點我們鍵盤的壽命
這個一會兒再說

回撥函式

先來看看它在回撥函式中應用它

var arr = [1, 2, 3];
console.log(arr.map((value)=>value*2));// [2, 4, 6]

不知道大家能不能看懂
它就相當於下面的程式碼

var arr = [1, 2, 3];
console.log(arr.map(function(value){
    return value*2;
}));// [2, 4, 6]

返回物件

還有一點要注意
就是如果我們的函式只是返回一個物件

var foo = function(){
    return {
        name: 'payen'
    }
}

使用箭頭函式簡化一步

let foo = () => {
    return {
        name: 'payen'
    }
}

再簡化一步

let foo = () => {name: 'payen'}; //錯誤的寫法

這時我的編輯器提示語法問題了
這是應為它認為“=>”後面是一個程式碼塊
而不會理解為是要返回的物件
解決辦法仍然是使用萬能的“( )”
這樣編輯器、瀏覽器都會認為它是表示式了

let foo = () => ({name: 'payen'}); //正確的寫法

this指向

巢狀函式就會產生this指向的問題
舉個例子

var flag = 'global';
var obj = {
    flag: 'object',
    method: function(){
        console.log(this.flag);
        setTimeout(function(){
            console.log(this.flag);
        }, 200)
    }
};
obj.method();//object global

即便把var宣告全改為let也會輸出 object undefined
第一個console.log不用說
物件的巢狀函式會指向全域性window物件
method方法丟失了同this之間的繫結
這被認為是JavaScript在設計上的重大缺陷
我們一般都會採用一些hack來解決

使用臨時快取變數
藉助於詞法作用域
利用一個識別符號self(通常起名self、that、_this)
(ES3)

var flag = 'global';
var obj = {
    flag: 'object',
    method: function(){
        var self = this;
        setTimeout(function(){
            console.log(self.flag);
        }, 200)
    }
};
obj.method(); //object

或者利用bind()
(ES5)

//ES5
var flag = 'global';
var obj = {
    flag: 'object',
    method: function(){
        setTimeout(function(){
            console.log(this.flag);
        }.bind(this), 200)
    }
};
obj.method(); //object

現在使用箭頭函式可以輕鬆解決問題
(ES6)

var flag = 'global';
var obj = {
    flag: 'object',
    method: function(){
        setTimeout(()=>console.log(this.flag), 200);
    }
};
obj.method(); //object

我們看到
箭頭函式在涉及this繫結時的行為
與普通函式行為完全不同
它是用當前的此法作用域覆蓋this本來的值
(“繼承”了method()方法的this繫結)

最後還是要向大家強調一下箭頭函式不夠理想的一點
它是匿名而不是具名的