ES6学习04 函数新特性

1.允许设置默认值

1.1.形参允许设置默认值

ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

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

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

参数变量是默认声明的,所以在函数体中不能用let或const再次声明,不然会报错。
定义了默认参数值时,函数不能有同名参数(包括没有设置默认参数的)。否则报错

// 不报错
function foo(x, x, y) {
  // ...
}

// 报错
function foo(x, x, y = 1) {
  // ...
}
// SyntaxError: Duplicate parameter name not allowed in this context

还有一点,参数的默认值,不会定义时直接传值,而是每次调用函数时重新计算默认参数的值。(惰性求值)

let x = 99;
function foo(p = x + 1) {
  console.log(p);
}
foo() // 100
x = 100;
foo() // 101

上面代码中,参数p的默认值是x + 1。这时,每次调用函数foo,都会重新计算x + 1,而不是默认p等于 100。

1.2.与赋值解构默认值结合使用

function foo({x = 6, y = 5, z}) {
  console.log(x, y);
}

foo({}) // 6 5 undefind
foo({x: 1}) // 1 5 undefind
foo({x: 1, y: 2}) // 1 2 undefind
foo() // TypeError: Cannot read property 'x' of undefined

上面代码只使用了对象的解构赋值默认值,没有使用函数参数的默认值。只有当函数foo的参数是一个对象时,变量x和y才会通过解构赋值生成。如果函数foo调用时没提供参数,变量x和y就不会生成,从而报错。通过提供函数参数的默认值,就可以避免这种情况。

function foo({x, y = 5} = {}) {
  console.log(x, y);
}

foo() // undefined 5

上述代码,函数执行时若没有传参,将默认为一个空对象

function fetch(url, { body = '', method = 'GET', headers = {} }) {
  console.log(method);
}

fetch('http://example.com', {})
// "GET"

fetch('http://example.com')
// 报错

上述代码,第二个参数是个对象,当调用函数传参时,不传参数则报错。这段例子,也与上面的例子很相似。都表明了当参数是对象时,并且对象没有设置默认值,当执行函数没有传参,便会报错。

// 写法一
function m1({x = 0, y = 0} = {}) {
  return [x, y];
}

// 写法二
function m2({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}

// 函数没有参数的情况
m1() // [0, 0]
m2() // [0, 0]

// x 和 y 都有值的情况
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]

// x 有值,y 无值的情况
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]

// x 和 y 都无值的情况
m1({}) // [0, 0];
m2({}) // [undefined, undefined]

m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]

上面的两种方法对函数的参数都设置了默认值,区别是写法一设置了函数赋值解构的默认值,又让参数默认值为一个空对象。
写法二则没有设置函数赋值解构的默认值,又让参数等于了一个带有具体属性(键值对)的对象

1.3.参数默认值的位置

通常函数默认值,应该是函数的尾部参数,这样会比较容易看出来

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

f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 报错
f(undefined, 1) // [1, 1]

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

f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 报错
f(1, undefined, 2) // [1, 5, 2]

上面代码很好的展示了设置了默认值的参数,在使用函数参数时是不可以省略的。
传undefined会触发默认值,传其他值时不会触发。

1.4.设置了参数默认值函数的作用域

若设置了函数的默认值,函数进行声明初始化时,函数会形成一个单独的作用域,在初始化之后,这个作用域就会消失

var x = 1;

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

f(2) // 2

上述代码,因为 调用函数时需要声明初始化,这时函数是一个单独的作用域,所以这时 y = x 中的 x 指向同一作用域的 x(第一个参数)
注:这种语法行为,在没有参数默认值时是不会生效的

let x = 1;

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

f() // 1

函数运行时 函数体内部也是一个单独的作用域,而 参数中的 x 找不到内部的,所以只能向外层寻找,所以输出的 1,函数体内部的 x 无法影响到默认值变量 x 。
若此时不定义全局变量 x 就会报错

若定义的 let 声明去拿前面的值,页面也会报错

var x = 1;
function foo(x = x) {
  // ...
}
foo() // ReferenceError: x is not defined

上面的代码中,参数(x = x)会形成的单独作用域,实际执行的是let x = x ,因为 let 声明的暂时性死区,导致报错。

默认值设置为函数

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

bar() // ReferenceError: foo is not defined

上面代码中,匿名函数里面的 foo 指向函数外层,但是函数外层并没有声明变量foo,所以就报错了。
其实就是参数与函数体中的代码块是两个不同的作用域,所以参数的函数中的 foo 只能向外找,找不到就报错了。

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

foo() // 2
x // 1

如果将 foo 函数体内的 var x = 3 中的 var 去除,那么 y( ) 运行时 ,函数中的 x = 2 会重新赋值给 foo 函数体中的 x = 3,这是函数体中的 x = 2,而全局的 x 不变。

在函数作用域中,不使用变量关键字声明的变量,在赋值时会往上一级作用域寻找已经声明的同名变量,直到全局作用域时还没找到,则会成为window的属性

1.5.设置函数默认值的实际应用

利用默认值,可以指定摸一个参数不得省略,如果省略就抛出错误

function throwIfMissing() {
  throw new Error('此参数不得省略');
}

function foo(mustBeProvided = throwIfMissing()) {
  return mustBeProvided;
}

foo()
// Error: Missing parameter

上面代码的foo函数,如果调用的时候没有参数,就会调用默认值throwIfMissing函数,从而抛出一个错误。从上面也可以了解到,在设置默认值为函数时,后面跟了一个括号,所以默认值为函数时,是在运行时才能执行,在定义时不会因为有括号而执行

另外,可以将参数默认值设为undefined,表明这个参数是可以省略的。

function foo( a = undefined) { ··· }

1.6.函数的length属性

在函数定义了默认值后,函数的length属性,将会返回没有指定默认值的个数。也就是说,指定了默认值后,length属性会失真

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

length属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。同理,后文的 rest 参数也不会计入length属性。使用了标签模板传参时,{ }中变量length属性也会失效。

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

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

2.rest参数

2.1rest参数的使用

ES6规定了 rest参数,它用于获取函数的多余参数,多于的参数以数组形式储存,这样可以不需要arguments对象了

function add(...values) {
  let sum = 0;

  for (var val of values) {
    sum += val;
  }

  return sum;
}

add(2, 5, 3) // 10

上面代码为一个求和函数,add中的形式参数(…values)为 rest参数的形式,可以向函数中传入任意数量的参数

下面是一个 rest 参数代替arguments变量的例子。

// arguments变量的写法
function sortNumbers() {
  return Array.prototype.slice.call(arguments).sort();
}

// rest参数的写法
const sortNumbers = (...numbers) => numbers.sort();

JavaScript中的Array.prototype.slice.call(arguments)能将有length属性的对象转换为数组(特别注意:
这个对象一定要有length属性).
但有一个例外,IE下的节点集合它不能转换(因为IE下的dom对象是以com对象的形式实现,js对象和com对象不能进行转换)
方法详情请点击此处

注意:

  1. rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
  2. 函数的length属性,不包括 rest 参数。

3.函数的严格模式

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

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

ES6规定:只要函数中使用了 默认值、赋值解构、或是扩展运算符,那么就不能设为严格模式,会报错。

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

这样规定的原因是,函数内部的严格模式,同时适用于函数体和函数参数。但是,函数执行的时候,先执行函数参数,然后再执行函数体。这样就有一个不合理的地方,只有从函数体之中,才能知道参数是否应该以严格模式执行,但是参数却应该先于函数体执行。
所以导致了,若是函数参数中在严格模式下会报错,参数会先执行,然后在函数体中发现 ‘use strict’ 时,才导致报错。
解决方法:
1.在全局设置严格模式,这样会导致全局的代码都在严格模式下运行
2.把函数包在一个无参数的立即执行函数里面,在外层的函数设置严格模式。

4.函数的name属性

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

function foo() {}
foo.name // "foo"
//这个属性早就被浏览器广泛支持,但是直到 ES6,才将其写入了标准。
var f = function () {};

// ES5
f.name // ""

// ES6
f.name // "f"

上面代码中,变量f等于一个匿名函数,ES5 和 ES6 的name属性返回的值不一样。

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

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

(new Function).name // "anonymous"	

5.箭头函数

内容较多详情请点击此处

6.更多新特性

还有一些新特性没有列出可,可以在《ECMAScript 6 入门》中查看
以上内容参考于 阮一峰老师 的《ECMAScript 6 入门》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值