1. 程式人生 > 實用技巧 >【轉】 前端筆記之Vue(四)UI元件庫&Vuex&虛擬伺服器初識

【轉】 前端筆記之Vue(四)UI元件庫&Vuex&虛擬伺服器初識

箭頭函式表示式的語法比函式表示式更簡潔,並且沒有自己的thisargumentssupernew.target。箭頭函式表示式更適用於那些本來需要匿名函式的地方,並且它不能用作建構函式。

語法

基礎語法

(param1, param2, …, paramN) => { statements } 
(param1, param2, …, paramN) => expression
//相當於:(param1, param2, …, paramN) =>{ return expression; }

// 當只有一個引數時,圓括號是可選的:
(singleParam) => { statements }
singleParam => { statements }

// 沒有引數的函式應該寫成一對圓括號。
() => { statements }

高階語法

//加括號的函式體返回物件字面量表達式:
params => ({foo: bar})

//支援剩餘引數預設引數
(param1, param2, ...rest) => { statements }
(param1 = defaultValue1, param2, …, paramN = defaultValueN) => { 
statements }

//同樣支援引數列表解構
let f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f();  // 6

描述

參考"ES6 In Depth: Arrow functions" on hacks.mozilla.org

.

引入箭頭函式有兩個方面的作用:更簡短的函式並且不繫結this

更短的函式

var elements = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];

elements.map(function(element) { 
  return element.length; 
}); // 返回陣列:[8, 6, 7, 9]

// 上面的普通函式可以改寫成如下的箭頭函式
elements.map((element) => {
  return element.length;
}); // [8, 6, 7, 9]

// 當箭頭函式只有一個引數時,可以省略引數的圓括號
elements.map(element => {
 return element.length;
}); // [8, 6, 7, 9]

// 當箭頭函式的函式體只有一個 `return` 語句時,可以省略 `return` 關鍵字和方法體的花括號
elements.map(element => element.length); // [8, 6, 7, 9]

// 在這個例子中,因為我們只需要 `length` 屬性,所以可以使用引數解構
// 需要注意的是字串 `"length"` 是我們想要獲得的屬性的名稱,而 `lengthFooBArX` 則只是個變數名,
// 可以替換成任意合法的變數名
elements.map(({ "length": lengthFooBArX }) => lengthFooBArX); // [8, 6, 7, 9]

沒有單獨的this

在箭頭函數出現之前,每一個新函式根據它是被如何呼叫的來定義這個函式的this值:

  • 如果是該函式是一個建構函式,this指標指向一個新的物件
  • 在嚴格模式下的函式呼叫下,this指向undefined
  • 如果是該函式是一個物件的方法,則它的this指標指向這個物件
  • 等等

This被證明是令人厭煩的面向物件風格的程式設計。

function Person() {
  // Person() 建構函式定義 `this`作為它自己的例項.
  this.age = 0;

  setInterval(function growUp() {
    // 在非嚴格模式, growUp()函式定義 `this`作為全域性物件, 
    // 與在 Person()建構函式中定義的 `this`並不相同.
    this.age++;
  }, 1000);
}

var p = new Person();

在ECMAScript 3/5中,通過將this值分配給封閉的變數,可以解決this問題。

function Person() {
  var that = this;
  that.age = 0;

  setInterval(function growUp() {
    // 回撥引用的是`that`變數, 其值是預期的物件.
    that.age++;
  }, 1000);
}

或者,可以建立繫結函式,以便將預先分配的this值傳遞到繫結的目標函式(上述示例中的growUp()函式)。

箭頭函式不會建立自己的this,它只會從自己的作用域鏈的上一層繼承this。因此,在下面的程式碼中,傳遞給setInterval的函式內的this與封閉函式中的this值相同:

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // |this| 正確地指向 p 例項
  }, 1000);
}

var p = new Person();

與嚴格模式的關係

鑑於this是詞法層面上的,嚴格模式中與this相關的規則都將被忽略。

var f = () => { 'use strict'; return this; };
f() === window; // 或者 global

嚴格模式的其他規則依然不變.

通過 call 或apply 呼叫

由於箭頭函式沒有自己的this指標,通過call()apply()方法呼叫一個函式時,只能傳遞引數(不能繫結this---譯者注),他們的第一個引數會被忽略。(這種現象對於bind方法同樣成立---譯者注)

var adder = {
  base : 1,
    
  add : function(a) {
    var f = v => v + this.base;
    return f(a);
  },

  addThruCall: function(a) {
    var f = v => v + this.base;
    var b = {
      base : 2
    };
            
    return f.call(b, a);
  }
};

console.log(adder.add(1));         // 輸出 2
console.log(adder.addThruCall(1)); // 仍然輸出 2

不繫結arguments

箭頭函式不繫結Arguments 物件。因此,在本示例中,arguments只是引用了封閉作用域內的arguments:

var arguments = [1, 2, 3];
var arr = () => arguments[0];

arr(); // 1

function foo(n) {
  var f = () => arguments[0] + n; // 隱式繫結 foo 函式的 arguments 物件. arguments[0] 是 n,即傳給foo函式的第一個引數
  return f();
}

foo(1); // 2
foo(2); // 4
foo(3); // 6
foo(3,2);//6 

在大多數情況下,使用剩餘引數是相較使用arguments物件的更好選擇。

function foo(arg) { 
  var f = (...args) => args[0]; 
  return f(arg); 
}
foo(1); // 1

function foo(arg1,arg2) { 
    var f = (...args) => args[1]; 
    return f(arg1,arg2); 
} 
foo(1,2);  //2

使用箭頭函式作為方法

如上所述,箭頭函式表示式對非方法函式是最合適的。讓我們看看當我們試著把它們作為方法時發生了什麼。

'use strict';
var obj = {
  i: 10,
  b: () => console.log(this.i, this),
  c: function() {
    console.log( this.i, this)
  }
}
obj.b(); 
// undefined, Window{...}
obj.c(); 
// 10, Object {...}

箭頭函式沒有定義this繫結。另一個涉及Object.defineProperty()的示例:

'use strict';
var obj = {
  a: 10
};

Object.defineProperty(obj, "b", {
  get: () => {
    console.log(this.a, typeof this.a, this);
    return this.a+10; 
   // 代表全域性物件 'Window', 因此 'this.a' 返回 'undefined'
  }
});

obj.b; // undefined   "undefined"   Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}   

使用new操作符

箭頭函式不能用作構造器,和new一起用會丟擲錯誤。

var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor

使用prototype屬性

箭頭函式沒有prototype屬性。

var Foo = () => {};
console.log(Foo.prototype); // undefined

使用yield關鍵字

yield關鍵字通常不能在箭頭函式中使用(除非是巢狀在允許使用的函式內)。因此,箭頭函式不能用作函式生成器。

函式體

箭頭函式可以有一個“簡寫體”或常見的“塊體”。

在一個簡寫體中,只需要一個表示式,並附加一個隱式的返回值。在塊體中,必須使用明確的return語句。

var func = x => x * x;                  
// 簡寫函式 省略return

var func = (x, y) => { return x + y; }; 
//常規編寫 明確的返回值

返回物件字面量

記住用params => {object:literal}這種簡單的語法返回物件字面量是行不通的。

var func = () => { foo: 1 };               
// Calling func() returns undefined!

var func = () => { foo: function() {} };   
// SyntaxError: function statement requires a name

這是因為花括號({})裡面的程式碼被解析為一系列語句(即foo被認為是一個標籤,而非物件字面量的組成部分)。

所以,記得用圓括號把物件字面量包起來:

var func = () => ({foo: 1});

換行

箭頭函式在引數和箭頭之間不能換行。

var func = ()
           => 1; 
// SyntaxError: expected expression, got '=>'

但是,可以通過在 ‘=>’ 之後換行,或者用 ‘( )’、'{ }'來實現換行,如下:

var func = (a, b, c) =>
  1;

var func = (a, b, c) => (
  1
);

var func = (a, b, c) => {
  return 1
};

var func = (
  a,
  b,
  c
) => 1;
 
// 不會有語法錯誤

解析順序

雖然箭頭函式中的箭頭不是運算子,但箭頭函式具有與常規函式不同的特殊運算子優先順序解析規則。

let callback;

callback = callback || function() {}; // ok

callback = callback || () => {};      
// SyntaxError: invalid arrow-function arguments

callback = callback || (() => {});    // ok

更多示例

// 空的箭頭函式返回 undefined
let empty = () => {};

(() => 'foobar')(); 
// Returns "foobar"
// (這是一個立即執行函式表示式,可參閱 'IIFE'術語表) 


var simple = a => a > 15 ? 15 : a; 
simple(16); // 15
simple(10); // 10

let max = (a, b) => a > b ? a : b;

// Easy array filtering, mapping, ...

var arr = [5, 6, 13, 0, 1, 18, 23];

var sum = arr.reduce((a, b) => a + b);  
// 66

var even = arr.filter(v => v % 2 == 0); 
// [6, 0, 18]

var double = arr.map(v => v * 2);       
// [10, 12, 26, 0, 2, 36, 46]

// 更簡明的promise鏈
promise.then(a => {
  // ...
}).then(b => {
  // ...
});

// 無引數箭頭函式在視覺上容易分析
setTimeout( () => {
  console.log('I happen sooner');
  setTimeout( () => {
    // deeper code
    console.log('I happen later');
  }, 1);
}, 1);

箭頭函式也可以使用條件(三元)運算子:

var simple = a => a > 15 ? 15 : a;
simple(16); // 15
simple(10); // 10

let max = (a, b) => a > b ? a : b;

箭頭函式內定義的變數及其作用域

// 常規寫法
var greeting = () => {let now = new Date(); return ("Good" + ((now.getHours() > 17) ? " evening." : " day."));}
greeting();          //"Good day."
console.log(now);    // ReferenceError: now is not defined 標準的let作用域

// 引數括號內定義的變數是區域性變數(預設引數)
var greeting = (now=new Date()) => "Good" + (now.getHours() > 17 ? " evening." : " day.");
greeting();          //"Good day."
console.log(now);    // ReferenceError: now is not defined

// 對比:函式體內{}不使用var定義的變數是全域性變數
var greeting = () => {now = new Date(); return ("Good" + ((now.getHours() > 17) ? " evening." : " day."));}
greeting();           //"Good day."
console.log(now);     // Fri Dec 22 2017 10:01:00 GMT+0800 (中國標準時間)

// 對比:函式體內{} 用var定義的變數是區域性變數
var greeting = () => {var now = new Date(); return ("Good" + ((now.getHours() > 17) ? " evening." : " day."));}
greeting(); //"Good day."
console.log(now);    // ReferenceError: now is not defined

箭頭函式也可以使用閉包:

// 標準的閉包函式
function A(){
      var i=0;
      return function b(){
              return (++i);
      };
};

var v=A();
v();    //1
v();    //2


//箭頭函式體的閉包( i=0 是預設引數)
var Add = (i=0) => {return (() => (++i) )};
var v = Add();
v();           //1
v();           //2

//因為僅有一個返回,return 及括號()也可以省略
var Add = (i=0)=> ()=> (++i);