ES6隨筆--各數據類型的擴展(3)--函數
ES6隨筆--各數據類型的擴展(3)--函數
1. 函數參數的默認值
可以在函數參數中指定默認值,函數內不能再用let
或const
聲明;
使用參數默認值時,不能出現同名參數;
參數默認值惰性求值,每次調用函數會重新計算參數默認值表達式;
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo() // 100
x = 100;
foo() // 101
可以與解構賦值默認值結合使用:
function ({x, y = 5} = {}) { console.log(x, y); } function(); // undefined 5 // 以下對比默認值設置不同方式 function bar({x, y} = {x:0, y:0}) { console.log(x, y); } function bar1({x=0, y=0} = {}) { console.log(x, y); } bar(); //0 0 bar1(); //0 0 bar({x:2, y:3}); //2 3 bar1({x:2, y:3}); //2 3 bar({x:2}); // 2 undefined bar1({x:2}); // 2 0 bar({y:2}); // undefined 2 bar1({y:2}); // 0 2 bar({z:4}); // undefined undefined bar1({z:4}); // 0 0 bar({}); // undefined undefined bar1({}); // 0 0
設置了默認值的參數一般是函數的尾參數,該參數可以省略;如果位於中間,則無法只省略該參數繼續為後面的參數賦值,必須要設置為undefined
,否則直接省略而為後面的參數賦值會報錯;
undefined
可以觸發默認值;
函數的length
屬性中不包含指定了默認值的參數。
指定了參數默認值的函數,函數進行聲明初始化時該參數會形成單獨的作用域;函數執行完畢後該作用域消失;
// 函數內部 let 聲明的變量與參數變量不同作用域 var x = 1; function foo(y = x) { let x = 2; console.log(y); } foo() // 1 // 參數的作用域與全局和函數內部都不同; var x = 1; function foo(x, y = x) { x = 2; console.log(y); } foo(); // undefined foo(2); // 2 console.log(x); // 1 // 參數設置默認值時指向全局變量x, 函數內部修改了全局變量,但並不影響參數y--不在同一個作用域; var x = 1; function foo(y = x) { x = 2; console.log(y); } foo(); // 1 console.log(x); // 2 // 暫時性死區報錯(參數默認值相當於 let x = x,此時會忽略全局裏的定義) var x = 1; function foo(x = x) { console.log(x); } foo() // ReferenceError: x is not defined
函數的參數默認值可以用來設置哪些參數不能省略;
2. rest函數
形式為...varialbleName
,獲取函數的多余參數放入數組中;
// 改寫 push 方法的例子 function push(array, ...items) { items.forEach(function(item) { array.push(item); console.log(item); }); return array.length; } var a = []; console.log(push(a, 1, 2, 3)); // 1 2 3 3
rest
參數只能是最後一個參數;函數的length
屬性,不包含rest
函數;
3. 嚴格模式
只要函數參數使用了默認值,解構賦值或者擴展運算符,函數內部就不能顯式設定嚴格模式;
替代的辦法 1) 使用全局性的嚴格模式;2) 使用立即執行的無參函數,使用嚴格模式並返回該函數;
4. name 屬性
函數的name
屬性,返回該函數的函數名;
如果把匿名函數賦值給一個變量,匿名函數的name
屬性返回該變量名(ES5 則為空字符串);
如果把一個具名函數賦值給一個變量,該函數的name
屬性返回原本的函數名。
const f = function () {};
console.log(f.name); // f
const f1 = function func() {};
console.log(f1.name); // func
Function
構造函數返回的函數實例, name
屬性返回anonymous
;
使用bind()
返回的函數,name
屬性會自動在函數名前面加bound
;
5. 箭頭函數
使用箭頭=>
定義函數;funcName(args) => {code block}
與function funcName(args) = {code block}
相同;
註意:
1. 箭頭函數中的this
綁定函數定義時所在的作用域,而不是運行時的作用域;
2. 不能使用new
命令;
3. 不可以使用argumets
對象,可以使用rest參數代替;
4. 不可以使用yield
命令,因此箭頭函數不能用作Generator
函數;
箭頭函數本身沒有自己的this
,在定義時指向外部作用域的對象;也不能通過call
,bind
,apply
改變this
的指向。
var f = function () {console.log(this.id);}
var f1 = () => {console.log(this.id)};
f.call({id:11}); // 11
f.call({id:22}); // 22
f1.call({id:11}); // undefined
f1.call({id:22}); // undefined
// 大括號被解釋為代碼塊,所以需要返回一個對象的情況下需要在大括號外加小括號;(再加一層大括號無效);
var f = (x) => {a:x};
console.log(f(3)); // undefined
var f = (x) => ({a:x});
console.log(f(3)); // a : 3
嵌套函數;
部署管道機制;
改寫λ演算;
6. 雙冒號運算符(提案)
用來取代bind
, apply
, call
的作用,綁定對象作用域;
// 將左邊對象,作為上下文環境(即this 對象),綁定到右邊的函數上
object::function
// 雙冒號左邊為空,右邊為一個對象的方法,代表將此方法綁定到該對象上;
var method = obj::obj.foo;
// 等同於
var method = ::obj.foo;
7. 尾調用優化
尾調用(Tail Call)函數的最後一步是調用另一個函數。
// 這種情況不屬於尾調用,相當於調用g(x)後返回undefined;‘return g(x)’算是一種尾調用。
function f(x){
g(x);
}
在函數內部調用其他函數時,所有當前被調用而未結束的函數的調用幀會形成調用棧;
尾調用由於是最後一步操作,所以不需要保留外部函數的調用幀,因為調用位置、內部變量等信息都不會再用到。
尾調用優化,即只保留最後調用函數的調用幀。但是需要做到內部調用函數不再使用外部函數的內部變量;
函數尾調用自身,即尾遞歸;
//尾調用 Fibonacci數列案例
function fib(n) {
if (n<=1) return 1;
return fib(n-1)+fib(n-2);
}
function fib1(n, ac1=1, ac2=1) {
if (n<=1) return ac2;
return fib1(n-1, ac2, ac1+ac2);
}
console.log(fib(10)); //~20ms 是優化函數運行時間至少10倍;
console.log(fib1(10)); //~1ms
console.log(fib(100)); // 無法計算,棧溢出
console.log(fib1(100)); // <10ms
尾遞歸優化只能在嚴格模式下開啟,正常模式無法執行;
尾遞歸優化在非嚴格模式下無效;實現方法可以使用循環代替:
// 非嚴格模式下實現尾調用優化--循環
// 遞歸函數
function test(x, y) {
if (y > 0) {
return test(x+1, y-1);
} else {
return x;
}
}
// 蹦床函數
function trampoline(fn) {
while (fn && fn instanceof Function) {
fn = fn();
}
return fn;
}
//改寫遞歸函數
function test1(x, y) {
if (y > 0) {
return test1.bind(null, x+1, y-1);
} else {
return x;
}
}
// 通過蹦床函數調用遞歸函數
var a1 = trampoline(test1(1, 100000));
console.log(a1); // 100001
8. 函數參數的尾逗號
ECMA2017允許函數的最後一個參數後面有逗號;
附加個人隨記:bind方法
bind
可以直接指定綁定的對象,以及對應的參數,返回綁定後的函數,後面加括號才會執行;
function testbind(str) {
return str + this;
}
var a1 = {name:"Bai"};
console.log(testbind.bind(a1));
// ? testbind(str) {
// return str + this;
// }
console.log(testbind.bind(a1,"huahua"));
// ? testbind(str) {
// return str + this;
// }
console.log(testbind()); //undefined[object Window]
console.log(testbind.bind(a1,"huahua")()); //huahua[object Object]
console.log(testbind.bind(a1)("huahua")); //huahua[object Object]
ES6隨筆--各數據類型的擴展(3)--函數