函数形参的默认值
es5中模拟默认参数
timeout和callback 为可选参数 如果不传相应的参数 系统会给它们赋予一个默认值,
但 timeout 如果传参转换为布尔值是假值 例如:undefined null false 0 NaN “” ‘’ 都会被赋值2000;
function makeRquest(url, timeout, callback) {timeout = timeout || 2000; // timeout 如果传转换为布尔值为假的值 例如:undefined null false 0 NaN "" '' 都会被赋值2000;callback = callback || function () { };console.log(url, timeout, callback);}makeRquest( 123, 0, null);; // 123 2000 ƒ () { }
更安全的做法
function makeRquest(url, timeout, callback) {
timeout = (typeof timeout !== "undefined") ? timeout : 2000;
callback = (typeof callback !== "undefined") ? callback : function () { };
console.log(url, timeout, callback);
}
makeRquest( 123, 0, null); // 123 0 null
es6中的默认参数值
es6使用默认参数的方法
1传参undefined或者不写(若后面有形参需要传参则填undefined)
相较于es5的优势
可传参 NaN false null 0
function makeRquest(url, timeout = 2000, callback = function () { }) {
console.log(url, timeout, callback);
}
//使用参数timeout和参数callback的默认值
makeRquest("/foo"); // /foo 2000 ƒ () { }
//使用参数callback的默认值
makeRquest("/foo", 500); // /foo 500 ƒ () { }
//不使用默认值
makeRquest("/foo", 500, function abc() { }); //foo 500 ƒ abc() { }
//可传任意参数NaN false
makeRquest("/foo", NaN, false); //foo NaN false
声明函数时,可以为任意参数指定默认值,在已指定默认值的参数后可以继续声明无默认值参数
function makeRquest(url, timeout = 2000, callback) {
console.log(url, timeout, callback);
}
//使用参数timeout的默认值
makeRquest("/foo", undefined, 'abc'); // /foo 2000 abc
//不使用参数timeout的默认值
makeRquest("/foo", 500); // /foo 500 undefined
//不使用默认值
makeRquest("/foo", null, false); //foo null false
默认参数值对arguments对象的影响
非严格模式
在非严格模式下,命名参数的变化会同步更新到arguments对象中,所以当first和second被赋予新值时,arguments[0]和arguments[1]相应地也更新
所以比较的结果都为true
function mixArgs(first, second) {
console.log(first, arguments[0], second, arguments[1]); // a a b b
console.log(first === arguments[0]); //true
console.log(second === arguments[1]); //true
first = "c";
second = "d";
console.log(first, arguments[0], second, arguments[1]); // c c d d
console.log(first === arguments[0]); // true
console.log(second === arguments[1]); //true
}
mixArgs("a", "b");
严格模式
然而,在ECMAScript 5的严格模式下,取消了arguments对象的这个令人感到困惑的行为,无论参数如何变化,arguments对象不再随之改变。
function mixArgs(first, second) {
"use strict";
console.log(first, arguments[0], second, arguments[1]); // a a b b
console.log(first === arguments[0]); //true
console.log(second === arguments[1]); //true
first = "c";
second = "d";
console.log(first, arguments[0], second, arguments[1]); // c a d b
console.log(first === arguments[0]); // false
console.log(second === arguments[1]); // false
}
mixArgs("a", "b");
es6使用默认参数值
在es6中,如果一个函数使用了默认参数值(哪怕只是形参中的默认参数),则无论是否显式定义了严格模式,arguments对象的行为都将与es5严格模式下保持一致, 无论参数如何变化,arguments对象不再随之改变。
默认参数值的存在使得arguments对象保持与命名参数分离(形参的更改不会同步更新到arguments对象中)
arguments对象中始终是调用函数时传递的全部参数 与 默认参数值不一致
function mixArgs(first, second = "b") {
console.log(arguments); // ["a"]
console.log(arguments.length); //1
console.log(first, arguments[0], second, arguments[1]); // a a b undefined
console.log(first === arguments[0]); //true
console.log(second === arguments[1]); //false
first = "c";
second = "d";
//形参的更改不会同步更新到arguments对象中
console.log(first, arguments[0], second, arguments[1]); // c a d undefined
console.log(first === arguments[0]); // false
console.log(second === arguments[1]); // false
}
mixArgs("a");
默认参数表达式
可以通过函数执行来得到默认参数的值
function getValue() {
return 5;
}
function add(first, second = getValue()) {
return first + second;
}
console.log(add(1, 1)); //2
console.log(add(1)); // 6
初次解析函数声明时不会调用getValue()方法,只有当调用add()函数且不传入第二个参数时才会调用。
let value = 5;
function getValue() {
return value++;
}
function add(first, second = getValue()) {
return first + second;
}
console.log(add(1, 1)); //2
console.log(add(1)); // 6
console.log(add(1)); // 7
console.log(add()); //NaN
默认参数是在函数调用时求值(如果在函数解析时求值,后定义的参数获取不到先定义的参数就会undefined+undefined=NaN),
所以可以使用先定义的参数作为后定义参数的默认值
function add(first, second = first) {
return first + second;
}
console.log(add(1, 1)); //2
console.log(add(1)); //2
可以将参数first传入一个函数来获得参数second的值
function getValue(value) {
return value + 5;
}
function add(first, second = getValue(first)) {
return first + second;
}
console.log(add(1, 1)); //2
console.log(add(1));// 7 不传second参数时则调用getValue()方法
在引用参数默认值的时候,只允许引用前面参数的值,即先定义的参数不能访问后定义的参数
function add(first = second, second) {
return first + second;
}
console.log(add(1, 1)); //2
console.log(add(undefined, 1)); //报错
默认参数的临时死区
默认参数也有临时死区,在这里的参数不可访问。与let声明类似,定义参数时会为每个参数创建一个新的标识符绑定,该绑定在初始化之前不可被引用
add(first = second, second)
add(1,1)
//等价于
let first = 1;
let second = 1;
add(undefined,1)
//等价于
let first = second;
let second = 1;
处理无命名参数
es5中的无命名参数
arguments对象包含的则是所有传入的参数,(arguments[0]是源数组object 所以查找索引从1开始 排除object)
function pick(object) {
let result = Object.create(null);
//
for (let i = 1; len = arguments.length, i < len; i++) {
result[arguments[i]] = object[arguments[i]]; //遍历传入对象每一个属性 并赋值给新对象
}
return result;
}
let book = {
title: "abc",
author: "ABC",
year: 2020
};
let bookData = pick(book, "title", "author", "year");
console.log(bookData.title, bookData.author, bookData.year); // abc ABC 2020
不定参数
在命名参数前添加三个点(…)就表明这是一个不定参数 该参数为一个数组,包含着自它之后传入的所有参数,通过这个数组名可逐一访问里面的参数
不定参数keys包含的是实参bookt之后传入的所有参数此例中是(”title”, “author”, “year”)
(而arguments对象包含的则是所有传入的参数,(book, “title”, “author”, “year”))
function pick(object, ...keys) {
console.log(object, keys); // {title: "abc", author: "ABC", year: 2020} ["title", "author", "year"]
let result = Object.create(null);
for (let i = 0; len = keys.length, i < len; i++) {
result[keys[i]] = object[keys[i]]; //遍历传入对象每一个属性 并赋值给新对象
}
console.log(result); //{title: "abc", author: "ABC", year: 2020}
return result;
}
let book = {
title: "abc",
author: "ABC",
year: 2020
};
let bookData = pick(book, "title", "author", "year");
console.log(bookData.title, bookData.author, bookData.year); // abc ABC 2020
console.log(pick.length); //1
注:函数的length属性统计的是函数命名参数的数量,不定参数的加入不会影响length属性的值.
上例中 pick()函数的length的值为1,因为只会计算命名参数 objcet
不定参数的使用限制
1.每个函数最多只能声明一个不定参数,而且一定要放在所有参数的末尾
//语法错误:不定参数后不能有其他命名参数 不定参数keys只能放在末尾,不能在不定参数后声明其他参数
function pick(object, ...keys,last) {
let result = Object.create(null);
for (let i = 0; len = keys.length, i < len; i++) {
result[keys[i]] = object[keys[i]];
}
console.log(result);
return result;
}
2.不定参数不能用于对象字面量setter之中
let object = {
// 语法错误,不可以在setter中使用不定参数
set name(...value) {
// 执行一些逻辑
}
}
因为对象字面量seter的参数有且只能有一个,而在不定参数的定义中,参数的数量可以无限多,所以在当前的上下文中不允许使用不定参数
不定参数对arguments对象的影响
不定参数的传入并不会影响arguments
原本es4 草案中打算移除arguments添加不定参数的特性,es4从未标准化,直到es6引入了不定参数 arguments也被保留下来
function checkArgs(...args) {
console.log(args.length);
console.log(arguments.length);
console.log(args[0],arguments[0]);
console.log(args[1],arguments[1]);
}
checkArgs("a","b");
// 2
// 2
// a a
// b b
arguments对象总是包含所有传入函数的参数 不定参数只包含不定参数的参数
增强的Function构造函数
通常用来动态创建新的函数 这种构造函数接收字符串形式的参数,分别为函数的参数及函数体。
var add = new Function("first", "second", "return first + second");
console.log(add(1, 1)); //2
es6增强了Function构造函数的功能,支持在创建函数时定义默认参数和不定参数
默认参数
var add = new Function("first", "second = first", "return first + second");
console.log(add(1)); //2
不定参数
var add = new Function("...args", "return args[0]");
console.log(add(1,2)); //2
展开运算符
判断参数中的最大值
let value1 = 25,
value2 = 50;
console.log(Math.max(value1,value2)); //50
es5判断数组中的最大值
let values = [25, 50, 75, 100];
console.log(Math.max.apply(Math, values)); //100
apply的风险
let values = [25, 50, 75, 100];
console.log(Math.max(...values)); //100
限定最小值为0
let values = [-25, -50, -75, -100];
console.log(Math.max(...values,0)); //0
name属性
如何选择合适的名称
- 函数声明:声明时对应的名称
匿名函数:被赋值为该匿名函数的变量名称(函数表达式的名字)
function doSomething() { } var doAnotherThing = function () { } console.log(doSomething.name); //doSomething console.log(doAnotherThing.name); //doAnotherThingname属性的特殊情况
函数表达式与函数声明 : 取函数表达式 函数表达式的名字权重比函数声明的名字权重高
- 方法:取对象的属性名
getter函数:取get + 函数名
var doSomething = function doSomethingElse() { } 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
特例
- 通过bind()函数创建的函数,其名称将带有”bound”前缀
通过Function构造函数创建的函数,其名称将是”anonymous”
var doSomething = function () { }; console.log(doSomething.bind().name); // bound doSomething console.log((new Function()).name); // anonymous
明确函数的多用途
function Person(name) {
this.name = name;
}
var person = new Person("Nicholas");
var notAPerson = Person("Nicholas");
console.log(person); //Person {name: "Nicholas"}
// name: "Nicholas"
// __proto__: Object
console.log(notAPerson);// undefined
给notAPerson变量赋值时,没有通过new关键字来调用Person(),最终返回undefined(如果在非严格模式下,还会在全局对象中设置一个name属性)。只有通过new关键字调用Person()时才能体现其能力,就像常见的JavaScript程序中显示的那样。而在ECMAScript 6中,函数混乱的双重身份终于将有一些改变。JavaScript函数有两个不同的内部方法:[[Call]]和[[Construct]]。当通过new关键字调用函数时,执行的是[[Construct]]函数,它负责创建一个通常被称作实例的新对象,然后再执行函数体,将this绑定到实例上;如果不通过new关键字调用函数,则执行[[Call]]函数,从而直接执行代码中的函数体。具有[[Construct]]方法的函数被统称为构造函数。切记,不是所有函数都有[[Construct]]方法,因此不是所有函数都可以通过new来调用,例如,我们在本章后面讲解的箭头函数就没有这个[[Construct]]方法。
es5中判断函数被调用的方法
function Person(name) {
if (this instanceof Person) {
this.name = name; //如果通过new关键字调用
} else {
throw new Error("必须通过new关键字来调用Person。");
}
}
var person = new Person("Nicholas");
var notAPerson = Person("Nicholas");
在这段代码中,首先检查this的值,看它是否为构造函数的实例,如果是,则继续正常执行;如果不是,则抛出错误。由于[[Construct]]方法会创建一个Person的新实例,并将this绑定到新实例上,通常来讲这样做是正确的,但这个方法也不完全可靠,因为有一种不依赖new关键字的方法也可以将this绑定到Person的实例上,如下所示:
function Person(name) {
if (this instanceof Person) {
this.name = name; //如果通过new关键字调用
} else {
throw new Error("必须通过new关键字来调用Person。");
}
}
var person = new Person("Nicholas");
var notAPerson = Person.call(person, "Michael"); //有效
console.log(person);
console.log(notAPerson);
调用Person.call()时将变量person传入作为第一个参数,相当于在Person函数里将this设为了person实例。对于函数本身,无法区分是通过Person.call()(或者是Person.apply())还是new关键字调用得到的Person的实例。
元属性 new.target
为了解决判断函数是否通过new关键字调用的问题,ECMAScript 6引入了new.target这个元属性。元属性是指非对象的属性,其可以提供非对象目标的补充信息(例如new)。当调用函数的[[Construct]]方法时,new.target被赋值为new操作符的目标,通常是新创建对象实例,也就是函数体内this的构造函数;如果调用[[Call]]方法,则new.target的值为undefined。有了这个元属性,可以通过检查new.target是否被定义过来安全地检测一个函数是否是通过new关键字调用的,就像这样:
function Person(name) {
console.log(new.target); //如果是new 关键字调用则 new.target 等于构造函数本身 fn Person(name){}
console.log(typeof new.target); //如果是new 关键字调用则 new.target 等于function
if (typeof new.target !== "undefined") {
this.name = name;
} else {
throw new Error("必须通过new关键字来调用Person。");
}
}
var person = new Person("Nicholas");
var notAPerson = Person.call(person, "Michael"); //抛错
console.log(person,notAPerson);
也可以检查new.target是否别某个特定构造函数所调用,
function Person(name) {
// console.log(new.target); //如果是new 关键字调用则 new.target 等于构造函数本身 fn Person(name){}
// Person 等于 fn Person(name){}
if (new.target === Person) {
this.name = name;
} else {
throw new Error("必须通过new关键字来调用Person。");
}
}
function AnotherPerson(name) {
Person.call(this.name);
}
var person = new Person("Nicholas");
console.log(person); //Person {name: "Nicholas"}
var anotherPerson = new AnotherPerson("Nicholas"); //抛错
在这段代码中,如果要让程序正确运行,new.target一定是Person。当调用newAnotherPerson(“Nicholas”)时,真正的调用Person.call(this, name)没有使用new关键字,因此new.target的值为undefined会抛出错误。
注:在函数外使用new.target是一个语法错误。
块级函数
严格模式
es5中
"use strict"
if(true){
// 在es5中抛出语法错误,在es6中不报错
function doSomething() {
// 空函数
}
}
es6中
在ECMAScript 6中,会将doSomething()函数视作一个块级声明,从而可以在定义该函数的代码块内访问和调用它
"use strict"
if (true) {
console.log(typeof doSomething); //function
// 在es5中抛出语法错误,在es6中不报错
function doSomething() {
// 空函数
}
}
在es6严格模式中:代码块内 块级函数会被提升至顶部, 代码块外:块级函数不存在 undefined
在定义函数的代码块内,块级函数会被提升至顶部,所以typeof doSomething的值为”function”,这也佐证了,即使你在函数定义的位置前调用它,还是能返回正确结果;但是一旦if语句代码块结束执行,doSomething()函数将不再存在。
块级函数的使用场景
- 块级函数会被提升至块的顶部,
let顶一顶函数表达式不会被提升
"use strict" if (true) { console.log(typeof doSomething); //报错 // 在es5中抛出语法错误,在es6中不报错 let doSomething = function () { // 空函数 } doSomething(); } console.log(typeof doSomething);非严格模式下的块级元素
标准 在es6非严格模式中 块级函数将提升至外围函数或全局作用域的顶部
if (true) { console.log(typeof doSomething); //报错 // 在es5中抛出语法错误,在es6中不报错 function doSomething() { // 空函数 } doSomething(); } console.log(typeof doSomething);
箭头函数
与普通函数的区别
· 没有this、super、arguments和new.target绑定 箭头函数中的this、super、arguments及new.target这些值由外围最近一层非箭头函数决定。(super将在第4章进行讲解。)
· 不能通过new关键字调用 箭头函数没有[[Construct]]方法,所以不能被用作构造函数,如果通过new关键字调用箭头函数,程序会抛出错误。
· 没有原型 由于不可以通过new关键字调用箭头函数,因而没有构建原型的需求,所以箭头函数不存在prototype这个属性。
· 不可以改变this的绑定 函数内部的this值不可被改变,在函数的生命周期内始终保持一致。
· 不支持arguments对象 箭头函数没有arguments绑定,所以你必须通过命名参数和不定参数这两种形式访问函数的参数。
· 不支持重复的命名参数 无论在严格还是非严格模式下,箭头函数都不支持重复的命名参数;而在传统函数的规定中,只有在严格模式下才不能有重复的命名参数。
箭头函数同样也有一个name属性,这与其他函数的规则相同
箭头函数的语法
单个参数
let reflect = value => value;
// 实则上相当于;
let reflect = function (value) {
return value;
}
多个参数
用小括号将参数包裹起来
let sum = (num1, num2) => num1 + num2;
// 实则上相当于;
let sum = function (num1, num2) {
return num1 + num2;
}
没有参数
没有参数也要用小括号
let getName = () => "Nicholas";
// 实则上相当于;
let getName = function () {
return "Nicholas";
}
若希望编写由多个表达式组成的更传统的函数体,那么需要用话花括号包裹函数体,并显示地定义一个返回值
let sum = (num1, num2) => {
return num1 + num2;
};
// 实则上相当于;
let sum = function (num1, num2) {
return num1 + num2;
}
空函数
如果想创建一个空函数 一对没有内容的花括号
let doNothing = () => { };
// 实则上相当于;
let doNothing = function () { };
返回一个对象
将对象包裹在小括号里
let getTempItem = id => ({ id: id, name: "Temp" });
// 实则上相当于;
let getTempItem = function (id) {
return {
id: id,
name: "Temp"
};
};
创建立即执行函数表达式
es5写法
let person = function (name) {
return {
getName: function () {
return name;
}
}
}("Nicholas");
console.log(person.getName()); // "Nicholas"
箭头函数写法
去掉fn关键字用小括号替代 + 箭头=>
注意,小括号只包裹箭头函数定义,没有包含(“Nicholas”),这一点与正常函数有所不同,由正常函数定义的立即执行函数表达式既可以用小括号包裹函数体,也可以额外包裹函数调用的部分。
let person = ((name) => {
return {
getName: function () {
return name;
}
}
})("Nicholas");
console.log(person.getName()); // "Nicholas"
箭头函数没有this绑定
实际上,因为this绑定的是事件目标对象的引用(在这段代码中引用的是document),而没有绑定PageHandler,且由于this.doSonething()在目标document中不存在,所以无法正常执行,尝试运行这段代码只会使程序在触发事件处理程序时抛出错误
let PageHandler = {
id: "123456",
init: function () {
document.addEventListener("click", function (event) {
this.doSomething(event.type); //报错
}, false);
},
doSomething: function (type) {
console.log("Handling" + type + "for" + this.id);
}
};
PageHandler.init();
es5解法
let PageHandler = {
id: "123456",
init: function () {
document.addEventListener("click", (function (event) {
this.doSomething(event.type);
}).bind(this), false);
},
doSomething: function (type) {
console.log("Handling" + type + "for" + this.id); //Handling click for 123456
}
};
PageHandler.init();
箭头函数解法
let PageHandler = {
id: "123456",
init: function () {
document.addEventListener("click",
event => this.doSomething(event.type), false); // this指的是最近一层非箭头函数的this
// 也就是init方法里的this :就是PageHandler{}对象
},
doSomething: function (type) {
console.log(" Handling " + type + " for " + this.id);
}
};
PageHandler.init();
// 实际上等同于
"use strict";
var PageHandler = {
id: "123456",
init: function init() {
var _this = this;
document.addEventListener("click", function (event) {
return _this.doSomething(event.type);
}, false); //this指的是最近一层非箭头函数的this 也就是init方法里的this :也急速PageHandler{}对象
},
doSomething: function doSomething(type) {
console.log(" Handling " + type + " for " + this.id);
}
};
PageHandler.init();
箭头函数中没有this绑定,必须通过查找作用域链来决定其值。如果箭头函数被非箭头函数包含,则this绑定的是最近一层非箭头函数的this;否则,this的值会被设置为全局对象。
箭头函数不能用new关键字创建实例
箭头函数缺少正常函数所拥有的prototype属性,它的设计初衷是“即用即弃”,所以不能用它来定义新的类型。如果尝试通过new关键字调用一个箭头函数,会导致程序抛出错误,就像这个示例一样:
var MyType = () => { },
object = new MyType(); //报错 不能通过new关键字调用箭头函数
在这段代码中,MyType是一个没有[[Construct]]方法的箭头函数,所以不能正常执行newMyType()。也正因为箭头函数不能与new关键字混用,所以JavaScript引擎可以进一步优化它们的行为。同样,箭头函数中的this值取决于该函数外部非箭头函数的this值,且不能通过call()、apply()或bind()方法来改变this的值。
箭头函数和数组
自定义排序函数
var result = values.sort(function (a, b) {
return a - b;
});
var result = values.sort((a, b) => a - b);
诸如sort()、map()及reduce()这些可以接受回调函数的数组方法,都可以通过箭头函数语法简化编码过程并减少编码量。
箭头函数没有arguments绑定
箭头函数没有自己的arguments对象,且未来无论函数在哪个上下文中执行,箭头函数始终可以访问外围函数的arguments对象
function createArrowFunctionReturningFirstArg() {
return () => arguments[0];
}
var arrowFunction = createArrowFunctionReturningFirstArg(5);
console.log(arrowFunction()); //5
箭头函数的辨识方法
可用typeof 和 instanceof 操作符调用箭头函数与调用其他函数并无二致
var comparator = (a, b) => a - b;
console.log(typeof comparator); // function
console.log(comparator instanceof Function) // true
可在箭头函数上调用 call()、apply()、bind()、但箭头函数的this值不会受这些方法的影响
var sum = (num1, num2) => num1 + num2;
console.log(sum.call(null, 1, 2));//3
console.log(sum.apply(null, [1, 2])); //3
var boundSum = sum.bind(null, 1, 2);
console.log(boundSum()); //3
尾调用优化
尾调用:函数作为另一个函数的最后一条语句被调用
function doSomething() {
return doSomethingElse(); //尾调用
}
ECMAScript 6中的尾调用优化
若满足以下条件,则搜索引擎自动进行尾调用
- 尾调用不访问当前栈帧的变量(也就是说函数不是一个闭包)。
- 在函数内部,尾调用是最后一条语句。
- 尾调用的结果作为函数值返回。
例如
"use strict"
function doSomething() {
// 优化后
return doSomethingElse();
}
不符合
"use strict"
function doSomething() {
// 无法优化,无返回
doSomethingElse();
}
"use strict"
function doSomething() {
// 无法优化,必须在返回值后添加其他操作。 *???存疑问
return 1 + doSomethingElse();
}
"use strict"
function doSomething() {
// 无法优化,调用不在尾部
var result = doSomething();
return result;
}
"use strict"
function doSomething() {
var num = 1,
func = () => num;
// 无法优化,该函数是一个闭包
var result = doSomething();
return func();
}
如何利用尾调用优化
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);
}
小结
在ECMAScript 6中,除了一些语法改进外函数没有太大的变化,但是变得比以前更易于使用了。
现在,可以为函数定义默认参数,而在ECMAScript 6之前,可能需要在函数体内添加额外的代码来检查参数是否存在,如若不存在则需要手动赋一个默认值。
也可以为函数定义不定参数,这个数组中包含其后所有的参数,由于使用的是真实数组,且可以根据需要决定要囊括到数组中的参数,因此不定参数是一个比arguments对象更灵活的决方案。展开运算符与不定参数形似,可以通过它解构数组并将每一个元素作为函数的独立参数使用。
在ECMAScript 6以前,如果要将数组中的元素作为独立参数传递给函数,只有以下两种方式:手动指定每一个参数或使用apply()方法。只要使用展开运算符,就可以轻松地将数组传入到任何函数中,且由于不再使用apply()方法,也就不需要担心函数的this绑定问题。函数中新增的name属性,有助于通过函数名称来对其进行调试或评估。ECMAScript 6也正式定义了块级函数的行为,即使在严格模式下块级函数也不再是一个语法错误了。
在ECMAScript 6中,普通的函数调用会触发函数的[[Call]]方法调用,通过new关键字调用函数会触发函数的[[Construct]]方法调用。
新增的元属性new.target可以帮助你检测函数是通过何种方式调用的。
ECMAScript 6中函数方面最大的改变是添加了箭头函数。箭头函数的设计目标是用来替代匿名函数表达式,它的语法更简洁,具有词法级的this绑定,没有arguments对象,函数内部的this值不可被改变,因而不能作为构造函数使用。尾调用优化可以帮助函数保持一个更小的调用栈,从而减少内存的使用,避免栈溢出错误。当程序满足优化条件时,引擎会自动对其进行优化。当然,你可能希望重写递归函数,从而使引擎更好地优化你的程序。
