ES6(六: 函数扩展)(默认值,rest参数,扩展运算符)
来源:互联网 发布:iptv服务器软件 编辑:程序博客网 时间:2024/05/18 20:36
前言
想必在ES6没出现类语法之前,js中最重要的类型就是函数。无可争议,这就是一个大重点。
(一)函数参数的默认值
在ES6之前,我们的做法无非这几种
var func = function(a,b){ // 开 关 写法 var a = a || '默认a'; // 判 断 写法 if (typeof b === 'undefined') b = '默认b'; console.log(a+' and '+b);};func(); // 默认a and 默认bfunc('赋值a'); // 赋值a and 默认bfunc('赋值a',null); // 赋值a and nullfunc(null,'赋值b'); // 默认a and 赋值b
上面代码,a采用开关方式赋予初始化值(最常用方式),b采用判断方式。函数形参没传入实参就是undefined,但是注意null !== ‘undefined’。
还有一种方式,用过arguments的length属性,这也是重载的实现方案(ps: js不支持java那种方法重载)。
var func = function(){ // arguments写法 if (arguments.length === 1) { y = '默认b'; }}
(1)ES6允许为函数的参数设置默认值
即直接写在参数定义的后面。我们将上面例子改成ES6的版本。
const func = (a = '默认a',b = '默认b') =>{ console.log(a+' and '+b);};func(); // 默认a and 默认bfunc('赋值a'); // 赋值a and 默认bfunc('赋值a',null); // 赋值a and nullfunc(null,'赋值b'); // null and 赋值b
这里和上面第四句输入有点差别。
这是因为,定义了默认值的参数,应该是函数的尾参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。而且判断默认值是使用 undefined(所以建议默认参数都设置在尾部)。
我们看下babel的编译就知道为什么了。
var func = function func() { var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '默认a'; var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '默认b'; console.log(a + ' and ' + b);};
(2)与解构赋值默认值结合
常见用法中是配合解构赋值的默认值使用。使语法更加简洁直观。
举个例子
const fetch= (url, { body = '', method = 'GET', headers = {} }) =>{ console.log(method);};// GETfetch('http://example.com', {});// POSTfetch('http://example.com', {method: 'POST'});// TypeError: Cannot match against 'undefined' or 'null'.fetch('http://example.com');
上面代码,第一次调用,传入空对象,找不到method 默认值生效。第二次调用,传入method 变成传入值。但是第三次调用,因为第二个参数没传,就是undefined,导致解构不成立,所以报错。
这时就出现了双重默认值
const fetch= (url, { body = '', method = 'GET', headers = {} } = {}) =>{ console.log(method);};
上面代码,函数fetch 没有第二个参数时,函数参数的默认值就会生效,然后才是解构赋值的默认值生效,变量method 才会取到默认值GET 。
(3)函数的length属性
函数的length 属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后, length 属性将失真。
console.log((function(a,b){}).length); // 2console.log((function(a = 5){}).length); // 0console.log((function(a, b, c = 5){}).length); // 2console.log((function(...args){}).length); // 0
上面代码中, length 属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。
比如,上面倒数第二个函数,定义了3个参数,其中有一个参数c 指定了默认值,因此length 属性等于3减去1,最后得到2。
这是因为length 属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。
当然,最后一个 rest参数也不会计入length 属性。
(4)作用域
大家都知道,变量的作用域规则是,即先是当前函数的作用域,然后才是全局作用域。
如果参数默认值是一个变量,则该变量所处的函数作用域内,而不是全局作用域。
const x = 1;const f = (x, y = x) => { console.log(y);};f(2); // 2
参数y 的默认值等于x 。调用时,由于函数作用域内部的变量x 已经生成,所以y 等于参数x ,而不是全局变量x 。
当调用时,函数作用域内部的变量x 没有生成,结果就会不一样。
const func = (y = x) =>{ let x = 2; console.log(y);};func(); //1
上面代码中,函数调用时, y 的默认值变量x 尚未在函数内部生成,所以x 指向全局变量,结果又不一样。
注意,此时,如果全局变量x 不存在,就会报错( ReferenceError: x is not defined)。
如果函数A 的参数默认值是函数B
由于函数的作用域是其声明时所在的作用域,那么函数B 的作用域不是函数A ,而是全局作用域。
let foo = 'outer';function bar(func = x => foo) { let foo = 'inner'; console.log(func()); // outer}bar();
函数bar 的参数func ,默认是一个匿名函数,返回值为变量foo 。这个匿名函数的作用域就不是bar 。
这个匿名函数声明时,是处在外层作用域,所以内部的foo 指向函数体外的声明,输出outer 。它实际上等同于下面的代码。
let foo = 'outer';let f = x => foo;function bar(func = f) { let foo = 'inner'; console.log(func()); // outer}bar();
(二)rest 参数
ES6引入rest参数(形式为“…变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了。类似java的不定项参数(只能是方法的最后一个参数)。
rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
const func = (value1, ...values) => { let sum = 0; for (var val of values) { sum += val; } console.log(`value1= ${value1}`); // value1= first console.log(`sum= ${sum}`); // sum= 6};func('first',1,2,3);
上面代码的函数,利用rest参数,可以向该函数传入任意数目的参数。
注意:rest参数必须在最后面。否则会报错。
// SyntaxError: Rest parameter must be last formal parameter
(三)扩展运算符
扩展运算符(spread)是三个点(…)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。主要用于函数调用(react中展开数组)
function push(array, ...items) { array.push(...items);}function add(x, y) { return x + y;}var numbers = [4, 38];console.log(add(...numbers)); // 42
应用(1)替代数组的apply方法
由于扩展运算符可以展开数组,所以不再需要apply 方法,将数组转为函数的参数了。
// ES5的写法function func(x, y, z) { console.log(`${x}-${y}-${z}`);}var args = [0, 1, 2];// null undefined指向windowfunc.apply(null, args); // 0-1-2// ES6的写法const func1 = (x, y, z)=> { console.log(`${x}-${y}-${z};`);};let args1 = [0, 1, 2];func1(...args1); // 0-1-2;
上面代码是一个spread 替代的例子。这样我们也可以应用Math.max 和Math.min方法。
var arr = [14, 3, 77];// ES5的写法console.log(Math.max.apply(null, arr)); // 77console.log(Math.min.apply(null, arr)); // 3// ES6的写法console.log(Math.max(...arr)); // 77console.log(Math.min(...arr)); // 3
上面代码表示,由于JavaScript不提供求数组最大最小元素的函数,所以只能套用Math.max 函数,将数组转为一个参数序列,然后求最大值。
应用(2)将一个数组添加到另一个数组尾部
// ES5的写法var arr1 = [0, 1, 2];var arr2 = [3, 4, 5];Array.prototype.push.apply(arr1, arr2);console.log(arr1); // [ 0, 1, 2, 3, 4, 5 ]// ES6的写法const arr3 = [0, 1, 2];const arr4 = [3, 4, 5];arr3.push(...arr4);console.log(arr3); // [ 0, 1, 2, 3, 4, 5 ]
上面代码的ES5写法中, push 方法的参数不能是数组,所以只好通过apply 方法将数组变成单个使用push 方法。
有了扩展运算符,就可以直接将数组传入push 方法。
下面是一个自定义时间节点。
// ES5var data = new (Date.bind.apply(Date, [null, 2017, 10, 1]));console.log(data); // 2017-10-31T16:00:00.000Z// ES6const data1 = new Date(...[2017, 10, 1]);console.log(data1); // 2017-10-31T16:00:00.000Z
应用(3)合并数组
扩展运算符提供了数组合并的新写法。
var arr1 = ['a', 'b'];var arr2 = ['c'];// ES5var newArr = arr1.concat(arr2);console.log(newArr); // [ 'a', 'b', 'c' ]// ES6const newArr1 = [...arr1, ...arr2];console.log(newArr1); // [ 'a', 'b', 'c' ]
应用(4)与解构赋值结合
const [first, ...rest] = [1, 2, 3, 4, 5];console.log(first); // 1console.log(rest); // [2, 3, 4, 5]const [first1, ...rest1] = [];console.log(first1); // undefinedconsole.log(rest1); // []const [first2, ...rest2] = ["foo"];console.log(first2);// "foo"console.log(rest2); // []
上面代码,将扩展运算符用于数组赋值。但是需要注意,这里rest参数也只能放在最后一位,不然会报错。
应用(5)函数返回值
JavaScript的函数只能返回一个值,如果需要返回多个值,只能返回数组或对象。扩展运算符提供了解决这个问题的一种变通方法。
var dateFields = readDateFields(database);var d = new Date(...dateFields);
我尝试将readDateFields写成一个数组,然后模拟书上的API的返回值。
const dateFields = [2017, 10, 1];const d = new Date(...dateFields);console.log(d); // 2017-10-31T16:00:00.000Z
应用(6)字符串
扩展运算符还可以将字符串转为真正的数组。
var str = "hello";// ES5 的处理方式var chars=str.split("");console.log(chars);// [ 'h', 'e', 'l', 'l', 'o' ]// ES6 的处理方式const b = [...str];console.log(b); // [ "h", "e", "l", "l", "o" ]
应用(7)类数组对象
任何类似数组的对象,都可以用扩展运算符转为真正的数组。
var nodeList = document.querySelectorAll('p');var arr = [...nodeList];console.log(nodeList);console.log('------------');console.log(arr);
运行结果为:
上面截图可以看出,扩展运算符将类数组对象(NodeList),转化为了真正的数组(Array);
应用(8)Map,Set,Generator
扩展运算符内部调用的是数据结构的Iterator接口,因此只要具有Iterator接口的对象,都可以使用扩展运算符,比如Map结构。
let map = new Map([ [1, 'one'], [2, 'two'], [3, 'three']]);let arr = [...map.keys()];console.log(arr); // [ 1, 2, 3 ]
Generator函数运行后,返回一个遍历器对象,因此也可以使用扩展运算符。
var go = function*(){ yield 1; yield 2; yield 3;};const arr1 = [...go()];console.log(arr1); // [1, 2, 3]
上面代码中,变量go 是一个Generator函数,执行后返回的是一个遍历器对象,对这个遍历器对象执行扩展运算符,就会将内部遍历得到的值,转为一个数组。
注意:
如果对没有iterator 接口的对象,使用扩展运算符,将会报错。
// TypeError: Cannot spread non-iterable object
- ES6(六: 函数扩展)(默认值,rest参数,扩展运算符)
- ES6 rest参数和扩展运算符
- ES6-rest参数和扩展运算符
- ES6-rest参数和扩展运算符
- ES6-函数的扩展-rest参数
- 10、函数的扩展 为函数参数指定默认值、函数的 rest 参数、箭头函数—ES6学习笔记
- 在es6中,对rest参数和扩展运算符的理解(待更新)
- es6函数扩展(六)
- ECMAScript6(ES6)标准之函数扩展特性箭头函数、Rest参数及展开操作符
- ES6-函数的扩展-函数参数的默认值
- ES6学习之路3----rest参数与扩展运算符
- ES6---扩展运算符和rest‘...’(三点运算符),在数组、函数、set/map等中的应用
- es6 扩展运算符 三个点(...)
- es6 扩展运算符 三个点(...)
- es6 扩展运算符 三个点(...)
- es6 扩展运算符 三个点(...)
- es6 扩展运算符 三个点(...)
- es6 扩展运算符 三个点(...)
- 51单片机+无源蜂鸣器调试经
- 放下你那屌屌的架子,学会聆听别人的想法---致那些傲慢的家伙
- (crm笔记2-4)表单数据删除后的页面跳转问题
- Android自定义View初识
- 2017中秋佳节记
- ES6(六: 函数扩展)(默认值,rest参数,扩展运算符)
- 第十二章 动态内存
- WPF and Silverlight 学习笔记(七):WPF布局管理之StackPanel、WrapPanel、DockPanel
- Jquery中AJAX参数详细介绍
- 图论中的双射
- BZOJ 2588 & SPOJ 10628:树上主席树
- Qt图片与Base64之间的编解码
- [Python]网络爬虫(二):利用urllib2通过指定的URL抓取网页内容
- java版云笔记(九)之动态sql