1. 程式人生 > >ES6函式的擴充套件

ES6函式的擴充套件

1.函式的預設值

 es5

{

function logEvent(x, y) {

y = y || 3;

console.log(x + y)

}

logEvent('2', '3'); //2,3

logEvent('2'); //2,3

logEvent('2', ''); //2,3

}

 這個例子中,前面兩個顯示是我們想要的結果,如果引數y賦值了,但是對應的布林值為false,則該賦值不起作用。

 就像上面程式碼的最後一行,引數y等於空字元,結果被改為預設值。

 解決方法,先判斷,

{

function logEvent(x, y) {

if (typeof y === 'undefined') {

y = 'World';

}

console.log(x + y)

}

logEvent('2', '3'); //2,3

logEvent('2'); //2,World

logEvent('2', ''); //2

}

 ES6 允許為函式的引數設定預設值,即直接寫在引數定義的後面。

{

function logEvent(x, y = 'world') {

console.log(x + y)

}

logEvent('2', '3'); //2,3

logEvent('2'); //2,world

logEvent('2', ''); //2

}

{

function Point(x = 0, y = 0) {

this.x = x;

this.y = y;

}

const p = new Point();

console.log(p) // { x: 0, y: 0 }

}

 ES6 的寫法還有兩個好處:首先,閱讀程式碼的人,可以立刻意識到哪些引數是可以省略的,

 不用檢視函式體或文件;其次,有利於將來的程式碼優化,即使未來的版本在對外介面中,徹底拿掉這個引數,

 也不會導致以前的程式碼無法執行。

 引數變數是預設宣告的,所以不能用let或const再次宣告。

{

function logEvent(x = 5) {

var x = 7 //不報錯

let x = 4 //報錯

const x = 6 //報錯

}

}

 使用引數預設值時,函式不能有同名引數。

{// 不報錯

function foo(x, x, y) {

// ...

}



// 報錯

// function foo(x, x, y = 1) {

// // ...

// }

}

 另外,一個容易忽略的地方是,引數預設值不是傳值的,而是每次都重新計算預設值表示式的值。也就是說,引數預設值是惰性求值的。

{

let x = 99;

function foo(p = x + 1) {

console.log(p);

}



foo() // 100



x = 100;

foo() // 101

}

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

{

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

}

 如果函式foo呼叫時沒提供引數,變數x和y就不會生成,從而報錯。

 所以

{

function foo({ x, y = 5 } = {}) {

console.log(x, y)

}

foo();

}

 另一案例

{

function fetch(url, { body = ' ', method = 'GET', headers = {} }) {

console.log(method);

}

fetch('http://example.com', {})

// "GET"

// fetch('http://example.com')

// 報錯

}

 如果函式fetch的第二個引數是一個物件,就可以為它的三個屬性設定預設值。這種寫法不能省略第二個引數,

{

function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {

console.log(method);

}

fetch('http://example.com') //get

}

下面介紹下下面這兩種設定預設值的方法

{

// 寫法一

function m1({ x = 0, y = 0 } = {}) {

return [x, y];

}



// 寫法二

function m2({ x, y } = { x: 0, y: 0 }) {

return [x, y];

}
 函式沒有引數的情況

m1() // [0, 0]

m2() // [0, 0]

// x 和 y 都有值的情況

m1({ x: 3, y: 8 }) // [3, 8]

m2({ x: 3, y: 8 }) // [3, 8]

// x 有值,y 無值的情況

m1({ x: 3 }) // [3, 0]

m2({ x: 3 }) // [3, undefined]

// x 和 y 都無值的情況

m1({}) // [0, 0];

m2({}) // [undefined, undefined]

m1({ z: 3 }) // [0, 0]

m2({ z: 3 }) // [undefined, undefined]

}

上面兩種寫法都對函式的引數設定了預設值,區別是寫法一函式引數的預設值是空物件,但是設定了物件解構賦值的預設值;

 寫法二函式引數的預設值是一個有具體屬性的物件,但是沒有設定物件解構賦值的預設值。

 3.引數預設值的位置

 如果非尾部的引數設定預設值,實際上這個引數是沒法省略的。

 例一

{

function f(x = 1, y) {

conosle.log([x, y]);

}



f() // [1, undefined]

f(2) // [2, undefined])

// f(, 1) // 報錯

f(undefined, 1) // [1, 1]

 例二

function f(x, y = 5, z) {

console.log([x, y, z]);

}



f() // [undefined, 5, undefined]

f(1) // [1, 5, undefined]

// f(1, , 2) // 報錯

f(1, undefined, 2) // [1, 5, 2]

f(1, null, 2); //[1,null,1]

}

 上面程式碼中,有預設值的引數都不是尾引數。這時,無法只省略該引數,而不省略它後面的引數,除非顯式輸入undefined。

 如果傳入undefined,將觸發該引數等於預設值,null則沒有這個效果

 4.函式的length屬性

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

// 如果設定了預設值的引數不是尾引數,那麼length屬性也不再計入後面的引數了。

{

console.log((function (a) { }).length); //1

console.log((function (a, b = 3, c) { }).length); //

console.log((function (a = 4, b, c) { }).length); //0

}

 5.作用域

 一旦設定了引數的預設值,函式進行宣告初始化時,引數會形成一個單獨的作用域(context)。等到初始化結束,

 這個作用域就會消失。這種語法行為,在不設定引數預設值時,是不會出現的。

{

let 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。

 6.rest引數

 ES6 引入 rest 引數(形式為...變數名),用於獲取函式的多餘引數,這樣就不需要使用arguments物件了。

 rest 引數搭配的變數是一個數組,該變數將多餘的引數放入陣列中。

 求和

{

function Add(...value) {

let sum = 0;

for (let i of value) {

sum += i;

}

console.log(sum);

}

Add(2, 4, 5);

}

 rest 引數代替arguments變數的例子

{

 arguments變數的寫法

function sortNumbers() {

return Array.prototype.slice.call(arguments).sort();

}

 rest引數的寫法

// const sortNumbers = (...numbers) => numbers.sort();

}

 arguments物件不是陣列,而是一個類似陣列的物件。所以為了使用陣列的方法,必須使用Array.prototype.slice.call先將其轉為陣列。

 rest 引數就不存在這個問題,它就是一個真正的陣列,陣列特有的方法都可以使用。下面是一個利用 rest 引數改寫陣列push方法的例子。

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

// 報錯

// function f(a, ...b, c) {

// // ...

// }

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

{

console.log((function (a) { }).length) // 1

console.log((function (...a) { }).length) // 0

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

}

 7.嚴格模式

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

 兩種方法可以規避這種限制。第一種是設定全域性性的嚴格模式,這是合法的。

{

'use strict';

function doSomething(a, b = a) {

// code

}

}

 第二種是把函式包在一個無引數的立即執行函式裡面。

{

const doSomething = (function () {

'use strict';

return function (value = 42) {

return value;

};

}());

}

8.name 屬性

{

function f() {

console.log(f.name)

}

f()

let f1 = function () {

// es5

console.log(f1.name)//''

// es6

console.log(f1.name)//f1

}

f1();

let f2 = function func2() {

console.log(func2.name)

}

f2();

}

 函式的name屬性,返回該函式的函式名。

 Function建構函式返回的函式例項,name屬性的值為anonymous。

{

(new Function).name // "anonymous"

}

 bind返回的函式,name屬性值會加上bound字首。

{

function foo() { };

// foo.bind({}).name // "bound foo"

(function () { }).bind({}).name // "bound "

}