通过上面这个例子不难发现, 不仅可以用解构的方法设置初始值, 还可以进行参数传递。当然, 这里也可以是对象形式的解构赋值。如果传入的参数无法解构, 就会报错:
function fun1({a=1, b=5, c=‘A’}){
console.log(c + (a + b));
}
fun1({}); //‘A6’
fun1(); //TypeError, 因为无法解构
//但这样设计函数对使用函数的码农很不友好
//所以, 技巧:
function fun2({a=1, b=5, c=‘A’}={}){
console.log(c + (a + b));
}
fun2(); //‘A6’
注意, 其实还有一种方法, 但不如这个好, 我们比较如下:
//fun1 比 fun2 好, 不会产生以外的 undefined
function fun1({a=1, b=5, c=‘A’}={}){
console.log(c + (a + b));
}
function fun2({a, b, c}={a: 1, b: 5, c: ‘A’}){
console.log(c + (a + b));
}
//传了参数, 但没传全部参数就会出问题
fun1({a: 8}); //‘A13’
fun2({a: 8}); //NaN
不过这里强烈建议, 将具有默认值的参数排在参数列表的后面。否则调用时依然需要传参:
function f1(a=1, b){
console.log(a + b);
}
function f2(a, b=1){
console.log(a + b);
}
f2(2); //3
f1(, 2); //报错
f1(undefined, 2); //3, 注意这里不能用 null 触发默认值
这里我们还需要单独讨论一下默认参数对 arguments 的影响:
function foo(a = 1){
console.log(a, arguments[0]);
}
foo(); //1 undefined
foo(undefined); //1 undefined
foo(2); //2 2
foo(null); //null null
很明显,默认参数并不能加到 arguments 中。
- 函数的 length 属性
这个属性ES6 之前就是存在的, 记得length表示预计传入的形参个数, 也就是没有默认值的形参个数:
(function(a){}).length; //1
(function(a = 5){}).length; //0
(function(a, b, c=5){}).length; //2
(function(…args){}).length; //0, rest参数也不计入 length
rest 参数
rest 参数形式为 ...变量名
, 它会将对应的全部实际传递的变量放入数组中, 可以用它来替代 arguments:
function f(…val){
console.log(val.join());
}
f(1, 2); //[1, 2]
f(1, 2, 3, 4); //[1, 2, 3, 4]
function g(a, …val){
console.log(val.join());
}
g(1, 2); //[2]
g(1, 2, 3, 4); //[2, 3, 4]
否则这个函数 g 你的这样定义函数, 比较麻烦:
function g(a){
console.log([].slice.call(arguments, 1).join());
}
这里需要注意2点:
-
rest参数必须是函数的最后一个参数, 它的后面不能再定义参数, 否则会报错。
-
rest参数不计入函数的 length 属性中
建议:
-
所有配置项都应该集中在一个对象,放在最后一个参数,布尔值不可以直接作为参数。这样方便调用者以任何顺序传递参数。
-
不要在函数体内使用arguments变量,使用rest运算符(…)代替。因为rest运算符显式表明你想要获取参数,而且arguments是一个类似数组的对象,而rest运算符可以提供一个真正的数组。
-
使用默认值语法设置函数参数的默认值。
扩展运算符
扩展运算符类似 rest运算符的逆运算, 用 ...
表示, 放在一个(类)数组前, 将该数组展开成独立的元素序列:
console.log(1, …[2, 3, 4], 5); //输出1, 2, 3, 4, 5
扩展运算符的用处很多:
- 可以用于快速改变类数组对象为数组对象, 也是用于其他可遍历对象:
[…document.querySelectorAll(‘li’)]; //[
- ,
- ,
- ];
-
- 结合 rest 参数使函数事半功倍:
function push(arr, …val){
return arr.push(…val); //调用函数时, 将数组变为序列
}
- 替代 apply 写法
var arr = [1, 2, 3];
var max = Math.max(…arr); //3
var arr2 = [4, 5, 6];
arr.push(…arr2); //[1, 2, 3, 4, 5, 6]
new Date(…[2013, 1, 1]); //ri Feb 01 2013 00: 00: 00 GMT+0800 (CST)
- 连接, 合并数组
var more = [4, 5];
var arr = [1, 2, 3, …more]; //[1, 2, 3, 4, 5]
var a1 = [1, 2];
var a2 = [3, 4];
var a3 = [5, 6];
var a = […a1, …a2, …a3]; //[1, 2, 3, 4, 5, 6]
- 解构赋值
var a = [1, 2, 3, 4, 5];
var [a1, …more] = a; //a1 = 1, more = [2, 3, 4, 5]
//注意, 扩展运算符必须放在解构赋值的结尾, 否则报错
- 字符串拆分
var str = “hello”;
var alpha = […str]; //alpha = [‘h’, ‘e’, ‘l’, ‘l’, ‘o’]
[…‘x\uD83D\uDE80y’].length; //3, 正确处理32位 unicode 字符
建议:使用扩展运算符(…)拷贝数组。
name 属性
name 属性返回函数的名字, 对于匿名函数返回空字符串。不过对于表达式法定义的函数, ES5 和 ES6有差别:
var fun = function(){}
fun.name; //ES5: “”, ES6: “fun”
(function(){}).name; //“”
对于有2个名字的函数, 返回后者, ES5 和 ES6没有差别:
var fun = function baz(){}
fun.name; //baz
对于 Function 构造函数得到的函数, 返回
anonymous
:new Function(“fun”).name; //“anonymous”
new Function().name; //“anonymous”
(new Function).name; //“anonymous”
对于 bind 返回的函数, 加上
bound
前缀function f(){}
f.bind({}).name; //“bound f”
(function(){}).bind({}).name; //"bound "
(new Function).bind({}).name; //“bound anonymous”
箭头函数
箭头函数的形式如下:
var fun = (参数列表) => {函数体};
如果只有一个参数(且不指定默认值), 参数列表的圆括号可以省略; (如果没有参数, 圆括号不能省略)
如果只有一个 return 语句, 那么函数体的花括号也可以省略, 同时省略 return 关键字。
var fun = value => value + 1;
//等同于
var fun = function(value){
return value + 1;
}
var fun = () => 5;
//等同于
var fun = function(){
return 5;
}
如果箭头函数的参数或返回值有对象, 应该用
()
括起来:var fun = n => ({name: n});
var fun = ({num1=1, num2=3}={}) => num1 + num2;
看完之前的部分, 箭头函数应该不陌生了:
var warp = (…val) => val;
var arr1 = warp(2, 1, 3); //[2, 1, 3]
var arr2 = arr1.map(x => x * x); //[4, 1, 9]
arr2.sort((a, b) => a - b); //[1, 4, 9]
使用箭头函数应注意以下几点:
- 不可以将函数当做构造函数调用, 即不能使用 new 命令;
- 不可以在箭头函数中使用 yield 返回值, 所以不能用过 Generator 函数;
- 函数体内不存在 arguments 参数;
- 函数体内部不构成独立的作用域, 内部的 this 和定义时候的上下文一致; 但可以通过 call, apply, bind 改变函数中的 this。关于作用域, 集中在ES6函数扩展的最后讨论。
举几个箭头函数的实例:
实例1: 实现功能如:
insert(2).into([1, 3]).after(1)
或insert(2).into([1, 3]).before(3)
这样的函数:var insert = value => ({
into: arr => ({
before: val => {
arr.splice(arr.indexOf(val), 0, value);
return arr;
},
after: val => {
arr.splice(arr.indexOf(val) + 1, 0, value);
return arr;
}
})
});
console.log(insert(2).into([1, 3]).after(1));
console.log(insert(2).into([1, 3]).before(3));
实例2: 构建一个管道(前一个函数的输出是后一个函数的输入):
var pipe = (…funcs) => (init_val) => funcs.reduce((a, b) => b(a), init_val);
//实现 2 的 (3+2) 次方
var plus = a => a + 2;
pipe(plus, Math.pow.bind(null, 2))(3); //32
实例3: 实现 λ 演算
//fix = λf.(λx.f(λv.x(x)(v)))(λx.f(λv.x(x)(v)))
var fix = f => (x => f(v => x(x)(v)))(x => f(v => x(x)(v)));
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
后话
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
对于面试,说几句个人观点。
面试,说到底是一种考试。正如我们一直批判应试教育脱离教育的本质,为了面试学习技术也脱离了技术的初心。但考试对于人才选拔的有效性是毋庸置疑的,几千年来一直如此。除非你有实力向公司证明你足够优秀,否则,还是得乖乖准备面试。这也并不妨碍你在通过面试之后按自己的方式学习。
其实在面试准备阶段,个人的收获是很大的,我也认为这是一种不错的学习方式。首先,面试问题大部分基础而且深入,这些是平时工作的基础。就好像我们之前一直不明白学习语文的意义,但它的意义就在每天的谈话间。92945208)]
[外链图片转存中…(img-TsmTHKgq-1710592945208)]由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
[外链图片转存中…(img-epXQtQYn-1710592945209)]后话
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
对于面试,说几句个人观点。
面试,说到底是一种考试。正如我们一直批判应试教育脱离教育的本质,为了面试学习技术也脱离了技术的初心。但考试对于人才选拔的有效性是毋庸置疑的,几千年来一直如此。除非你有实力向公司证明你足够优秀,否则,还是得乖乖准备面试。这也并不妨碍你在通过面试之后按自己的方式学习。
其实在面试准备阶段,个人的收获是很大的,我也认为这是一种不错的学习方式。首先,面试问题大部分基础而且深入,这些是平时工作的基础。就好像我们之前一直不明白学习语文的意义,但它的意义就在每天的谈话间。所谓面试造火箭,工作拧螺丝。面试往往有更高的要求,也迫使我们更专心更深入地去学习一些知识,也何尝不是一种好事。