函数在JavaScript语言中发挥着举足轻重的地位,es6中又加入了很多非常好用的内容,最近在看书过程中发现很多值得一说的东西,下边就整理一下。 #默认参数 ##es5中模拟默认参数 默认参数是开发中经常需要考虑的场景,在es5中一般使用如下的方式来模拟
arg1 = arg1 || 2;
}
但是上边的模拟方式存在漏洞,JavaScript中存在隐式转换
,在执行或操作时候会将变量转换为布尔类型,如果arg1值为0一样会将arg1赋值为2。 更好的方法为:
arg1 = typeof arg1 !=='undefined' || 2;
es6中的默认参数
es6中的默认参数就非常简单了,只要使用如下的方法即可:
function func (arg1 = 2, arg2, ...rest) {
console.log(arg1, arg2)
}
func(undefined,3)//2,3
使用等号赋值的方式给与参数默认值,当参数没有传参,或者手动赋值undefined时候,就会给参数赋予默认值,这里指的注意的是:当给参数赋值null不会给参数赋予默认值
。 默认参数使用中还可以使用之前的变量来赋值,如下:
function mixArgs (first, second = first) {
console.log(first + second);
}
export function test () {
// func(undefined, 3);
mixArgs(1, 1);// 2
mixArgs(1);// 2
}
默认参数与arguments
function mixArgs (first, second) {
console.log(first === arguments[0]);
console.log(second === arguments[1]);
first = "c ";
second = "d";
console.log(first === arguments[0]);
console.log(second === arguments[1]);
console.log(first, arguments[0]);
console.log(second, arguments[1]);
}
export function test () {
mixArgs("a", "b");
}
结果如下:
image
arguments数组中的值不会随着变量赋值而改变值。 使用默认参数情况如下:
function mixArgs (first, second = "b") {
console.log(first === arguments[0]);
console.log(second === arguments[1]);
first = "c ";
second = "d";
console.log(first === arguments[0]);
console.log(second === arguments[1]);
console.log(first, arguments[0]);
console.log(second, arguments[1]);
}
export function test () {
mixArgs("a");
}
image
情况其实很容易想明白,设置默认参数的情况下,当你没有给参数赋值,那么他的变量会被赋值为默认参数而arguments数组位置中的变量值为undefined,所以一开始second===argumnets[1]值为false。 ##默认参数表达式 默认参数可以设置为一个函数,比如之前中的一个例子可以写成如下形式:
function func (arg1 = getValue(), arg2, ...rest) {
console.log(arg1, arg2);
}
function getValue () {
return "2";
}
这里需要着重说明一下的是:
默认参数表达式只有在变量没有被赋值或者手动设置为undefined的时候才会执行
如下边的例子
let value = 1;
function getValue () {
return value++;
}
function mixArgs (first, second = getValue()) {
console.log(first + second);
}
export function test () {
// func(undefined, 3);
mixArgs(1, 1);// 2
mixArgs(1);// 2
mixArgs(1);// 3
}
getValue·
函数只有在函数第二个参数需要使用默认参数的时候被调用。 在默认参数表达式中甚至可以将前一个变量的值赋值给表达式,如下:
function getValue (value) {
return value+1;
}
function mixArgs (first, second = getValue(first)) {
console.log(first + second);
}
export function test () {
mixArgs(1, 1);// 2
mixArgs(1);// 3
}
但是使用这种技只能是使用前一个变量的值赋值给后一个变量,如果反过来就会报错,因为在es6中存在临时死区(TDZ)。 比如下边这个例子就会报错。
function mixArgs (first = second, second) {
console.log(first + second);
}
export function test () {
mixArgs(undefined, 1);// 2
}
因为上边的例子等于下边这种情况
let first = second;
let second
这种情况在JavaScript中是不准许的,在JavaScript引入块级作用域之后,在块级作用域中从开始到变量声明之间属于临时死区,使用临时死区中的变量会之间报错,这里顺便插一句,下边这种情况不会报错:
alert(second)//undefined
if(true) {
let second;
}
因为在块级作用域之外变量没有放入临时死区(tdz),调用的话自然也不会报错。 #函数参数长度 函数参数长度一般使用的有两个,形参长度(函数名.length)和实参长度(arguments.length)。 首先说明一下形参不能识别,默认参数和默认表达式参数,从使用默认表达式或者默认参数形式表示的参数开始,函数形参长度就不计算了。 例如,下边这两个例子,第一个例子从第一个参数变量开始就使用默认参数所以形参长度就为0,第二个例子从第二个参数变量开始使用默认参数所以形参长度为1。
function func (arg1=2, arg2 , ...rest) {
console.log(func.name, func.length)//0
}
function func (arg1, arg2 = 2, ...rest) {
console.log(func.name, func.length)//1
}
实参长度理解就比较简单了,函数调用时候传入几个参数arguments长度就是多少。 #不定参数
不定参数设计初衷就是为了取代arguments参数。
JavaScript中的函数是不限制掺入参数数量的,但是这个设定有时候不利于函数中对于传入参数的调用,引入不定参数之后有时候可以让我们写代码更优雅。 ##不定参数的限制 1.不定参数在一个函数中只能有一个,且必须是函数参数的最后一个。 2.对象的setter字面量之中不能使用不定参数,因为对象字面量的setter参数只能有一个所以不能使用不定参数。
函数的名称
var doSomething = function doSomeThingElse() {
}
console.log(doSomething.name)//doSomeThingElse
console.log(doSomething.bind(this).name);//bound doSomeThingElse
console.log((new Function()).name); //anonymous
函数的多重用途
JavaScript中的函数有两个不同的内部方法:[Call]
和[Construct]
函数,当通过new关键字调用函数,执行的是内部[Constructor]函数。 执行[Construct]函数的时候,首先创建一个通常称为实例的新对象,然后执行函数体,将this绑在实例上。 [Call]函数就直接执行函数体。 具有[construct]方法的函数称为构造函数,箭头函数没有[Construct]方法
。 ##判断函数如何被调用的方法 ###es5方法 在es5中可以通过判断this来间接了解函数是通过哪种方式调用,例子如下:
function Person (name) {
if (this instanceof Person) {
this.name = name;
} else {
throw new Error("必须通过new调用函数");
}
}
通过new来调用的方法会首先创造一个方法的实例然后将this绑定到这个实例上。
但是这种方式存在缺陷,当使用Person.call(this)
的方式一样不会报错。
es6的方法
es6中提供了new.target这个元属性,该属性指向new操作符的目标,通常是新创建对象的实例,如果函数是通过[Call]方法调用,则new.target的值为undefined
。 通过new.target我们就可以使用如下的方式来判断函数的调用方式。
function Person (name) {
if (typeof new.target !== "undefined") {
this.name = name;
} else {
throw new Error("必须通过new调用函数");
}
}
块级函数
有时候我们希望将一个方法的使用权限限制在某个块级作用域中,这时候我们可以在块级作用域中声明块级函数,如下边的例子中,someThind函数就是块级函数知在块级中有权限访问。
function blockMethod () {
console.log(typeof someThing);//undefined
if (true) {
function someThing() {
}
}
}
function blockMethod () {
if (true) {
console.log(typeof someThing);//function
function someThing() {
}
}
}
function blockMethod () {
if (true) {
console.log(typeof someThing)//报错(临时死区)
let someThing = function () {
}
console.log(typeof someThing)//function
}
}
箭头函数
简介
箭头函数没有this、arguments、super、new.target绑定
箭头函数中this argumnets super new.target这些属性一律是绑定外围第一个非箭头函数的对应属性,并且箭头函数的this不能通过call,apply等函数修改,但是可以在箭头函数上调用这类方法
。 例如下边这个例子arguments和new.target就绑定外围arrowMethod方法的对应属性。
function arrowMethod () {
let sum = (a, b) => {
console.log(new.target);//arrowMethod
console.log(arguments);//3,4
}
sum(1, 2);
}
new arrowMethod(3,4);
没有[Construct]方法和原型 所以箭头函数不能被new操作。
箭头函数存在name属性
function arrowMethod () {
let sum = (a, b) => {
console.log(sum.name)//sum
}
sum(1, 2);
}
arrowMethod(3,4);
箭头函数的简写
(a, b) => a+b
//相当于
function (a,b) {
return a+b
}
如果希望箭头函数返回一个对象字面量,需要用括号包裹,如下:
let getItem = id => ({id:id,name:"temp"})
网友评论