《深入理解ES6》第三章 函数

函数

《深入理解ES6》—— Nicholas C. Zakas

在ES5函数之上,进行增量改进。

1. 参数默认值

参数默认值 让函数在接收到的参数数量不足时行为更清晰。

1.1. JS函数的特点

调用时可接受任意数量的参数,而无视函数声明处的参数数量。

1.2. ES5中模拟

利用逻辑或运算符(||),当左侧的值为假时,返回右侧的操作数。

function makeRequest( url, timeout, calback ) {
    // 方式一
    timeout = timeout || 3000;

    // 方式二 
    timeout = ( typeof timeout !== "undefined" ) ?
                timeout : 3000;  
}

方式一:如果传给 timeout 的值是 0,则会出错。

方式二:公共模式。

1.3. ES6中的参数默认值

未给参数传值,才会使用默认值。

给参数传值了(就算是 nullundefined),则不使用默认值。

function makeRequest( url, timeout = 3000, calback ) {

}

参数默认值是一个表达式,这意味着,可以是一个函数调用

function getValue() {
    return Math.random();
}
function add( first, second = getValue() ) {
    return first + second;
}

2. 剩余参数

说明

解决传入的参数过多的情况,替换 arguments

语法

三个点(...)与一个紧跟着的具名参数组成,是数组类型。

示例

function foo( name, ...args ) {
    // ...
}

限制

  • 一个函数只能有一个剩余参数,且必须放在最后
  • 不能在 setter 中使用

3. 扩展运算符

语法

在数组前面加 ...

说明

避免使用apply()混淆代码的真实意图。

JS引擎会将数组分割为独立参数传递进去。

示例

let values = [1, 2, 3, 5, 4];

// 以前
Math.max.apply( Math, values ); //=> 5
// 现在
Math.max(...values); //=> 5
Math.max(...values, 10); //=> 10

4. 函数的名称属性

ES6给所有函数添加了 name 属性,以解决各种函数定义方式造成调试困难的问题。

示例:

function fnA() {};
fnA.name; //=> "fnA"

let fnB = function () {};
fnB.name; //=> "fnB"

// setter函数 会有 "set" 前缀
// bind()创建的函数 会有 "bound" 前缀
// Function 构造器创建的函数 会有 "anonymous" 前缀

5. 明确函数的用途

用途

  • 普通函数,用于调用
  • 构造函数,创建对象

new

函数根据是否使用 new 来调用,而有双重用途。

当用new来调用时,函数内部的this是一个新对象,并作为函数的返回值。

区分

通过首字母是否大写来区分是否用new来调用,如 PersongetPersonName

机制

JS为函数提供了两个不同的内部方法:

  • [[call]]:当函数未使用new调用时,[[call]]会被执行,运行的是函数体里的代码。
  • [[constructor]]:当使用new调用时,[[constructor]]会被执行,负责创建一个新对象,并将新最新作为this去执行函数体

ES6

新增 new.target 从而消除函数调用的不确定性。

function Person( name ) {
    if ( typeof new.target !== "undefined" ) {
        this.name = name; // 使用 new 调用
    }
}

6. 块级函数

在ES3中,在代码块中声明函数(即块级函数)严格来说是一个语法错误,
但所有的浏览器却都支持该语法。

但支持该语法的浏览器都有轻微的行为差异,所以最佳实践就是不要在代码块中声明函数(而是使用函数表达式)。

ES6标准化这种行为来消除不兼容性。

if( true ) {
    typeof doSomething; //=> "function"

    function doSomething() {

    }

    doSomething();
}

ES6 会将 doSomething() 函数视为块级声明,
并允许它在定义所在的代码块内部被访问。

块级函数会被提升到定义所在代码块的顶部,
一旦if执行完毕,doSomething()就会被移除。

使用 let 的函数表达式不会造成提升。

7. 箭头函数

7.1. 与传统JS函数的比较

  • 没有 thissuperargumentsnew.target 绑定
  • 不能使用 new 来调用:没有[[Construct]]方法,不能作为构造函数
  • 没有原型:不能使用new,就不需要原型了
  • 不能更改this:在函数的整个生命周期内,其值保持不变
  • 没有arguments对象:依赖具名参数和剩余参数来访问函数的参数

在JS编程中,this 绑定是发生错误的常见根源之一,
在嵌套函数中会因为调用方式的不同,而导致丢失对外层 this 值的追踪,
就可能导致预期外的程序行为。

其次,箭头函数使用单一的 this 值来执行代码,
使得JS引擎可以更容易对代码的操作进行优化;
而常规函数可能会作为构造函数使用(导致this易变而不利于优化)。

其余差异也是聚集在减少箭头函数内部的错误与不确定性,
这样JS引擎也能更好地优化箭头函数的运行。

7.2. 语法

参数 => 函数体

7.2.1. 参数

当只有一个参数时,包裹参数列表的括号()可以省略。

// 无参数
() => 函数体

// 一个参数
(arg1) => 函数体
arg1 => 函数体

// 多个参数
(arg1, arg2) => 函数体
7.2.2. 函数体

只有一个表达式,且以该表达式作为返回值是,函数体不需要{}包裹。

arg1 => arg1 * 2;
(arg1, arg2) => arg1 + arg2;
(arg1, arg2) => (arg1 + arg2);
// 返回对象字面量
() => ( {name: 1, gender: 0} ); 

arg1 => {
    return arg1 * 3;
}

7.3. 没有 this 绑定

JS最常见的错误领域之一就是函数内的this绑定。

一个函数内部的this值可以被改变,这取决于调用该函数时的上下文。

箭头函数没有 this 绑定,
意味着箭头函数内部的this值只能通过查找作用域链来确定。

如果箭头函数在一个非箭头函数的函数体内,
那么 this 值就会与该函数的相等。

箭头函数的 this 值由包含它的函数决定。

7.4. 没有 arguments 绑定

尽管箭头函数没有自己的 arguments 对象,
但仍然能访问包含它的函数的 arguments 对象。

7.5. 识别

let f = () => {};

typeof f; //=> "function"
f instanceof Function; //=> true

8. 尾调用优化

通常用于递归,会带来显著的性能提升。

但闭包不能被优化,这也是为什么避免使用闭包的原因。

9. 总结

函数在ES6中并未经历巨大变化,但一系列增量改进使得函数更易于使用。

在特定参数未被传入时,函数的默认参数允许你更容易指定需要使用的值。

剩余参数允许你将余下的所有参数放入指定数组。
使用真正的数组并让你指定哪些参数需要被包含,
使得剩余参数成为比 arguments 更为灵活的解决方案。

扩展运算符是剩余参数的好伙伴,允许在调用函数时将数组解构为分离的参数。

新增的 name 属性能帮你在调试与执行方面更容易得识别函数。

函数的行为被[[Call]][[Construct]]方法所定义,
前者对应普通的函数执行,二者对应使用了new的调用。
new.target元属性能在函数体内判断该函数调用时是否使用了new

ES6函数的最大变化就是增加了箭头函数。
箭头函数被设计用于替代匿名函数表达式,
它拥有更简洁的语法、词法级的this绑定,并且没有 arguments 对象。
不能改变this绑定的值,因此不能作为构造器。

尾调用优化允许某些函数调用被优化,以保持更小的调用栈、使用更少的内存,
并防止堆栈溢出。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值