重读《深入理解ES6》—— 函数

1,299 阅读4分钟

在 ES6 之前,JavaScript 的函数语法几乎没有发生太大的变化,而遗留的一些问题导致实现一些基本的功能经常要编写很多代码。而 ES6 对函数的大力度改进,让我们编写函数时更加方便且更少出错。

而关于参数的处理以及箭头函数是我非常喜欢的功能。

一、函数形参的默认值

JavaScript 中的函数有一个比较特别的地方是形参和实参的数量可以不相同。当定义的形参没有默认值传入的时候,JavaScript 会为我们自动传入一个默认值。

在 ES5 的阶段,我们通常会自己设置一个默认值,就像下面这样:

function getAge(age) {
    age = age || 10;
    console.log(age);
}

getAge(12); // 12
getAge(0); // 10

这段代码想告诉我们,当传入参数时,我们就取传入的值,如果没有传入,我们就取默认的 10。但是这通常会有一个问题,当我们传入一个 0 的时候,它也会被默认为一个假值,并最终将 age 赋值为 10。

在这种情况下,我们往往通过 typeof 检查参数类型,来实现相关功能:

function getAge(age) {
    age = (typeof age !== "undefined") ? age : 10;
    console.log(age);
}

getAge(12); // 12
getAge(0); // 10

这种写法可以帮助我们更好地处理参数,但仔细想想,若是有多个参数时,这会让我们写许多额外的代码。幸好 ES6 参数默认值设置的出现,让我们可以很好的解决这个问题。

function getAge(age=10) {
    console.log(age);
}
getAge(); // 10,使用默认值
getAge(12); // 12,不使用默认值
getAge(0); // 0,不适用默认值,且传入参数为假值的情况

值得注意的一点是:

function getAge(age=10, newAge=age) {
	console.log(newAge);
}
getAge(); // 10

你可以把第二个参数的默认值设置为第一个参数,但是不能把第一个参数的默认值设置为第二个参数。

另一方面,函数的默认值还可以是一个表达式:

function getValue() {
    return 3;
}

function add(first, second=getValue()) {
    return first + second;
}

add(1, 3); // 4
add(2); // 5

二、箭头函数

箭头函数是一个比较有趣的特性。因为简单的语法,大家工作中也经常用到,所以大家也比较熟悉,下面就简单说说箭头函数的语法。

箭头函数有1个参数,且函数体只有一条语句

let f = value => value;

// 相当于

let f = function(value) {
    return value;
};

箭头函数没有参数或者有多个参数,参数部分需要使用小括号

let f = () => 20;
// 相当于 
let f = function() {
    return 20
};

let f = (a, b) => a + b;
// 相当于
let f = function(a, b) {
    return a + b;
}

箭头函数的函数体内有多条语句

let f = (a, b) => {
    return a * b;
};

想让箭头函数返回一个对象字面量,需要将该字面量包裹在小括号里

let f = () => ({ id: 1, name: 'zhangsan', age: 18 });

// 相当于

let f = function() {
    return {
        id: 1,
        name: 'zhangsan',
        age: 18
    };
}

简单的介绍完箭头函数的基本语法,我们来看看箭头函数的一些主要特性:

1、箭头函数没有 this 绑定

箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值。如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this ;否则,this 的值会被设置为全局对象。

或者再简单一点来说:箭头函数的 this 指向离它最近的父级作用域

举个例子:

// 普通函数
let person = {
    name: 'zhangsan',
    age: 18,
    say: function() {
        console.log(this); // person 对象
        console.log(this.name); // 'zhangsan'
        console.log(this.age);  // 18
    }
};

person.say(); // 普通函数,谁调用,this 指向谁
// 箭头函数
let person = {
    name: 'zhangsan',
    age: 18,
    say: () => {
        console.log(this); // window 对象
        console.log(this.name); // undefined
        console.log(this.age);  // undefined
    }
};

person.say(); // 箭头函数,没有 this,函数内部的 this,指向父级作用域,即全局

最后,因为箭头函数中没有 this ,所以也不能用 call()、apply()、bind() 等方法改变 this 的指向。

2、箭头函数没有原型,不能通过 new 来调用

箭头函数的设计初衷是”即用即弃“,所以不能用它来定义新类型。所以,箭头函数中没有 prototype 属性,自然不能实例化,也就是不能用 new 关键字来调用。

let Person = () => {};
let p1 = new Person(); // Uncaught TypeError: Person is not a constructor

let Person = () => {};
console.log(Person.prototype); // undefined
3、没有 argments 绑定
// 普通函数
let person = function() {
    console.log(arguments);
}
person(1); // 打印 arguments 对象
// 箭头函数
let person = () => {
    console.log(arguments); 
}

person(1); // Uncaught ReferenceError: arguments is not defined

总的来说,箭头函数和一些小特性的加入,可以使我们的编码更加方便。

友情链接

重读《深入理解ES6》 —— 块级作用域

重读《深入理解ES6》 —— 模板字符串

如果文章中错误或表述不严谨的地方,欢迎指正