箭头函数
语法:(参数1,参数2,...)=>{方法体};
如果只有一个参数,那也可以不用括号。只有没有参数,或者多个参数的情况下,才需要使用括号。
注:箭头函数虽然语法简洁,但也有很多场合不适用。箭头函数不能使用arguments、super 和new.target,也不能用作构造函数。此外,箭头函数也没有prototype 属性。
函数名
函数名就是指向函数的指针,所以它们跟其他包含对象指针的变量具有相同的行为。
使用不带括号的函数名会访问函数指针,而不会执行函数。
function sum(num1, num2) {
return num1 + num2;
}
console.log(sum(10, 10)); // 20
let anotherSum = sum; //使用不带括号的函数名会访问函数指针,而不会执行函数,anotherSum 和sum 都指向同一个函数
console.log(anotherSum(10, 10)); // 20
sum = null; //切断了它与函数之间的关联。而anotherSum()还是可以照常调用
console.log(anotherSum(10, 10)); // 20
理解参数
ECMAScript 函数既不关心传入的参数个数,也不关心这些参数的数据类型。定义函数时要接收两个参数,并不意味着调用时就传两个参数。你可以传一个、三个,甚至一个也不传,解释器都不会报错。
在使用function 关键字定义(非箭头)函数时,可以在函数内部访问arguments 对象,从中取得传进来的每个参数值。
arguments 对象是一个类数组对象(但不是Array 的实例),因此可以使用中括号语法访问其中的元素(第一个参数是arguments[0],第二个参数是arguments[1])。而要确定传进来多少个参数,可以访问arguments.length 属性。
function howManyArgs() {
let result = 0;
for(let i=0;i<arguments.length;i++){
result = result + arguments[i]
}
console.log(result);
}
howManyArgs(); // 0
howManyArgs(10, 20); // 30
howManyArgs(10); // 10
howManyArgs(20,30,40); // 90
arguments 对象其实还有一个callee 属性,是一个指向arguments 对象所在函数的指针。
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * factorial(num - 1);
}
}
阶乘函数一般定义成递归调用的,就像上面这个例子一样。只要给函数一个名称,而且这个名称不会变,这样定义就没有问题。但是,这个函数要正确执行就必须保证函数名是factorial,从而导致了紧密耦合。
使用arguments.callee 就可以让函数逻辑与函数名解耦:
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1);
}
}
这个重写之后的factorial()函数已经用arguments.callee 代替了之前硬编码的factorial。
这意味着无论函数叫什么名称,都可以引用正确的函数。
没有重载
ECMAScript函数没有签名,因为参数是由包含零个或多个值的数组表示的。没有函数签名,自然也就没有重载。
如果在ECMAScript中定义了两个同名函数,则后定义的会覆盖先定义的。来看下面的例子:
function addSomeNumber(num) {
return num + 100;
}
function addSomeNumber(num) {
return num + 200;
}
let result = addSomeNumber(100); // 300
扩展参数
let values = [1, 2, 3, 4];
function getSum() {
let sum = 0;
for (let i = 0; i < arguments.length; ++i) {
sum += arguments[i];
}
return sum;
}
console.log(getSum(...values)); // 10
//因为数组的长度已知,所以在使用扩展操作符传参的时候,并不妨碍在其前面或后面再传其他的值,包括使用扩展操作符传其他参数
console.log(getSum(-1, ...values)); // 9
console.log(getSum(...values, 5)); // 15
console.log(getSum(-1, ...values, 5)); // 14
console.log(getSum(...values, ...[5,6,7])); // 28
this
- 在标准函数中,this 引用的是把函数当成方法调用的上下文对象,这时候通常称其为this 值(在网页的全局上下文中调用函数时,this 指向windows)。
window.color = 'red';
let o = {
color: 'blue'
};
function sayColor() {
console.log(this.color);
}
sayColor(); // 'red' 在全局上下文中调用sayColor(),this 指向window
o.sayColor = sayColor; //sayColor()赋值给o
o.sayColor(); // 'blue' this 会指向对象o
- 在箭头函数中,this 引用的是定义箭头函数的上下文.
window.royaltyName = 'zhangsan';
function King() {
this.royaltyName = 'lisi';
// this 引用King 的实例
setTimeout(() => console.log(this.royaltyName), 1000);
}
function Queen() {
this.royaltyName = 'wangwu';
// this 引用window 对象
setTimeout(function() { console.log(this.royaltyName); }, 1000);
}
new King(); // lisi
new Queen(); // zhangsan
闭包
匿名函数经常被人误认为是闭包(closure)。闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。
内存泄漏
把HTML 元素保存在某个闭包的作用域中,就相当于宣布该元素不能被销毁。
function assignHandler() {
let element = document.getElementById('someElement');
element.onclick = () => console.log(element.id);//匿名函数引用着assignHandler()的活动对象,阻止了对element 的引用计数归零。即内存不会被回收。
}
修改
function assignHandler() {
let element = document.getElementById('someElement');
let id = element.id;//闭包改为引用一个保存着element.id 的变量id,从而消除了循环引用。
element.onclick = () => console.log(id);
element = null;//闭包没有直接引用element,包含函数的活动对象上还是保存着对它的引用.把element 设置为null。就解除了对这个COM 对象的引用.确保其内存可以在适当的时候被回收。
}