每个函数都是Function类型的实例,而 Function 也有属性和方法,跟其他引用类型一样
几种定义方式
- 函数声明
function sum (num1, num2) {
return num1 + num2;
}
- 函数表达式
let sum = function(num1, num2) {
return num1 + num2;
};
- 构造函数
let sum = new Function("num1", "num2", "return num1 + num2"); // 不推荐
箭头函数
ECMAScript 6 新增了使用胖箭头(=>)语法定义函数表达式的能力。
let arrowSum = (a, b) => {
return a + b;
};
let functionExpressionSum = function(a, b) {
return a + b;
};
console.log(arrowSum(5, 8)); // 13
console.log(functionExpressionSum(5, 8)); // 13
如果只有一个参数,那也可以不用括号。只有没有参数,或者多个参数的情况下,才需要使用括号:
let double = (x) => { return 2 * x; };
let triple = x => { return 3 * x; };
省略大括号的情况
// 以下两种写法都有效,而且返回相应的值
let double = (x) => { return 2 * x; };
let triple = (x) => 3 * x;
// 可以赋值
let value = {};
let setName = (x) => x.name = "Matt";
setName(value);
console.log(value.name); // "Matt"
// 无效的写法:
let multiply = (a, b) => return a * b;
箭头函数不能使用 arguments、super 和
new.target,也不能用作构造函数。此外,箭头函数也没有 prototype 属性。
函数名
因为函数名就是指向函数的指针,所以它们跟其他包含对象指针的变量具有相同的行为。
function sum(num1, num2) {
return num1 + num2;
}
console.log(sum(10, 10)); // 20
let anotherSum = sum;
console.log(anotherSum(10, 10)); // 20
sum = null;
console.log(anotherSum(10, 10)); // 20
ECMAScript 6 的所有函数对象都会暴露一个只读的 name 属性,其中包含关于函数的信息。多数情况下,这个属性中保存的就是一个函数标识符,或者说是一个字符串化的变量名。即使函数没有名称,也会如实显示成空字符串。如果它是使用 Function 构造函数创建的,则会标识成"anonymous":
function foo() {}
let bar = function() {};
let baz = () => {};
console.log(foo.name); // foo
console.log(bar.name); // bar
console.log(baz.name); // baz
console.log((() => {}).name); //(空字符串)
console.log((new Function()).name); // anonymous
如果函数是一个获取函数、设置函数,或者使用 bind()实例化,那么标识符前面会加上一个前缀:
function foo() {}
console.log(foo.bind(null).name); // bound foo
let dog = {
years: 1,
get age() {
return this.years;
},
set age(newAge) {
this.years = newAge;
}
}
let propertyDescriptor = Object.getOwnPropertyDescriptor(dog, 'age');
console.log(propertyDescriptor.get.name); // get age
console.log(propertyDescriptor.set.name); // set age
理解参数
ECMAScript 函数的参数跟大多数其他语言不同。ECMAScript 函数既不关心传入的参数个数,也不关心这些参数的数据类型。定义函数时要接收两个参数,并不意味着调用时就传两个参数。你可以传一个、三个,甚至一个也不传,解释器都不会报错。
之所以会这样,主要是因为 ECMAScript 函数的参数在内部表现为一个数组。函数被调用时总会接收一个数组,但函数并不关心这个数组中包含什么。如果数组中什么也没有,那没问题;如果数组的元素超出了要求,那也没问题。事实上,在使用 function 关键字定义(非箭头)函数时,可以在函数内部访问 arguments 对象,从中取得传进来的每个参数值。
arguments 对象是一个类数组对象(但不是 Array 的实例),因此可以使用中括号语法访问其中的元素(第一个参数是 arguments[0],第二个参数是 arguments[1])。而要确定传进来多少个参数,可以访问 arguments.length 属性。
//在下面的例子中,sayHi()函数的第一个参数叫 name:
function sayHi(name, message) {
console.log("Hello " + name + ", " + message);
}
//可以通过 arguments[0]取得相同的参数值。因此,把函数重写成不声明参数也可以:
function sayHi() {
console.log("Hello " + arguments[0] + ", " + arguments[1]);
}
箭头函数中的参数
如果函数是使用箭头语法定义的,那么传给函数的参数将不能使用 arguments 关键字访问,而只能通过定义的命名参数访问。
console.log(arguments[0]);
}
foo(5); // 5
let bar = () => {
console.log(arguments[0]);
};
bar(5); // ReferenceError: arguments is not defined
虽然箭头函数中没有 arguments 对象,但可以在包装函数中把它提供给箭头函数:
function foo() {
let bar = () => {
console.log(arguments[0]); // 5
};
bar();
}
foo(5);
注意 ECMAScript 中的所有参数都按值传递的。不可能按引用传递参数。如果把对象作为参数传递,那么传递的值就是这个对象的引用。
和子由渑池怀旧
人生到处知何似,应似飞鸿踏雪泥。
泥上偶然留指爪,鸿飞那复计东西。
老僧已死成新塔,坏壁无由见旧题。
往日崎岖还记否,路人长困蹇驴嘶。