写在前面
函数(下)链接:juejin.cn/post/684490…
函数是所有编程语言的重要组成部分,es6出现之前,JavaScript的函数语法一直没有太大的变化,遗留了很多的问题,导致实现一些基本功能要编写较多的代码。今天学完了函数部分的语法,由于内容较多分成两个部分来总结,对比es6和es6之前的语法来总结。
文章稍稍长,既然点进来了就耐心看完再出去呗
函数(上)
1.函数形参的默认值
1.1 es5中设置默认参数
function makeRequest(url, timeout, calback) {
timeout = (typeof timeout !== "undefined") ? timeout : 2000;
calback = (typeof timeout !=="oundefined") ? calback : function() {};
}
1.2 es6中设置默认参数
es6简化了为形参提供默认值的代码,如果没有实参传入时提供一个初始值
function makeRequest(url, timeout = 2000,calback = function() {}) {
}
在es6的语法中,url是必须参数,其余两个是可选参数,不为可选参数传递值或者传递undefined才会使用其默认值,还是会优先使用实参。实参值为null也是合法的。
1.3 默认参数值对argument对象的影响
argument的length值是实参的个数。
在es5的非严格模式下,函数命名参数的改变会体现在同步更新到argumemt对象中,在es5的严格模式中,无论参数如何变化,argument对象将不再随之改变。
在es6中,如果使用了默认参数值,无论在什么模式,argument对象的行为与es5严格模式下的行为一致。
1.4默认参数的表达式和默认参数的临死区
默认参数表达式就是参数以表达式的形式体现出来,下面代码中first = second就是表达式。
默认参数表达式要注意的地方,当使用函数调用结果作为默认参数值时,不要忘记写小括号(),否则是对函数的引用而不是结果。
function add(first = second,second) {
return first + second;
}
console.log(add(1,1));//2
console.log(add(undefined,1));// 抛出错误
这里为神马会爆出错误呢?在这个例子里,两次调用add函数在引擎背后做了哪些事情呢 ?
//add(1,1)
let first = 1;
let second = 2;
//add(undefined,1)
let first = secod;
let second= 1;
在之前学习js的时候发现函数里面的变量都没有像c,java语言那样经过定义再使用,原因就是调用函数时候引擎内部进行定义声明。
在这里,first初始化时候second没有初始化在临死区域中,前面let绑定时说过引用临死区域中绑定的行为都会报错。
2.无命名参数
2.1在es5中的无命名参数
在早期es5中不定义每一个要用到的参数,而是根据argument对象来检查函数的所有参数。举个栗子:
//只有一个参数boo
function pick(boo) {
//创建一个空对象result
let result = Object.create(null);
//重第二个参数开始
for(let i = 1;i<arguments.length;i++) {
result[[arguments[i]] = boo[[arguments[i]];
//这里若i==1,即result.author = boo.author
}
return result;
}
let book = {
title:'qqqq';
author:'refef';
year:"1999";
};
let bookPick = pick(book, "author","year");
console.log(bookPick.author);
console.log(bookPick.year)
上述demo里只定义了一个对象,第一个参数传入是被复制属性的源对象book,其他参数为被复制属性的名称author和year。这里需要从索引1开始遍历来拷贝属性名。
在es6中可以用不定参数来解决这些问题,是问题变得更加简单。
2.2 在es6中的不定参数
在函数的命名参数前面添加三个点...就表示一个不定参数,如...keys就是一个不定参数,keys是一个数组。
用不定参数重现上面pick函数的例子
function pick(object, ...keys) {
let result = Object.create(null);
for( let i = 0,len = keys.length; i<len; i++) {
result[keys[i]]= object[keys[i]];
}
return result;
let book = {
title:'qqqq';
author:'refef';
year:"1999";
};
let bookPick = pick(book, "author","year");
console.log(bookPick.author);
console.log(bookPick.year)
}
这里keys数组包含了book后面传入的所有参数。argument对象包含的是所有传入的参数包括object,函数的length值是函数命名参数的数量,不定参数的加入对其无影响,在本例中pick()函数的length=1,只计算object
2.3 不定参数的使用限制
每个函数只能声明一个不定参数,而且只能放在所有参数的末尾
不定参数不能用于对象字面量setter中,seetter的参数有且只能有一个,不定参数的参数数量可以有无限多个
2.4 不定参数对arguements的影响
不论是否使用了不定参数,arguments始终包含所有传入函数的参数,这一点在前面的2.2中也有提到。
3.增强的Function构造函数
es5中的function
Function构造函数是javascript语法中很少被用到的一部分,通常用它动态创建新函数,这种构造函数接受字符串的形式,分别为函数的参数以及函数体。
var add = new Function("first", "second", "return first + second") ;
console.log(add(1,1));
es6中的Function
在es6中可以在创建函数的时候定义默认参数和不定参数
var pickFirst = new Function("...args", "return args[0]");
console.log(pickFirst(1 ,2)); //1
4.展开运算符
在说展开运算符之前,先看一个Math.max的例子,它可以接受任意数量的·1参数但是不能接受数组。在es5及之前,需要手动遍历数组或者使用apply。
let values = [1,45,63];
console.log(Math.max.apply(Math,values));
这个代码很难让人看懂,需要了解this,apply改变this指向 在es6中,可以添加不定参数表示一个数组,因而就不需要apply()方法了。js引擎会将参数数组分割成独立的参数并依次传入。
let values = [1,45,63];
console.log(Math.max(...values));
//==console.log(Math.max(1,45,63));
如果还有其他值想进行比较,可以单独传入。如和0比较:
let values = [1,45,63];
console.log(Math.max(...values,0));
5.name属性
js中的匿名函数表达式的广泛使用加大了调试的难度。es6中name属性使函数都有一个自己合适的名字。
一般非匿名函数name属性值对应声明时的函数名称,匿名函数对应着被赋值为该匿名函数的变量的名称
name属性值得特殊情况
1.函数非匿名函数,又被赋值的情况下name值是函数表达式自己的名字不是被赋值变量的名字,因为这个名字比函数本身被赋值的变量的权重高。
2.对象字面量的name值取对象字面量
var person = {
get firstName = {
return "aaa"
},
sayName: function() {
console.log(this.name);
}
}
console.log(person.sayName.name); //"sayName"
console.name(person.firstName.name); //" get firstName"
3.bind()函数创建的函数,其名称带有bond前缀,通过Function构造函数创建的函数,其名称是“anonymous”。