函数形参默认值

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属性值来获取对函数的引用

函数的两种用途

  1. 结合new使用,函数返回一个新对象,this指向实例
  2. 直接调用

判断函数是通过什么方式调用

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)
    }
}

可以被优化,如果递归函数足够大,可以大幅提升程序性能


一声蔷薇udVkP
25 声望3 粉丝

未来可期


« 上一篇
git知识全梳理
下一篇 »
vue入门