目录:
function foo( a, b ) {
// 为什么不能写 a = a || 1; b = b || 2, 因为一旦用户传递0进来就也会走进默认值, 这是说不通的
a = a === undefined ? 1 : a;
b = b === undefined ? 2 : b;
return a + b;
}
console.log(foo(2, 3)); // 输出5
console.log(foo(6)); // 输出8
上面这样给默认值, 其实让我们的代码量增加了这个是小事, 主要是也不利于阅读和维护。特别是在参数一多的情况下
在书写形参的时候, 直接给形参以赋值的形式设置默认值, 当函数调用的时候, 如果传递的相应实参为undefined, 系统将会使用默认值
// a = 1, b = 2就是给了默认值了
function foo( a = 1, b = 2) {
return a + b;
}
console.log(foo(2, 3)); // 输出5
console.log(foo(6)); // 输出8
有了这个参数默认值以后, 我们阅读代码其实更加方便了, 书写起来也不用这么繁琐了, 这是参数默认值带给我们最直接的好处
在ES5中, 我们知道arguments列表和形参列表是相互映射的, 你变我也变得那种, 但是在开启严格模式下失效, 同样当我们使用ES6的参数默认值语法时, arguments将不再与形参列表相互映射
function test( a, b ) {
console.log( a, b ); // 输出1, 2
console.log( arguments.a, arguments.b ); // 输出1, 2
a = 10;
arguments.b = 30;
console.log( a, b ); // 输出10, 30
console.log( arguments.a, arguments.b ); // 输出10, 30
}
test(1, 2);
function foo( a = 2, b = 3 ) {
console.log( a, b ); // 输出1, 2
console.log( arguments.a, arguments.b ); // 输出1, 2
a = 10;
arguments.b = 30;
console.log( a, b ); // 输出10, 2
console.log( arguments.a, arguments.b ); // 1, 30
}
foo(1, 2)
"use strict"
function demo( a, b ) {
console.log( a, b ); // 输出1, 2
console.log( arguments.a, arguments.b ); // 输出1, 2
a = 10;
arguments.b = 30;
console.log( a, b ); // 输出10, 2
console.log( arguments.a, arguments.b ); // 1, 30
}
demo(1, 2);
只要我们给了形参默认值的语法, 形参将会和let, const一样开启暂时性死区
function demo( a, b = a ) {
return a + b;
}
console.log(demo(1)); // 输出2
function foo( a = b, b ) {
return a + b;
}
console.log(foo(1, 2)); // 直接报错, 因为在b还没有声明之前就使用了b
假设有一个累加函数, 累加函数的参数是没办法限制的, 我们要让他可以传两个数字累加, 也可以10个, 可以100个, 所以在ES6以前我们往往这么处理
// 1. 强制调用者传递数组
function getSumByArr( numberArr ) {
let sumNumber = 0;
numberArr.forEach(function(it) {
sumNumber += it;
})
return sumNumber;
}
console.log(getSumByArr([1, 2, 3])); // 输出6
// 2. 使用arguments
function getSumByArg() {
let sumNumber = 0;
let args = [].slice.call(arguments);
args.forEach(function(it) {
sumNumber += it;
})
return sumNumber;
}
console.log(getSumByArg([1, 2, 3])); // 输出6
那么上面的两种方法有什么缺陷呢?
ES6的剩余参数语法专门用于收集函数末尾的参数, 并将其放进一个形参数组中
语法如下:
function foo(...形参数组名) {}
我们来看看这哥们做的事情吧, 同样实现一个求和函数
function getSum(...numberArr) {
let sumNumber = 0;
numberArr.forEach(function(it) {
sumNumber += it;
})
return sumNumber;
}
console.log(getSum(1, 2, 3)); // 输出6
console.log(getSum(2, 3, 4, 5)); // 输出14
这样我们不仅在阅读上能够比较清晰的明白getSum函数会接收一系列参数, 也不用担心arguments带来的部分问题啦
剩余参数需要注意的事情
function demo( x, y, ...arg) {
console.log(x, y, arg);
}
demo( 1, 2, 3, 4); // 这样调用系统知道x = 1, y = 2, 然后arg将3 和 4收进一个数组
function demo( x, ...arg, y ) {
console.log(x, y, arg);
}
demo( 1, 2, 3, 4, 5 ); // 这你告诉人家...arg要怎么个收法啊, 他知道arg要收哪些变量? 所以我们不能将剩余参数放在中间, 必须在最后一个参数
function demo(...arg) {
// 他知道把所有的参数都放进arg里
}
demo( 1, 2, 3 );
function foo(...arg1, ...arg2) {
// 你说这咋分啊, arg1拿几个, arg2拿几个?
}
foo(2, 3, 4);
// 所以一个函数仅可以出现一个剩余参数
一般来说, 我们定义构造函数就是要使用new实例对象的, 但是有时候不管是由于我们的粗心还是刻意不符合规范操作, 都会导致我们没有使用new但是构造函数并没有提示我们, 在过去我们是这样处理这个问题的, 如下
function Person( name, age ) {
if(!this instanceof Person) {
// 如果this的原型链上没有Person的原型, 则代表他并没有使用new
throw new Error('Person must be called with new ');
}
this.name = name;
this.age = age;
}
const lina = new Person( 'lina', 17 );
const dona = Person('dona', 18); // 这一行会直接报错, 因为如果不用new关键字, this一定指向window
const loki = Person.call(lina, 20); // 这样也不会报错, 因为使用call修改了this指向
上面最后的loki没有通过new但是也没有报错, 这很显然是ES5在判定是否有正确使用构造函数的缺陷
ES6提供new.target属性来告知开发者调用构造函数的人是否是通过new调用的, 如果是则new.target会返回该构造函数, 如果不是则会返回undefined
function Person( name, age ) {
if(new.target === undefined) {
throw new Error('Person must be called with new ');
}
this.name = name;
this.age = age;
}
const lina = new Person( 'lina', 17 );
const dona = Person('dona', 18); // 这一行会直接报错, 因为如果不用new关键字, this一定指向window
const loki = Person.call(lina, 20); // 他也跑不掉, 会报错
const tick = {
number: 10,
startTick: function() {
const _this = this; // 我们在setInterval的操作函数里是拿不到这个this的, 所以我们必须要保存一下
let timer = setInterval(function() {
_this.number --;
console.log(this.number);
if(_this.number === 0) {
clearInterval(timer);
}
}, 1000)
}
}
tick.startTick();
我们通常会像上面一样用一个新的变量来保存this, 或者我们会像下面一样使用bind函数来返回一个新的函数
const tick = {
number: 10,
startTick: function() {
let timer = setInterval(function() {
this.number --;
console.log(this.number);
if(this.number === 0) {
clearInterval(timer);
}
// 在这里使用bind绑定一下this
}.bind(this), 1000)
}
}
tick.startTick();
但是上面两种方案还是有一些问题的:
箭头函数是ES6推出的新的函数表达式书写方式, 理论上来说, 任何可以使用函数表达式和匿名函数的场景都可以写成箭头函数
语法如下:
(参数1, 参数2) => {
函数体
}
我们将上面的代码替换成函数表达式先瞅一瞅
const tick = {
number: 10,
startTick: function() {
let timer = setInterval(() => {
this.number --;
console.log(this.number);
if(this.number === 0) {
clearInterval(timer);
}
}, 1000)
}
}
tick.startTick();
我们会惊奇的发现, 我们一没有在外面声明this的变量, 也没有使用bind, 但是使用箭头函数竟然可以直接让代码不报错。
别急, 这只是让你混个脸熟, 接下来我们好好看看这个箭头函数的用法和特点
箭头函数中的this指向有两种说法, 这两种说法都对, 但是本质上是第二种说法, 第一种说法只是第二种说法的一种现象
箭头函数的参数如果只有一个, 则可以省略掉小括号, 直接书写参数( 没有参数或者多个参数都不满足要求 )
const demo = (config) => {
console.log(config);
}
// 上面的写法可以直接写成如下这样, 一个参数的情况下直接省略参数的小括号
const demo = config => {
console.log(config);
}
const demo = config => {
return config + 1;
}
// 上面的写法可以直接写成如下这样, 函数体只有一条返回语句的话直接可以省略大括号和return关键字
const demo = config => config + 1;
}
// 如果返回值是一个对象的话, 我们需要用小括号包裹一下对象, 来告诉系统我们这是返回值对象不是函数体括号
const demo = config => ({ a: config + 1 })
和this一样, 箭头函数中没有自己的arguments, new.target, 如果强行访问, 拿到的是箭头函数定义环境的arguments, new.target指向
箭头函数没有原型prototype
由于第五点, 所以箭头函数不能作为构造函数使用
适合箭头函数存在的场景: