1. 程式人生 > >es6 函式擴充套件,引數作用域和箭頭函式

es6 函式擴充套件,引數作用域和箭頭函式

函式的擴充套件

函式引數的預設值

基本用法

ES6 之前,不能直接為函式的引數指定預設值,只能採用變通的方法。

function log(x, y = 'World') {
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello

與解構賦值預設值結合使用

function foo({x, y = 5}) {
  console.log(x, y);
}

foo({}) // undefined 5
foo({x: 1
}) // 1 5 foo({x: 1, y: 2}) // 1 2 foo() // TypeError: Cannot read property 'x' of undefined

引數預設值的位置

通常情況下,定義了預設值的引數,應該是函式的尾引數。因為這樣比較容易看出來,到底省略了哪些引數。如果非尾部的引數設定預設值,實際上這個引數是沒法省略的。

// 例一
function f(x = 1, y) {
  return [x, y];
}

f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 報錯
f(undefined, 1) // [1, 1]
// 例二 function f(x, y = 5, z) { return [x, y, z]; } f() // [undefined, 5, undefined] f(1) // [1, 5, undefined] f(1, ,2) // 報錯 f(1, undefined, 2) // [1, 5, 2]

函式的 length 屬性

指定了預設值以後,函式的length屬性,將返回沒有指定預設值的引數個數。也就是說,指定了預設值後,length屬性將失真。

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function
(a, b, c = 5) {
}).length // 2
  • fn.length 返回形參個數
  • arguments.length 返回實參個數

*作用域

一旦設定了引數的預設值,函式進行宣告初始化時,引數會形成一個單獨的作用域。等到初始化結束,這個作用域就會消失。這種語法行為,在不設定引數預設值時,是不會出現的。

var x = 1;

function f(x, y = x) {
  console.log(y);
}

f(2) // 2

上面程式碼中,引數y的預設值等於變數x。呼叫函式f時,引數形成一個單獨的作用域。在這個作用域裡面,預設值變數x指向第一個引數x,而不是全域性變數x,所以輸出是2

let x = 1;

function f(y = x) {
  let x = 2;
  console.log(y);
}

f() // 1

上面程式碼中,函式f呼叫時,引數y = x形成一個單獨的作用域。這個作用域裡面,變數x本身沒有定義,所以指向外層的全域性變數x。函式呼叫時,函式體內部的區域性變數x影響不到預設值變數x

var x = 1;

function foo(x = x) {
  // ...
}

foo() // ReferenceError: x is not defined

上面程式碼中,引數x = x形成一個單獨作用域。實際執行的是let x = x,由於暫時性死區的原因,這行程式碼會報錯”x 未定義“。

var x = 1;
function foo(x, y = function() { x = 2; }) {
  var x = 3;
  y();
  console.log(x);
}

foo()//3
x//1

如果將var x = 3var去除,函式foo的內部變數x就指向第一個引數x,與匿名函式內部的x是一致的,所以最後輸出的就是2,而外層的全域性變數x依然不受影響。

var x = 1;
function foo(x, y = function() { x = 2; }) {
  x = 3;
  y();
  console.log(x);
}

foo() // 2
x // 1

rest 引數

ES6 引入 rest 引數(形式為...變數名),用於獲取函式的多餘引數,這樣就不需要使用arguments物件了。rest 引數搭配的變數是一個數組,該變數將多餘的引數放入陣列中。

function add(...values) {
  let sum = 0;

  for (var val of values) {
    sum += val;
  }

  return sum;
}

add(2, 5, 3) // 10

arguments物件不是陣列,而是一個類似陣列的物件。所以為了使用陣列的方法,必須使用Array.prototype.slice.call先將其轉為陣列。rest 引數就不存在這個問題,它就是一個真正的陣列,陣列特有的方法都可以使用。下面是一個利用 rest 引數改寫陣列push方法的例子。

function push(array, ...items) {
  items.forEach(function(item) {
    array.push(item);
    console.log(item);
  });
}

var a = [];
push(a, 1, 2, 3)

注意,rest 引數之後不能再有其他引數(即只能是最後一個引數),否則會報錯。

// 報錯
function f(a, ...b, c) {
  // ...
}

函式的length屬性,不包括 rest 引數。

(function(a) {}).length  // 1
(function(...a) {}).length  // 0
(function(a, ...b) {}).length  // 1

嚴格模式

從 ES5 開始,函式內部可以設定為嚴格模式。

ES2016 做了一點修改,規定只要函式引數使用了預設值、解構賦值、或者擴充套件運算子,那麼函式內部就不能顯式設定為嚴格模式,否則會報錯

name 屬性

返回函式名。

function foo() {}
foo.name // "foo"
var f = function () {}; // "f"

箭頭函式

基本用法

ES6 允許使用“箭頭”(=>)定義函式。

var f = v => v;
//上面的箭頭函式等同於
var f = function(v) {
  return v;
};

如果箭頭函式不需要引數或需要多個引數,就使用一個圓括號代表引數部分。

var f = () => 5;
// 等同於
var f = function () { return 5 };

var sum = (num1, num2) => num1 + num2;
// 等同於
var sum = function(num1, num2) {
  return num1 + num2;
};

如果箭頭函式的程式碼塊部分多於一條語句,就要使用大括號將它們括起來,並且使用return語句返回。

var sum = (num1, num2) => { return num1 + num2; }

由於大括號被解釋為程式碼塊,所以如果箭頭函式直接返回一個物件,必須在物件外面加上括號,否則會報錯。

// 報錯
let getTempItem = id => { id: id, name: "Temp" };

// 不報錯
let getTempItem = id => ({ id: id, name: "Temp" });

使用注意點

箭頭函式有幾個使用注意點。

  1. 函式體內的this物件,就是定義時所在的物件,而不是使用時所在的物件。
  2. 不可以當作建構函式,也就是說,不可以使用new命令,否則會丟擲一個錯誤。
  3. 不可以使用arguments物件,該物件在函式體內不存在。如果要用,可以用 rest 引數代替。
  4. 不可以使用yield命令,因此箭頭函式不能用作 Generator 函式。

this指向的固定化,並不是因為箭頭函式內部有繫結this的機制,實際原因是箭頭函式根本沒有自己的this,導致內部的this就是外層程式碼塊的this。正是因為它沒有this,所以也就不能用作建構函式。

箭頭函式轉成 ES5 的程式碼如下。

// ES6
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

// ES5
function foo() {
  var _this = this;

  setTimeout(function () {
    console.log('id:', _this.id);
  }, 100);
}
//轉換後的 ES5 版本清楚地說明了,箭頭函式裡面根本沒有自己的this,而是引用外層的this。

由於箭頭函式沒有自己的this,所以當然也就不能用call()apply()bind()這些方法去改變this的指向。

函式引數的尾逗號

ES2017 允許函式的最後一個引數有尾逗號(trailing comma)。

這樣的規定也使得,函式引數與陣列和物件的尾逗號規則,保持一致了。

function clownsEverywhere(
  param1,
  param2,
) { /* ... */ }

clownsEverywhere(
  'foo',
  'bar',
);