语法:(参数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
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
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 对象的引用.确保其内存可以在适当的时候被回收。 }