ES6函数扩展, 箭头函数, 函数参数默认值作用域

ES6函数的扩展

1.0 函数参数的默认值

ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。

function log(x, y) {
  y = y || 'World';
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World

**ES6之后可以直接在函数的参数中指定默认值, 如下代码演示: **

function f(x, y = 5, z) {
  return [x, y, z];
}

函数的 length 属性

指定了默认值以后, 函数的 length 属性, 将返回没有指定默认值的参数个数. 也就是说, 指定了默认值之后, length 属性将是真.

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2

上述代码中 length属性的返回值, 等于函数的参数个数减去指定了默认值的参数个数. 比如, 上面最后一个函数, 定义了3个参数, 其中有一个参数 c 指定了默认值, 因此length 属性等于 3减去 1, 最后得到 2.

这是因为 length 属性的含义是, 该函数预期传入的参数个数. 某个参数指定默认值以后, 预期传入的参数个数就不包括这个参数了. 同理, 马上即将提到的 rest 参数也不会计入 length 属性.

(function(...args) {}).length // 0

如果设置了默认值的参数不是尾参数, 那么 length 属性也不再计入后面的参数了

(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1

1.1.1 函数参数默认值 作用域

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

var 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. 因为 函数体内部的局部变量 x 是一个单独的作用域, 参数部分的变量 x 也是一个单独的作用域, 它们是两个不同的作用域.

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

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

f() // ReferenceError: x is not defined

1.1.2 函数参数默认值作用域容易引起报错问题

下面这样写, 也会报错

var x = 1;

function foo(x = x) {
  // ...
}

foo() // ReferenceError: x is not defined

上面代码中, 参数 x = x 形成一个单独的作用域. 实际执行的是 let x = x, 由于暂时性死区的原因, 这行代码会报错 “x未定义”.

如果实在不理解上述问题, 可以和下面例子对比理解:

var x = 1;
var y = 2;
function foo(x = y) {
    console.log(x); // 2
}
function bar(x = x){ //  x is not defined
    console.log(x);
}
foo()
bar()

对比:

对于 foo 函数, 函数默认值是 x=y, 实际上执行的是 let x = y, 而用 let 声明的变量 x 存在暂时性死区, 但是 变量 y 并没有暂时性死区, 所以 变量y 可以向外查找到全局作用域中的 变量y.

但是

对于 bar 函数, 函数默认值是 x=x, 实际上执行的是 let x = x, 而用 let声明的变量 x 存在暂时性死区, 所以将 等号右边的 x 赋值给等号左边的x 的时候, 由于 x 的暂时性死区的问题, 会导致出现 x is not defined 的 未定义问题.

如果函数的默认值仍然是一个函数, 该函数的作用域也遵守这个规则, 如下所示

let foo = 'outer';

function bar(func = () => foo) {
  let foo = 'inner';
  console.log(func());
}

bar(); // outer

上面代码中,函数bar的参数func的默认值是一个匿名函数,返回值为变量foo。函数参数形成的单独作用域里面,并没有定义变量foo,所以foo指向外层的全局变量foo,因此输出outer。 如果全局中没有 foo变量, 则会报错.

2.0 rest参数

ES6引入 rest参数 (形式为 ...变量名), 用于获取函数的多余参数, 这样就不需要使用 arguments 对象了. rest参数搭配的变量是一个数组, 该变量将多余的参数放入数组中.

function sum(...values) {
    console.log(values); // rest参数搭配的变量values 是一个数组, 该变量 values 将多余的参数放入数组中. [ 1, 2, 3 ]
    let sum = 0;
    for (var val of values) {
        sum += val;
    }
    return sum
}
console.log(sum(1, 2, 3)); // 6

上面代码的add函数是一个求和函数,利用 rest 参数,可以向该函数传入任意个数的参数。

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

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

3.0 严格模式

从ES5开始, 函数内部可以设定严格模式.

function doSomething(a, b) {
  'use strict';
  // code
}

ES2016 做了一点修改,规定只要函数参数使用了,那么函数内部就不能显式设定为严格模式,否则会报错

// 报错
function doSomething(a, b = a) {
  'use strict';
  // code
}

// 报错
const doSomething = function ({a, b}) {
  'use strict';
  // code
};

// 报错
const doSomething = (...a) => {
  'use strict';
  // code
};

const obj = {
  // 报错
  doSomething({a, b}) {
    'use strict';
    // code
  }
};

4.0 函数的name属性

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

function foo() {}
foo.name // "foo"

如果将一个匿名函数赋值给一个变量,ES5 的name属性,会返回空字符串,而 ES6 的name属性会返回实际的函数名。

var f = function () {};

// ES5
f.name // ""

// ES6
f.name // "f"

如果将一个具名函数赋值给一个变量,则 ES5 和 ES6 的name属性都返回这个具名函数原本的名字

const bar = function baz() {};

// ES5
bar.name // "baz"

// ES6
bar.name // "baz"

Function构造函数返回的函数实例,name属性的值为anonymous

(new Function).name // "anonymous"

bind返回的函数,name属性值会加上bound前缀。

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

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

5.0 箭头函数

箭头函数比较常用, 我们这里不再做详细的解释案例, 只记录箭头函数的注意点

箭头函数使用注意点:

  1. 箭头函数体内的 this对象, 就是定义时所在的对象, 而不是使用时所在的对象.
  2. 箭头函数不可当做构造函数, 也就是说不可以使用 new命令, 否则会抛出一个错误.
  3. 箭头函数不可以使用 arguments 对象, 该对象在函数体内不存在, 如果要用 , 可以使用 rest 参数代替.
  4. 箭头函数不可以使用 yield 命令, 因此箭头函数不能作为 Generator 函数.

🏴 注意: 上面四点中, 第一点尤其值得注意. this 对象的指向是可变的, 但在箭头函数中它是固定的.

总结

交流学习添加微信(备注技术交流学习): Gene199302
在这里插入图片描述

上一章: ES6字符串扩展的相关方法

下一章: ES6数组扩展, Array.from()方法,数组实例的flat()方法

该博客为学习阮一峰 ES6入门课所做的笔记记录, 仅用来留作笔记记录和学习理解

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值