参数默认值
// 默认值最好放在后面,即尾参数
function Point(x = 0, y = '') {
// 构造函数默认返回 this,即实例
this.x = x;
this.y = y;
// 参数不能再次声明
let x = 1; // SyntaxError: Identifier 'x' has already been declard
const y = 2; // SyntaxError: Identifier 'x' has already been declard
}
// 但是这样却可以
function foo(x = 1) {
var x = 3;
console.log(x);
}
foo(); // 3
// 或
function foo(x) {
var x = 3;
console.log(x);
}
foo(); // 3
// 参数同名
// 允许
function foo (x, x, y) {}
// SyntaxError: Duplicate parameter name not allowed in this context
function foo(x, x, y = 1) {}
// 惰性求值
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo(); // 100
x = 100;
foo(); // 101
// 解构赋值1️⃣
function foo({x, y = 5}) {
console.log(x,y);
}
foo({}); // undefined 5
foo({x:1,y:2}); // 1 2
foo({x:1}); // 1 5
foo(); // TypeError: Cannot destructure property `x` of 'undefined' or 'null'
// 解构赋值2️⃣
function foo({x, y = 5} = {}) {
console.log(x, y);
}
foo(); // undefined 5
// 解构赋值3️⃣
// 默认空对象,x y 任何时候都有值
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
// 默认{x: 0, y: 0}
function m2({x, y} = {x: 0, y: 0}) {
return [x, y];
}
m1(); // [0, 0]
m2(); // [0, 0]
m1({x: 3, y: 8}); // [3, 8]
m2({x: 3, y: 8}); // [3, 8]
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]
m1({}) // [0, 0];
m2({}) // [undefined, undefined]
m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
// 解构赋值4️⃣
function f(x = 1, y) {
return [x, y];
}
f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // Uncaught SyntaxError: Unexpected token ,
f(undefined, 1) // [1, 1]
// 解构赋值5️⃣
function f(x, y = 5, z) {
return [x, y, z];
}
f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // Uncaught SyntaxError: Unexpected token ,
f(1, undefined, 2) // [1, 5, 2]
// 传入 undefined 触发默认值,而 null 不会
function foo(x = 5, y = 6) {
console.log(x, y);
}
foo(undefined, null); // 5 null复制代码
length 属性
返回没有默认值的参数个数,计算最后一个有默认值的参数前面的参数个数
(function(x){}).length; // 1
(function(x = 1){}).length; // 0
(function(x, y = 1){}).length; // 1
(function (a = 0, b, c) {}).length; // 0
(function (a, b = 1, c) {}).length; // 1
(function(...args) {}).length; // 0复制代码
作用域
函数参数有默认值时,在函数初始化时在参数上会行程单独的作用域(context)
// 1️⃣
var x = 1;
function f(x, y = x) { // y 指向第一个参数 x
console.log(y);
}
f(2); // 2
// 2️⃣
let x = 1;
function f(y = x) { // y 延
let x = 2;
console.log(y);
}
f(); // 1
// 3️⃣
function f(y = x) {
let x = 2;
console.log(y);
}
f(); // ReferenceError: x is not defined
// 4️⃣
var x = 1;
function foo(x = x) {
}
foo(); // ReferenceError: x is not defined
// 5️⃣
let foo = 'outer';
function bar(func = () => foo) {
let foo = 'inner';
console.log(func());
}
bar(); // outer
// 6️⃣
function bar(func = () => foo) {
let foo = 'inner';
console.log(func());
}
bar() // ReferenceError: foo is not defined
// 7️⃣
var x = 1;
function foo(x, y = function() { x = 2; }) { // 匿名函数内部的 x 指向第一个参数 x
var x = 3; // 和参数中的x不是一个作用域
console.log(y(), x);
}
foo(); // undefined 3
x; // 1
// 8️⃣
var x = 1;
function foo(x, y = function() { x = 2; }) {
x = 3;
console.log(x, y(), x);
}
foo(); // 3 undefined 2
x; // 1复制代码
rest 参数
// rest 参数本身是数组
function add(...values) {
let sum = 0;
for (let val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3); // 10
function sortNumbers() {
// arguments 是类数组对象
return Array.prototype.slice.call(arguments);
}
// 同上
const sortNumbers = (...numbers) => numbers.sort();
// rest 参数后不能再有其他参数
function f(a, ...b, c){} // Uncaught SyntaxError: Rest Parameter must be last formal parameter复制代码
严格模式
只要函数参数使用了默认值、解构赋值或扩展运算符,那函数内部就不能用显式设定严格模式。因为函数参数先执行(可能是非严格模式),但是到了函数内部中时发现是严格模式,所以报错。
// Uncaught SyntaxError: Illegal 'use strict' directive in function with non-simple parameter list
function doSomething(a, b = a) {
'use strict';
}
const doSomething = function ({a, b}) {
'use strict';
};
const doSomething = (...a) => {
'use strict';
};
const obj = {
doSomething({a, b}) {
'use strict';
}
};
// 解决办法
'use strict';
function doSomething(a, b = a) {}
// 或
const doSomething = (function() {
'use strict';
return function(value = 42) {
return value;
}
}());复制代码
name 属性
function foo() {}
foo.name; // foo
var f = function () {};
// ES5
f.name; // ""
// ES6
f.name; // "f"
const bar = function baz() {};
// ES5 ES6
bar.name; // "baz"
(new Function).name; // "anonymous"
function foo() {}
foo.bind({}).name; // "bound foo"
(function () {}).bind({}).name; // "bound "复制代码
箭头函数
- 若返回对象需要用括号包起来
- 函数体内的 this 是定义时所在对象,而不是使用时所在的对象,即固定的
// setTimeout 内 this 指向 window function foo() { setTimeout(function() { console.log('id:', this.id); }, 100); } var id = 21; foo.call({ id: 42 }); // 21 // setTimeout 内 this 指向 {id: 42},定义时确定且不会改变 function foo() { setTimeout(() => { console.log('id:', this.id); }, 100); } var id = 21; foo.call({ id: 42 });复制代码
- 不可以当构造函数
Uncaught TypeError: fn is not a constructor at <anonymous>:1:1
- 没有 arguments 对象
- 不可以用 yield 命令
let getTempItem = id => {id: id, name: 'Temp'}; // Uncaught ReferenceError: Function is not defined;
// 不需要返回值
let fn = () => void doesNotReturn();
// 利用箭头函数自动将传参以数组返回
const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]
// pipeline 机制
const pipeline = (...funcs) => val => funcs.reduce((a, b) => b(a), val);
const plus1 = a => a + 1;
const mult2 = a => a * 2;
const addThenMult = pipeline(plus1, mult2);
addThenMult(5); // 12复制代码
尾调用
尾调用只在严格模式下开启,正常模式无效,即 func.arguments(返回调用时函数的参数) 和func.caller (返回调用当前函数的那个函数)俱无效。
尾递归
// 尾递归优化
function sum(x,y){
if (y > 0) {
return sum(x+1, y-1);
} else {
return x;
}
}
// 会发生RangeError: Maximum call stack size exceed,即堆栈溢出
sum(1, 100000);
// 蹦床函数
function trampoline(f) {
while(f && f instanceof Function) {
f = f();
}
return f;
}
// 每次sum函数执行都会返回自身的另一个版本
function sum(x, y) {
if (y > 0) {
return sum.bind(null, x + 1, y - 1);
}
return x;
}
trampoline(sum(1, 100000));复制代码