1.箭头函数
ES6新增属性。箭头函数特别适合嵌入函数的场景。
//只有一个参数,括号可以省略
let double = x => {return 2 * x};
let tripble = (x) => {return 2 * x};
//没有参数需要括号
let getRandom = () => {return Math.random();};
//有多个参数需要括号
let sum = (a, b) => {return a + b}
/**
* 箭头函数也可以不使用大括号,但这会改变函数的行为。如果不使用大括号
* 那么箭头后面就只能有一行代码(赋值操作或者表达式),而且省略大括号
* 会隐式返回这行代码的值。
*/
let fun1 = (x) => {return 2 * x;}
let fun2 = (x) => 2 * x; //等于上面的写法
let fun3 = (x) => return 2 * x ; //无效写法
箭头函数虽然语法简介,但是很多场合不适用。箭头函数不能使用arguments、super、new.target,也不能用作构造函数。箭头函数也没有protptype属性。
2.函数名
因为函数名就是指向函数的指针,所以它们跟其他包含对象指针的变量具有相同的行为。
在ES6中,所有的函数对象都会暴露一个只读的name属性,其中包含关于函数的信息。
function sum(num1, num2) { return num1 + num2; }
console.log(sum(1,2)); //3
let anotherSum = sum;
sum = null;
console.log(anotherSum(1,2)); //3
console.log(anotherSum.name) //sum 返回的是方法名
anotherSum.name = '小马哥';
console.log(anotherSum.name) //sum name属性只读,不能修改
3.理解参数
ECMAScript函数即不关心传入的参数个数,也不关心这些参数的数据类型。定义函数时要接收2个参数,并不意味着调用的时候就要传入2个参数,可以传1个,3个甚至一个都不传。事实上,在使用function关键字定义(非箭头)函数时,可以在函数的内部访问arguments对象,从中获取传进来的每个参数值。arguments是一个类数组对象(不是Array的实例)
function sayHi(name, message) {
console.log(arguments.length); //2
return '小马哥对' + name + '说' + message;
}
function sayHello() {
console.log(arguments.length); //2
return '小马哥对' + arguments[0] + '说' + arguments[1];
}
let sayHello2 = () => {
return '小马哥对' + arguments[0] + '说' + arguments[1];
}
console.log(sayHi('韩梅梅', '今天加班')) //小马哥对韩梅梅说今天加班
console.log(sayHello('韩梅梅', '今天加班')) //小马哥对韩梅梅说今天加班
console.log(sayHello2('韩梅梅', '今天加班')) //报错
箭头函数中的参数:如果函数是使用箭头函数语法定义的,那么传给函数的参数不能使用arguments关键字访问,而只能通过定义的命名参数访问。
function foo(){
let sayHello = () => {
console.log( '小马哥对' + arguments[0] + '说' + arguments[1]); //小马哥对韩梅梅说今天加班
}
sayHello();
}
foo('李雷','你也加班');
4.没有重载
ECMAScript函数如果定义了同名函数,则后面定义的会覆盖先定义的。
5.默认参数值
在ECMAScript5.1及以前,实现默认参数的一种常用方式就是检测某个参数是否等于undefined。在ES6开始可以支持显示定义默认参数。
function sayName(name = '小马哥') {
console.log('myName==' + name);
}
sayName('孙红雷'); //myName==孙红雷
sayName(); //myName==小马哥
6.参数扩展与收集
ES6新增了扩展操作符,使用它可以非常简洁地操作和组合数据。扩展操作符最有用的场景就是函数定义中的参数列表。
- 6.1扩展参数:对于可迭代对象应用扩展操作符,并将其作为一个参数传入,可以将可迭代对象拆分,并将返回的每个值单独传入。
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.apply(null, values)); //10---es5写法
console.log(getSum(-1,...values,5)); //14
console.log(getSum(...values,5,6,7,8,9,10));//55
console.log(getSum(...values,...[5,6,7,8,9,10]));//55
console.log(getSum(5,...values,...[5,6,7,8,9,10]));//60
- 6.2收集参数: 在构思函数定义时,可以使用扩展操作符把不同长度的独立参数组合为一个数组。类似于arguments对象的构造机制,只不过收集参数的结果会得到一个Array实例。
let ignorFirst = (firstValue, ...values) => {
console.log(values);
}
ignorFirst() // []
ignorFirst(1,2,3,4,5) //[2, 3, 4, 5]
7.函数内部(arguments,this,new.target)
- arguments对象:这个对象只有以function关键字定义函数时候才会有,主要用于包含函数参数。arguments还有一个callee属性,是指向arguments对象所在函数的指针。
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1); //指向当前对象所在的函数
}
}
let trueFactorial = factorial;
factorial = function () {
return 0;
}
console.log(trueFactorial(5)) //120
console.log(factorial(5)) //0
- this对象:在标注函数(指向调用该函数的上下文)和箭头函数中(指向定义该函数的上下文)有不同的行为。
在标准函数中:this引用的是把函数当成方法调用的上下文对象,通常这个时候,称其为this值(在网页的全局上下文中调用函数时,this指向windows)。
window.color = 'red';
let o = {
color: 'blue',
}
function sayColor() {
console.log(this.color);
}
sayColor() //red
o.sayColor = sayColor;
//调用的时候,函数调用者变更为o,this指向o
o.sayColor(); //blue
在箭头函数中:this引用的是定义箭头函数的上下文,在事件回调或者定时回调中调用某个函数,this指向的并非想要的对象。此时将回调函数写成箭头函数就可以解决这个问题。因为箭头函数中的this会保留定义该函数时的上下文。
window.color = 'red';
let o = {
color: 'blue',
}
let sayColor = () => console.log(this.color);
sayColor() //red
o.sayColor = sayColor;
o.sayColor(); //red
- caller属性:这个属性引用的是调用当前函数的函数,或者如果的在全局作用域中调用的则为null。
function outer(){
let arr =[];
for (let i=0;i<20;i++){
arr[i] = i;
}
inner();
}
function inner(){
let arr =[];
for (let i=0;i<20;i++){
arr[i] = i;
}
console.log(inner.caller) //outer的源代码
console.log(arguments.callee.caller) //等价于上面
}
outer();
- new.target属性(ES6新增): 检测函数是否使用new关键字调用的new.target属性。如果函数是正常调用的则new.target的值是undefined,反之将引用被调用的构造函数。
8.函数属性与方法(length,prototype,apply(),call())
- length:表示该函数方法的入参个数。
- prototype:保存引用类型所有实例方法的地方,就意味着toString()、valueOf()等方法都保存在prototype上,进而由所有实例共享。在ES5中,prototype属性是不可枚举的,因此使用for-in循环不会返回这个属性。
- apply():这个方法都会以指定的this值来调用函数,即会设置调用函数时函数体内的this对象的值。apply()接收两个参数:函数体内this的值和一个参数数组。第二个参数可以是Array的实例,但也可以是arguments对象。
- call():方法作用和apply()一样,只是传参的形式不同,第一个参数是this,后面的参数必须一个一个列出来。
function sum(num1,num2){
return num1 + num2;
}
function sum2(num1,num2){
return sum.apply(this,arguments)
}
function sum3(num1,num2){
return sum.call(this,num1,num2);
}
console.log(sum2(20,30)); //50
console.log(sum3(20,30)); //50
apply()和call(),更重要的作用是控制函数调用上下文,即函数体内this值的能力。
window.color = 'red';
let o = {
color: 'blue'
};
function sayColor() {
console.log(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.apply(window); //red
sayColor.apply(o); //blue
sayColor.call(o); //blue
9.闭包
匿名函数经常被人误以为是闭包。闭包指的是那些引用了另一个函数作用域变量的函数,通常是在嵌套函数中实现的。
function compare(propertyName) {
return function (object1, object2) {
//内部函数(匿名函数)引用了外部函数的变量propertyName
let value1 = object1[propertyName];
let value2 = object2[propertyName];
return value1 === value2 ? 0 : value1 > value2 ? 1 : -1;
}
}
在这个内部函数被返回并在其他地方被使用后,它仍然引用着哪个变量。因为这是因为内部函数的作用域链包含compare()函数的作用域。
10.this 对象
在闭包中使用this会让代码变复杂。如果内部函数没有使用箭头函数定义,则this对象会在运行时绑定到执行函数的上下文。如果在全局函数中调用,非严格模式下,this等于window。
window.identity = 'The Window';
let object = {
identity: 'My Object',
getIdentityFunc() {
return function () {
return this.identity;
}
}
}
console.log(object.getIdentityFunc()()) //The Window
object.getIdentityFunc()返回函数,所以object.getIdentityFunc()()立即调用返回的函数,从而得到一个字符串。每个函数在被调用的时候都会创建两个特殊的变量:this和arguments。内部函数用于不可能直接(可以间接,箭头函数或者使用变量)访问外部函数的这两个变量。
11.立即调用的函数表达式
立即调用的函数表达式又称为立即嗲用的函数表达式(IIFE)。类似于函数声明,但是由于被包含在括号中,所以会被解释为函数表达式。紧跟在第一组括号后面的第二组括号会立即调用前面的函数表达式。在ES5.1及以前,为了防止变量定义外泄,IIFE是个非常有效的方式。也不会导致闭包相关的内存问题,因为不存在对这个函数的引用,为此,只要函数执行完毕,其作作用域链就可以被销毁。
(function () {
//块级作用域
})();