ES6 函数的扩展

函数参数的默认值

function foo(x = 5) {
    let x = 1 // error
    const x = 2 // error
}

函数变量是默认声明的,在函数体内不能使用let或const再次声明,否则或报错。

解构与默认值结合使用

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(); //报错
    

上面的代码使用了对象的解构赋值默认值,而没有使用函数参数的默认值。只有当函数foo的参数是一个对象时,变量x和y才会通过解构赋值而生成,否则就会报错。

双重默认值

function foo(url, { method = 'GET'} = {}) {
    console.log(method)
};

foo('http://www.baidu.com')
// "GET"

当函数没有传入第二个参数时,函数默认值生效。之后对象的解构赋值生效,变量method取得默认值。

// 函数参数的默认值是空对象,但设置了对象解构赋值的默认值
function m1({x = 0, y = 0} = {}) {
    return [x, y]
};

// 函数参数的默认值是一个有具体属性的对象,但没有设置对象解构赋值的默认值
function m2({x, y} = {x: 0, y: 0}) {
    return [x, y]
};

函数默认值的位置应该在函数的尾部,否则参数无法省略。

函数的length属性,返回没有指定默认值的参数个数。(如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数,总之就是很鸡肋

作用域

一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域。等到初始化结束,这个作用域会消失。这种语法是在不设置函数参数默认值时是不会出现的。

var x = 1;

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

f(2); // 2

如果在作用域里,变量并未定义,则会指向外层的全局变量,函数体内部的局部变量影响不到默认变量。

如果参数的默认值是一个函数,该函数的作用域也遵守这个规则。

let x = 1;

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

f() // 1

如果此时,全局变量x不存在,就会报错。

var x = 1;

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

foo(); // 3
x // 1

// 函数foo的参数形成一个单独的作用域,变量y的匿名函数内部的变量x指向同于作用域下的第一个参数x,而函数内部又声明了一个变量x,console的变量为函数内部声明的变量,调用匿名函数改变参数x的值,全局变量x从未改变

var x = 1;

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

foo() // 2
x // 1

// 函数内部变量x指向第一个参数x,与匿名函数内部x是一致的。

rest参数

rest参数是一个数组,该变量将多余的参数放入其中。

function add(...value) {
    let sun = 0
    
    for(var val of value) {
        sun += val
    }

    reutrn sun
}

add(2, 5, 3) // 10

rest参数之后不能有其他参数,否则会报错。

函数的length属性不包括rest参数。

只要函数参数使用了默认值、解构赋值或者扩展运算符,则不可在函数内部显示设定为严格模式,否则就会报错。

name属性

函数的name属性返回该函数的函数名。

箭头函数

ES6 允许使用箭头( => )定义函数

var f = v => v;

// 等同于
var f = function (v) {
    return v
}

// 如果箭头函数不需要参数或需要多个参数,就使用圆括号代表参数部分

() => 5;
(num1, num2) => num1 + num2;

// 如果箭头函数的代码块部分多与=于一条语句,则需要使用大括号将其扩起来,并使用return语句返回
// 由于大括号被解释为代码块,所以如果箭头函数要返回对象,需在外面加上括号

箭头函数可以与变量解构结合使用。

注意事项

  • 函数体内的this对象就是定义时所在的对象,而不是使用时所在的对象
  • 不可以当作构造函数(不可以使用new命令,否则会报错)
  • 不可以使用arguments对象,该对象在函数体内不存在
  • 不可以使用yield命令(箭头函数不可以用作generator函数)

箭头函数根本没有自己的this,它的this指向外层代码块的this

箭头函数无法使用call(),apply(),bind()这些方法起改变this的指向

箭头函数内部还可以使用箭头函数。

绑定this

函数绑定运算符是并排的双冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象作为上下文环境(即this对象)绑定到右边的函数上。

foo :: bar;
// 等同于
bar.bind(foo)

如果双冒号的左边为空,右边是一个对象的方法,则等于将该方法绑定在该对象上。

var method = obj::obj.foo;
// 等同于
var method = ::obj.foo

尾调用优化

某个函数的最后一步是调用另一个函数

function f(x) {
    return g(x)
}

函数调用会在内存中形成一个“调用记录”(调用帧),保存调用的位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用帧上方还会形成一个B的调用帧。等B运行结束,将结果返回给A,B的调用帧才会消失。如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推。所有的调用帧就形成一个调用栈。

尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,直接用内层函数的调用帧取代外层函数的调用帧即可。

只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则无法进行“尾调用优化”

尾递归

函数调用自身称为递归,如果尾调用自身称为尾递归。

// 递归
function a(n) {
    if(n === 1) return 1
    return n * a(n - 1)
}

// 尾递归
function a(n, m) {
    if( n === 1) return m
    return a(n-1, n*m)
}

尾递归的改写

把所有要用到的内部变量改写成参数

严格模式

ES6 的尾调用优化只在严格模式下开启,正常模式下是无效的。

因为正常模式下函数内部有两个变量,可以跟踪函数的调用栈:

  • func.arguments:返回调用时函数的参数
  • func.caller:返回调用当前函数的函数

尾调用优化时,函数的调用栈会改写,因此上面两个变量就会失真。严格模式禁用这两个变量,所以尾调用模式仅在严格模式下生效。

尾递归优化的实现

那么普通模式中就无法使用尾递归调用了吗?当然不是,我们可以使用一种曲线救国的政策 —— 蹦床函数

将递归执行转为循环执行

function a(f) {
    while ( f && f instanceof Function) {
        f = f()    
    }
    return f
}

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ES6 引入了一些数值扩展,其中包括了新的数值类型(如二进制和八进制)、新的数学方法和常量。 1. 二进制和八进制 在 ES6 中,我们可以使用 `0b` 或 `0B` 前缀来表示二进制数,使用 `0o` 或 `0O` 前缀来表示八进制数。例如: ```javascript let binary = 0b1010; // 二进制的 10 let octal = 0o777; // 八进制的 511 ``` 2. Number.isFinite() `Number.isFinite()` 方法用于检查一个数值是否为有限数。与全局的 `isFinite()` 函数不同的是,`Number.isFinite()` 不会将非数值转换为数值。例如: ```javascript Number.isFinite(Infinity); // false Number.isFinite(-Infinity); // false Number.isFinite(NaN); // false Number.isFinite(123); // true ``` 3. Number.isNaN() `Number.isNaN()` 方法用于检查一个数值是否为 NaN。与全局的 `isNaN()` 函数不同的是,`Number.isNaN()` 只有在参数是数值且等于 NaN 时才返回 true。例如: ```javascript Number.isNaN(NaN); // true Number.isNaN('hello'); // false Number.isNaN(123); // false ``` 4. Number.parseInt() `Number.parseInt()` 是 `parseInt()` 的一个新的方法,它将字符串转换为整数,并且只接受数字作为字符串的参数。例如: ```javascript Number.parseInt('123'); // 123 Number.parseInt('123.45'); // 123 Number.parseInt('hello'); // NaN ``` 5. Number.parseFloat() `Number.parseFloat()` 是 `parseFloat()` 的一个新的方法,它将字符串转换为浮点数,并且只接受数字作为字符串的参数。例如: ```javascript Number.parseFloat('3.14'); // 3.14 Number.parseFloat('3.14hello'); // 3.14 Number.parseFloat('hello'); // NaN ``` 6. Number.MAX_SAFE_INTEGER 和 Number.MIN_SAFE_INTEGER `Number.MAX_SAFE_INTEGER` 和 `Number.MIN_SAFE_INTEGER` 属性分别表示 JavaScript 中最大的安全整数和最小的安全整数。例如: ```javascript Number.MAX_SAFE_INTEGER; // 9007199254740991 Number.MIN_SAFE_INTEGER; // -9007199254740991 ``` 7. Number.isSafeInteger() `Number.isSafeInteger()` 方法用于检查一个数值是否为安全整数。安全整数是指符合以下两个条件的整数: - 在 JavaScript 中能够精确表示。 - 绝对值不大于 `Number.MAX_SAFE_INTEGER`。 例如: ```javascript Number.isSafeInteger(123); // true Number.isSafeInteger(9007199254740992); // false,超出了安全整数范围 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值