函数的参数
默认参数的实现
in ES5
- 方法1:使用
||判断参数是否提供(不安全,值为0时会被认为是false而使用默认值) - 方法2:使用
typeof param !== "undefined"判断参数是否提供
in ES6
ES6的默认参数:未提供参数或者参数是undefined时,会使用默认值
默认参数的值
参数的默认值可以是一个简单值,也可以是一个函数调用表达式
函数调用表达式的调用时机:第二个参数缺失并且调用add()方法时,会执行该函数调用表达式。(当add()函数编译时不会执行该函数调用表达式)
参数的访问
后面的参数可以访问前面的参数,反之则不能
【原因】:默认参数也有TDZ,当默认参数被初始化后才会从TDZ中移除,该参数才可以被访问。
Unnamed Parameters
in ES5
使用arguments来获取多余的参数
in ES6
使用rest parameters来处理参数,如下:key是rest parameter
rest parameters的一些限制:
- rest parameter只能有一个,并且是最后一个参数
- rest parameter不能在对象的访问器属性setter中使用
默认参数对arguments的影响
ES5非严格模式下,直接修改参数值会影响到arguments
12345function mixArgs(first) {console.log(first === arguments[0]); // truefirst = "c";console.log(first === arguments[0]); // true}ES5严格模式下,直接修改参数值不会影响到arguments
12345function mixArgs(first) {console.log(first === arguments[0]); // truefirst = "c";console.log(first === arguments[0]); // false}ES6中,直接修改参数值不会影响到arguments;并且设置了默认值的参数缺失时,将会使用默认参数,这时该参数和arguments没有关联
12345678910function mixArgs(first, second = "b") {console.log(arguments.length); // 1console.log(first === arguments[0]); // trueconsole.log(second === arguments[1]); // falsefirst = "c";second = "d"console.log(first === arguments[0]); // falseconsole.log(second === arguments[1]); // false}mixArgs("a") // second缺失
Function函数
作用
使用Function动态的创建一个函数
在ES6中的特性
可以使用default parameter和rest parameter
函数的属性:name
正常情况下name的值:是函数声明或函数表达式声明
123456789function doSomething() {// empty}var doAnotherThing = function() {// empty};console.log(doSomething.name); // "doSomething"console.log(doAnotherThing.name); // "doAnotherThing"特殊情况下name的值
- 同时有函数声明和函数表达式声明,name的值是函数声明
- 对象迭代器函数,name值会加上前缀:
getorset - 使用
bind()创建的新函数,name值会加上前缀:bound - 通过
new Function()创建的新函数,name值为:anonymous123456789101112131415161718var doSomething = function doSomethingElse() {// empty};var person = {get firstName() {return "Nicholas"},sayName: function() {console.log(this.name);}}console.log(doSomething.name); // "doSomethingElse"console.log(person.sayName.name); // "sayName"console.log(person.firstName.name); // "get firstName"console.log(doSomething.bind().name); // "bound doSomething"console.log((new Function()).name); // "anonymous"
判断函数是否是构造函数
JavaScript的函数有两个内置方法:[[Call]] 和 [[Construct]]
使用new操作符调用函数时,会执行[[Construct]]方法,否则执行[[Call]]方法(箭头函数没有[[Construct]]方法,所以不能作为构造函数)
in ES5
通过函数中的this对象的实例来判断函数是否作为构造函数
存在的问题:不使用new运算符(例如:使用call方法调用函数),this的实例也有可能是Person
in ES6
使用一个metaproperty:new.target来判断函数是否作为构造函数
当执行[[Call]]方法,new.target的值是undefined
当执行[[Construct]]方法,typeof new.target === Person
Block-Level Functions
在ES6中,在{}创建的block中声明的函数,这个函数是Block-Level Functions。Block-Level Functions是以函数声明方式创建的,而不是使用函数直接量来创建的。
严格模式下,Block-Level Functions只能在当前block中被访问,并且会提升到该block的顶部。
非严格模式下,Block-Level Functions会被提升到全局作用域的顶部
Arrow Functions
定义
|
|
箭头函数和普通函数的区别
- 箭头函数内部的变量:
this,super,arguments,new.target,与包含该箭头函数的最近非箭头函数相关联this在箭头函数声明时被指定,并且不能被更改,在函数的整个生命周期中保持不变arguments变量不存在
- 箭头函数不能作为构造函数(因为箭头函数没有
[[Construct]]方法) - 箭头函数没有
prototype属性 - 箭头函数的参数的命名不能重复1var aa = (a, a) => {} // Uncaught SyntaxError: Duplicate parameter name not allowed in this context
尾调用(Tail Call)
定义:函数的最后一个执行语句是一个函数调用。
123function doSomething() {return doSomethingElse(); // tail call}尾调用存在的问题:栈溢出
- ES6在严格模式下对尾调用做了优化,当满足以下情况,当前
stack frame就会被清空并重用- 当前函数不是一个闭包(closure),即该尾调用不会访问当前栈帧中变量
- 该尾调用返回后,创建尾调用的函数没有其他工作要做
- 该尾调用的返回值作为该函数的返回值
尾调用优化失效的情况有:
1234567891011121314151617181920212223"use strict";function doSomething() {// not optimized - no returndoSomethingElse();}function doSomething() {// not optimized - must add after returningreturn 1 + doSomethingElse();}function doSomething() {// not optimized - call isn't in tail positionvar result = doSomethingElse();return result;}function doSomething() {var num = 1,func = () => num;// not optimized - function is a closurereturn func();}利用ES6在严格模式下对尾调用的优化来优化代码,例子:递归
12345678910111213141516171819function factorial(n) {if (n <= 1) {return 1} else {// 不满足优化条件,所以当n很大时,会出现栈溢出return n * factorial(n - 1)}}// 优化代码function factorial(n, p = 1) {if (n <= 1) {return 1 * p} else {let result = n * p// optimizedreturn factorial(n - 1, result);}}