作者:一声蔷薇udVkP
来源:SegmentFault 思否社区
函数形参默认值
1、es5、es6对默认参数的处理区别:
es5:形参默认值是要先判断,参数是否有值,如果没有在赋值
es6:在定义函数时,可以直接给形参添加默认值,当函数调用实参为undefined则给形参赋值为默认值
注意:当传进来的值为null,则不调用默认参数
2、默认参数对argument的影响
1、影响argument的length属性
2、命名参数的变化不会跟新同步到argument对象中
es5非严格模式下。参数的变化会同步到argument中,严格模式下则不会
es6只要函数使用默认参数,无论是否显示定义严格模式都使得arguments对象保持与命名参数分离
例子-非严格模式:
functionminArgs(first,second = "b"){
console.log(argument.length)
console.log(first===argument[0])
console.log(second===argument[1])
first = "c";
second = "d";
console.log(first===argument[0])
console.log(second===argument[1])
}
minArgs( "a")
// 1
// true
// false
// false
// false
默认参数表达式
1、可以使用函数调用结果作为默认参数
2、可以使用先定义的参数作为函数调用的默认参数
3、先定义参数不能访问后定义参数,这是因为后定义的参数临时死区(TDZ)
例子-函数调用结果作为默认参数:
letvalue =5
functiongetValue{
returnvalue++
}
functionadd(a,b=getValue){
returna+b
}
console.log(add(1,2))//3
console.log(add(1))//6
不定参数
1、概述:
es5中无命名参数-argument:记录所有传进来的实参
es6中不定参数-(...):记录自这个参数后传进来的所有参数,使用上会更加方便
2、不定参数的使用限制:
a:每个函数只能声明一个不定参数
b:不定参数只能放在末尾
c:不定参数不能用于对象字面量的setter中
3、不定参数对argument的影响:
无影响
展开运算符(...)
概念:
1、不定参数是可以让你指定多个参数,整合成数组
2、展开运算符是让你指定数组,将他们打散后作为各种独立的参数传入函数
增强Function构造函数
目的:用来动态创建新函数
参数:字符串形式的参数,分别为函数参数,函数体
支持创建函数时使用默认参数和不定参数
例子:
var add = new Function0( "first", "second", "return first+second")
console.log(add(1,2))//3
//默认参数
var add1 = new Function0( "first", "second=first", "return first+second")
console.log(add1(1))//2
//不定参数
var add2 = new Function0( "...args", "return args[0]")
console.log(add2(1,2,3))//1
name属性
1、目的:用于辨别函数
2、如何选择合适的名称:
函数声明:
function doSomething{}
doSomething.name = doSomething
匿名函数表达式
let doAnotherThing = function{}
doAnotherThing.name = doAnotherThing
3、name的特殊情况
情况1:
letdoSomethingElse = functiondoSomething{}
doSomethingElse.name =doSomething
情况2:
var person={
get firstName{
},
sayName: function{
}
}
person.firstName.name = "get firstName"
(getter函数和setter函数会再name属性前加一个前缀"get"、"set")
person.sayName.name = "sayName"
情况3:
通过bind函数创建的,名称带有"bound"前缀
情况4:
通过mew Function创建的,名称为"anonymous"
注意:不能使用name属性值来获取对函数的引用
函数的两种用途
- 结合new使用,函数返回一个新对象,this指向实例
- 直接调用
判断函数是通过什么方式调用
js函数有两个不同的内部方法[[Call]]和[[Construct]]
当通过new调用,执行[[Construct]],否则执行[[Call]]
es5判断函数的调用方式:instanceof
当通过new调用的时候,可以检查this的值是否为构造函数的实例
functionPerson(name){
if(this instanceof Person){
this.name = name;
} else{
throw new Error( "必须通过构造函数调用")
}
}
var person = new Person( "lisi")//有效
var p1 = Person.call(Person, "wangwu")//有效
var p2 = Person( "dd")//Error( "必须通过构造函数调用")
弊端:通过instanceof 不能准确判断这个函数是不是new调用,因为通过call或者apply等方法也可以改变this的指向,让this指向某个实例
es6判断函数调用方式:元属性->new.target
元属性:非对象的属性,可以提供非对象目标的补充信息(例如new)
当通过new调用,new.target被赋值为新创建对象的实例,也就是函数体内this构造函数,否则值为undefined
例子:
functionPerson(name){
// if(new.target === Person)
if(typeof new.target !== "undefined"){
this.name = name;
} else{
throw new Error( "必须通过构造函数调用")
}
}
var person = new Person( "lisi")//有效
var p1 = Person.call(Person, "wangwu")//抛出错误
注意:new.target只能在函数体内使用,否则报错
块及函数
1、在es3之前版本在代码块中声明一个函数,会被认定是一个错误
2、最好不要在代码块中使用函数声明,推荐用函数表达式
3、由于不同浏览器兼容不同,所以es5严格模式下,代码块中声明一个函数时程序会抛出错误
4、es6会将声明的函数视为一个块及函数
例子:
if( true){
console.log(typeof doSomething)// "function"
//块级函数
functiondoSomething{
console.log( "ss");
}
doSomething
// let函数表达式
letdoSomethingElse = function{
}
}
console.log(doSomething) //undefined
讲解:定义函数的代码块中,函数会被提升到顶部,一旦if语句代码块结束执行,doSomething不复存在。块级函数和let函数表达式类似,一旦执行过程流出代码块,函数定义立即被移除,区别是,块级函数会被提升到代码块顶部,let定义的函数表达式不会被提升。
es6非严格模式,块级函数会被提升到全局作用域
箭头函数
特点:
1、没有this,super,arguments,new.target的绑定,箭头函数的this、arguments,new.target这些值由外围最近一层非箭头函数决定
2、不能通过new关键字调用
3、没有原型,所以不存在prototype属性
4、不可改变this的绑定,函数体内部的this值不可被该变
5、不支持arguments对象,始终可以访问外围的arguments对象
6、不支持重复命名参数
语法:
参数、箭头、函数体组成
情况一:当只有一个参数时:
let reflect = value=>value
相当于:
letreflect = function(value){
returnvalue
}
情况二:两个参数时,参数用括号括起来
情况三:没有参数时,直接一个括号后面跟箭头和函数体
情况四:如果想让箭头函数向外返回一个对象字面量,将该字面量包裹在小括号里
letgetTempItem = =>({name: "zhangsan",age:17})
相当于
letgetTempItem = function{
return{
name: "zhangsan",
age:17
}
}
情况五:创建立即执行函数表达式:
letperson = ((name)=>{
return{
getName{
returnname
}
}
})( "kankan")
用小括号包裹箭头函数定义,不包括调用实际参数部分
箭头函数的this值:取决于外部非箭头函数的this值
箭头函数设计初衷:即用即弃。
functionpageHandle{
id:123,
init: function{
document.addEnventListener( "click",event=>this.doSomething(event.typ))
},
doSomethis( type){
console.log( type)
}
}
尾调用优化
尾调用:函数作为另一个函数的最后一条语句被调用
es5引擎中,尾调用实现和其他函数调用实现类似,创建一个栈帧,将其推入调用栈表示函数调用,每一个未用完的栈帧都会保存在内存中,当调用栈变得过大会造成程序问题
es6严格模式下满足以下3个条件,尾调用不创建栈帧,而是清除并重用当前栈帧
1、尾调用不访问当前栈帧的变量(不是闭包)
2、在函数内部,尾调用是最后一条语句
3、尾调用结果作为函数值返回
例:(可以被js引擎自动优化)
"use strict"
functiontest{
returndoSomething
}
递归函数是其最主要的应用场景,优化效果显著
functionfactorial(n){
if(n<=1){
return1
} else{
returnn*factorial(n-1)
}
}
在递归调用前执行了乘法操作,所以当前阶乘函数无法优化
functionfactorial(n,p=1){
if(n<=1){
return1*p
} else{
letresult = n*p
优化后
returnfactorial(n-1,result)
}
}
可以被优化,如果递归函数足够大,可以大幅提升程序性能
SegmentFault 思否社区和文章作者展开更多互动和交流。