ES6新技术之函 数技术
积累了相当的ES5函数技术的使用经验,ES6也对函数对象技术作了多处的使用上的改进,让其更加的灵活方便。
本文部分内容择译自《Understanding ECMAScript 6 》。
参数
第一大改进是函数的参数的使用,包括:参数的默认值,余参和实参的展开操作。
形式参数的默认值
第一,支持定义形式参数的默认值,这样ES6函数在「参数值」上提供了灵活性,节省函数定义时的手动(extra code)检查参数值的存在与否;
function makeRequest(url, timeout = 2000, callback = function() {}) {
// the rest of the function
}
// uses default timeout and callback
makeRequest("/foo");
// uses default callback
makeRequest("/foo", 500);
// doesn't use defaults
makeRequest("/foo", 500, function(body) {
doSomething(body);
});
余参
第二,提供的余参(Rest parameters )新语法,这样新函数在「参数个数」(number of parameters)上提供了灵活性,一种比ES5 arguments更合理更灵活的方式。余参语法(...restPara)自动将调用函数时提供无名(无形式定义)的参数「收集」到一起,组成一个参数数组。
function pick(object, ...keys) {
let result = Object.create(null);
// 循环不用担心边界,不像arguments包含了所有参数,你循环不希望有的参数
for (let i = 0, len = keys.length; i < len; i++) {
result[keys[i]] = object[keys[i]];
}
return result;
}
ES6余参相对ES5 arguments对象,ES5 arguments 对形式参数是盲的,不知道函数参数具体定义;而ES6余参明确是“余下的参数”,有一部分参数(余下之外的参数)是明确的。
余参有两项限制:第一,每个函数只有一个余参,且必位于尾部;第二,不用于 对象的setter ,因为setter 只能有一个参数。
参数的默认值(默认参数)让函数可以用「比定义更少的参数」使用函数,而余参则相反,让函数可用「比定义更多的参数」使用函数。
实际参数的展开操作符
函数参数的第三项改进,是在调用(处理数组元素的)函数时,可使用展开操作符(spread operator ),展开「数组型实参」为多个形式参数,简化函数的编程任务;
如果你有一个「处理某个数组的函数方法」——形式参数是该数组的数组项(例如Math.max(25, 50, 75, 100))——时,现在你不必预先手动拆开数组一个一个的传(或改用数组形参,在函数内重新处理),或者用fn.apply()技巧,只需要在调用的时候使用实参操作展开操作符,系统为自动为你拆开数组,再调用。
let values = [25, 50, 75, 100]
// equivalent to
// console.log(Math.max(25, 50, 75, 100));
console.log(Math.max(...values)); // 100
这里展开操作符有一些使用上假定,假定第一,一个有多个数组元素形式参数的函数方法;第二,展开操作只用于实参展开,这个与形式余参容易混淆,因为语法上都是三点(...);
展开操作符除了用于函数实参的展开调用,还可用以数组创建(数组字面量上子数组的展开),和对象创建(子属性展开)。
const x = ['a', 'b'];
const y = ['c'];
const z = ['d', 'e'];
const arr = [...x, ...y, ...z]; //['a', 'b', 'c', 'd', 'e']
新形式
第二大改进是函数形式的本身,主要是箭头函数,还有块级函数等。
箭头函数
ECMAScript 6 中函数的最大变化是增加了箭头函数,一种新函数形式/形态。箭头函数旨在代替匿名函数表达式。箭头函数具有更简洁的语法、自动的词法"this"绑定,并且没有"arguments"对象。此外,箭头函数不能更改其"this"绑定,因此不能用作构造函数。
块内函数
ECMAScript 6 支持块级(局部分支)的函数定义,这个行为以ES5以前是非法的(严格模式下会报语法错误),因为看似不合理。然而如果某个流程分支如果足够复杂,能抽象出通用代码作函数,也是行得通的,合理的。
"use strict";
if (true) {
console.log(typeof doSomething); // throws an error
let doSomething = function () {
// empty
}
doSomething();
}
console.log(typeof doSomething);
元属性
fn.name属性用于调试追踪
var doSomething = function doSomethingElse() {
// empty
};
var person = {
get firstName() {
return "Nicholas"
},
sayName: function() {
console.log(this.name);
}
}
console.log(doSomething.name); // "doSomethingElse"
console.log(person.sayName.name); // "sayName"
console.log(person.firstName.name); // "get firstName"
new.target
JS函数有双重角色——普通函数和对象构造函数。ES5之前,没有严格限定开发者将一个普通将一个普通错当构造函数的行为。只能通过instanceof 勉强规避这种行为。现在ES6 提供了函数内部属性 切底规避这种行为。
function Person(name) {
if (typeof new.target !== "undefined") {
this.name = name;
} else {
throw new Error("You must use new with Person.")
}
}
var person = new Person("Nicholas");
var notAPerson = Person.call(person, "Michael"); // error!
使用new 操作符时函数的执行情况
JavaScript 对函数内部有两种不同的内部方法:[[Call]] 和[[Construct]]。当调用没有 new 的函数时,将执行[[Call]] 方法,该方法直接执行函数体代码。
而当使用 new 调用函数时,则调用[[Construct]]方法。[[Construct]]方法先创建一个名为实例的新对象,然后绑定函数体"this",再执行函数体。
具有 [[Construct]]方法的函数称为构造函数。箭头函数只有[[Call]] ,不能用作对象构造函数。
尾调优化
Tail call optimization allows some function calls to be optimized to maintain a smaller call stack, use less memory, and prevent stack overflow errors.
This optimization is applied by the engine automatically when it is safe to do so;
however, you might decide to rewrite recursive functions to take advantage of this optimization.
尾调用优化允许优化某些函数调用,以维护较小的调用堆栈,使用更少的内存,并防止堆栈溢出错误。
当引擎安全时,自动应用此优化;
但是,您可能决定重写递归函数以利用此优化。
参考
http://blog.poetries.top/js-knowledge-note/#/note/basis/func?id=%E5%B0%BE%E9%80%92%E5%BD%92