使用“箭头”(=>)定义函数。
var f = v => v;
// ES 5
var f = function(v) {
return v;
};
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
var f = () => 5;
var sum = (num1, num2) => num1 + num2;
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回
var sum = (num1, num2) => { return num1 + num2; }
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号
var getTempItem = id => ({ id: id, name: "Temp" });
call, apply, bind无法改变this
var foo = () => console.log(this);
foo.call({a:123}); // window
foo.apply({a:123}); // window
foo.bind({a:123})(); // window
不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。【纯词法作用域的this和arguments,可看做普通变量】
不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误
不可以使用yield命令,因此箭头函数不能用作Generator函数
形参和=>之间不能换行,否则会报语法错误
Function("x\n => 2"); // Uncaught SyntaxError: Unexpected token =>
逻辑运算优先级高于箭头函数
0 || () => 2; // Uncaught SyntaxError: Unexpected token )
没有prototype属性
内部也没有super, new.target绑定
使用默认参数,在函数体的检查就不再需要了
function multiply(a, b = 1) {
return a*b;
}
multiply(5); // 5
定义了默认值的参数,如果传入的是undefined将使用默认值
function multiply(a, b = 2) {
return a*b;
}
multiply(5, undefined); // 10
默认参数可以使用前面的参数
function multiply(a, b = a) {
return a*b;
}
multiply(5); // 25
arguments不包含默认参数(调用时传入啥就是啥)
function test(a = 1, b = 2, c = 3) {
console.log(arguments);
}
test(); // []
test(1); // [1]
test(undefined); // [undefined]
TEMPORAL DEAD ZONE(TDZ)
var x = 'outer scope x';
var y = 'outer scope y'
(function() {
console.log(x); // undefined
console.log(y); // ReferenceError: y is not defined
var x = 'inner scope x';
let y = 'inner scope y';
}());
分隔的作用域
(function() {
var outer = 123;
(function(a = function() {
console.log('outer:', typeof outer); // number
console.log('inner:', typeof inner); // undefined
}) {
var inner = 1;
a();
} ())
} ())
new Function支持
new Function('a = 1', 'b = 2', 'return a*b;')(); // 2
定义了默认参数的参数将不记入function.length中
(function(a){}).length // 1
(function(a = 5){}).length // 0
(function(a, b, c = 5){}).length // 2
还可以利用此特性设置某些参数不可省略
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
foo()
// Error: Missing parameter
剩余参数语法允许将不确定数量的参数表示为数组。
function multiply(multiplier, ...theArgs) {
return theArgs.map(x => multiplier * x);
}
var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]
剩余参数同样也不记入function.length中
function multiply(multiplier, ...theArgs) {
return theArgs.map(x => multiplier * x);
}
console.log(multiply.length); // 1
不能在setters中使用
var obj = {
set e(...args) {
this._e = args;
},
get e() {
return this._e;
}
};
// SyntaxError: Setter must have exactly one formal parameter.
new Function支持
new Function('a', '...b', 'console.log(b)')(1,2,3,4); // [2, 3, 4]
它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
应用:
合并数组
// ES5
[1, 2].concat(more)
// ES6
[1, 2, ...more]
字符串转化成数组
[...'hello']
// [ "h", "e", "l", "l", "o" ]
这个属性早就被浏览器广泛支持,但是直到ES6,才将其写入了标准。
* 如果将一个匿名函数赋值给一个变量,ES5的name属性,会返回空字符串,而ES6的name属性会返回实际的函数名
```javascript
var func1 = function () {};
// ES5
func1.name // ""
// ES6
func1.name // "func1"
```
如果将一个具名函数赋值给一个变量,则ES5和ES6的name属性都返回这个具名函数原本的名字。
const bar = function baz() {};
// ES5
bar.name // "baz"
// ES6
bar.name // "baz"
bind返回的函数,name属性值会加上“bound ”前缀
function foo() {};
foo.bind({}).name // "bound foo"
(function(){}).bind({}).name // "bound "
Function构造函数返回的函数实例,name属性的值为“anonymous”
(new Function).name // "anonymous"
对象中的方法
var o = { foo: function(){}, bar: function baz(){}};
o.qux = function(){};
o.foo.name === "foo" &&
o.bar.name === "baz" &&
o.qux.name === "";
getter和setter
var o = { get foo(){}, set foo(x){} };
var descriptor = Object.getOwnPropertyDescriptor(o, "foo");
descriptor.get.name === "get foo" &&
descriptor.set.name === "set foo";
对象简写
var o = { foo(){} };
o.foo.name === "foo";
var f = "foo";
({f() { return f; }}).f() === "foo";
[*]Symbol作为key的方法
var sym1 = Symbol("foo");
var sym2 = Symbol();
var o = {
[sym1]: function(){},
[sym2]: function(){}
};
o[sym1].name === "[foo]" &&
o[sym2].name === "";
class申明中的name
class foo {};
class bar { static name() {} };
foo.name === "foo" &&
typeof bar.name === "function";
class表达式中的name与申明保持一致
经class表达式赋值后的变量的name
var foo = class {};
var bar = class baz {};
var qux = class { static name() {} };
foo.name === "foo" &&
bar.name === "baz" &&
typeof qux.name === "function";
对象中的class表达式name
var o = { foo: class {}, bar: class baz {}};
o.qux = class {};
o.foo.name === "foo" &&
o.bar.name === "baz" &&
o.qux.name === "";
原型链上的方法和静态方法的name
class C { foo(){} };
(new C).foo.name === "foo";
class C { static foo(){} };
C.foo.name === "foo";
name的描述属性
var descriptor = Object.getOwnPropertyDescriptor(function f(){},"name");
descriptor.enumerable === false &&
descriptor.writable === false &&
descriptor.configurable === true; //不可写,不可枚举,可配置,可删除
继承Function的类可以通过new正常调用创建function,并拥有正确的原型链
class C extends Function {}
var c = new C("return 'foo';");
c() === 'foo';
c instanceof C && c instanceof Function && Object.getPrototypeOf(C) === Function;
通过继承的类创建的function可以使用new调用,也可以调用原型链上的call, apply, bind方法
bind不会改变函数的原型
function correctProtoBound(proto) {
var f = function(){};
if (Object.setPrototypeOf) {
Object.setPrototypeOf(f, proto);
} else {
f.__proto__ = proto;
}
var boundF = Function.prototype.bind.call(f, null);
return Object.getPrototypeOf(boundF) === proto;
}
correctProtoBound(Function.prototype) && correctProtoBound({}) && correctProtoBound(null);
generator, arrow function, class, subclass bind后原型也都不会改变
var fn = function(a, b) {};
var desc = Object.getOwnPropertyDescriptor(fn, "length");
if (desc.configurable) {
Object.defineProperty(fn, "length", { value: 1 });
fn.length === 1;
}
第一次明确规定,所有ECMAScript的实现,都必须部署“尾调用优化”,这就是说,在ES6中,只要使用尾递归,就不会发生栈溢出,相对节省内存。
// 尾调用
function f(x){
return g(x);
}
// 尾递归
function factorial(n, total) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}
尾调用和尾递归优化后的调用栈深度始终为1,因为在进行尾部函数调用的时候前面的函数调用环境已经销毁,节省内存开销,不会发生栈溢出。