作者:一声蔷薇udVkP
来源:SegmentFault 思否社区
函数形参默认值
1、es5、es6对默认参数的处理区别:
es5:形参默认值是要先判断,参数是否有值,如果没有在赋值
es6:在定义函数时,可以直接给形参添加默认值,当函数调用实参为undefined则给形参赋值为默认值
注意:当传进来的值为null,则不调用默认参数
2、默认参数对argument的影响
1、影响argument的length属性
2、命名参数的变化不会跟新同步到argument对象中
es5非严格模式下。参数的变化会同步到argument中,严格模式下则不会
es6只要函数使用默认参数,无论是否显示定义严格模式都使得arguments对象保持与命名参数分离
例子-非严格模式:
function minArgs(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)
例子-函数调用结果作为默认参数:
let value =5
function getValue(){
return value++
}
function add(a,b=getValue()){
return a+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:
let doSomethingElse = function doSomething(){}
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的值是否为构造函数的实例
function Person(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
例子:
function Person(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"
//块级函数
function doSomething(){
console.log("ss");
}
doSomething()
//let函数表达式
let doSomethingElse = 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
相当于:
let reflect = function(value){
return value
}
情况二:两个参数时,参数用括号括起来
情况三:没有参数时,直接一个括号后面跟箭头和函数体
情况四:如果想让箭头函数向外返回一个对象字面量,将该字面量包裹在小括号里
let getTempItem = ()=>({name:"zhangsan",age:17})
相当于
let getTempItem = function(){
return {
name:"zhangsan",
age:17
}
}
情况五:创建立即执行函数表达式:
let person = ((name)=>{
return {
getName(){
return name
}
}
})("kankan")
用小括号包裹箭头函数定义,不包括调用实际参数部分
箭头函数的this值:取决于外部非箭头函数的this值
箭头函数设计初衷:即用即弃。
function pageHandle(){
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"
function test(){
return doSomething()
}
递归函数是其最主要的应用场景,优化效果显著
function factorial(n){
if(n<=1){
return 1
}else{
return n*factorial(n-1)
}
}
在递归调用前执行了乘法操作,所以当前阶乘函数无法优化
function factorial(n,p=1){
if(n<=1){
return 1*p
}else{
let result = n*p
优化后
return factorial(n-1,result)
}
}
可以被优化,如果递归函数足够大,可以大幅提升程序性能