参数默认值
function sum(a, b, c) {
// b = b || 1;
// c = c || 1;
b = b === undefined && 1;
c = c === undefined && 1;
return a + b + c;
}
console.log(sum(10, 11, 12)); // 33
使用
在书写形参时,直接给形参赋值,赋的值即为默认值。
这样一来,当调用函数时,如果没有给对应的参数赋值(给它的值是undefined),则会自动使用默认值。
function sum(a, b = 1, c = 1) {
return a + b + c;
}
console.log(sum(10)); // 12
<div id="container"></div>
<script>
const parent = document.getElementById("container");
function craeteElement(name = "div", container = parent, content = "") {
const ele = document.createElement(name);
if(content) {
ele.innerHTML = content;
}
container.appendChild(ele);
}
// craeteElement("div", parent, "123");
craeteElement(undefined, undefined, "123");
</script>
对 arguments 的影响
function test(a, b) {
console.log("a:", a, "b:", b); // a: 1 b: 2
console.log(arguments); // [1, 2]
a = 3;
console.log("a:", a, "b:", b); // a: 3 b: 2
console.log(arguments); // [3, 2]
}
test(1, 2);
严格模式下:
"use strict"
function test(a, b) {
console.log("a:", a, "b:", b); // a: 1 b: 2
console.log(arguments); // [1, 2]
a = 3;
console.log("a:", a, "b:", b); // a: 3 b: 2
console.log(arguments); // [1, 2]
}
test(1, 2);
只要给函数加上参数默认值,该函数会自动变成严格模式下的规则:argument 和 形参脱离。
function test(a = 1, b = 3) {
console.log("a:", a, "b:", b); // a: 2 b: 3
console.log(arguments); // [2]
a = 3;
console.log("a:", a, "b:", b); // a: 3 b: 3
console.log(arguments); // [2]
}
test(2);
留意暂时性死区
形参和ES6中的let和const声明一样,具有作用域,并且根据参数的声明顺序,存在暂时性死区。
function test(a = b, b) {
console.log(a, b); // Cannot access 'b' before initialization
}
test(undefined, 1);
剩余参数
function sum(arg) {
let sum = 0;
for(let i = 0; i < arg.length; i ++) {
sum += arg[i];
}
return sum;
}
console.log(sum([1])); // 1
console.log(sum([1, 2, 3, 4])); // 10
arguments的缺陷:
- 如果和形参配合使用,容易导致混乱。
- 从语义上,使用arguments获取参数,由于形参缺失,无法从语义上理解函数的真实意图。
function sum() {
let sum = 0;
for(let i = 0; i < arguments.length; i ++) {
sum += arguments[i];
}
return sum;
}
console.log(sum(1)); // 1
console.log(sum(1, 2, 3, 4)); // 10
ES6的剩余参数专门用于收集末尾的所有参数,将其放置到形参数组中。
语法:
function (...形参名) {
}
function sum(...args) {
// args 收集了所有的参数,形成一个数组
// console.log(args);
let sum = 0;
for(let i = 0; i < args.length; i ++) {
sum += args[i];
}
return sum;
}
console.log(sum(1)); // 1
console.log(sum(1, 2, 3, 4)); // 10
细节:
1. 一个函数,只能出现一个剩余参数。
2. 一个函数,如果有剩余参数,剩余参数必须是最后一个参数。
展开运算符
使用方式: ...要展开的东西
。
对 数组展开 ES6
// 对所有数组求和
function sum(...args) {
let sum = 0;
for(let i = 0; i < args.length; i ++) {
sum += args[i];
}
return sum;
}
// 获取一个指定长度的随机数组成的数组
function getRandomNumbers(length) {
const arr = [];
for(let i = 0; i < length; i ++) {
arr.push(Math.random());
}
return arr;
}
const numbers = getRandomNumbers(10);
console.log(numbers);
// 将数组的每一项展开,依次作为参数传递,而不是把整个数组作为一个参数传递
console.log(sum(...numbers)); // 相当于传递了10个参数
深度克隆:
const arr1 = [1, 2, 3];
const arr2 = [...arr1];
console.log(arr2, arr2 === arr1); // [1, 2, 3] false
对 对象展开 ES7
const obj1 = {
name: "刘",
age: 20
}
const obj2 = {
...obj1
}
console.log(obj2, obj2 === obj1); // {name: "刘", age: 20} false
明确函数的双重用途
使用构造函数,或使用普通函数。
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.fullName = `${firstName} ${lastName}`;
}
const p1 = new Person("p1", "p");
console.log(p1); // {firstName: "p1", lastName: "p", fullName: "p1 p"}
const p2 = Person("p2", "p");
console.log(p2); // undefined
function Person(firstName, lastName) {
// 判断是否是使用new的方式来调用函数
// 过去的判断方式
if(!(this instanceof Person)) {
throw new Error("该函数没有使用new来调用");
}
this.firstName = firstName;
this.lastName = lastName;
this.fullName = `${firstName} ${lastName}`;
}
const p1 = new Person("p1", "p");
console.log(p1); // {firstName: "p1", lastName: "p", fullName: "p1 p"}
// 可避开判断
const p3 = Person.call(p1, "p3", "p");
console.log(p3); // undefined
const p2 = Person("p2", "p");
console.log(p2); // Uncaught Error: 该函数没有使用new来调用
ES6提供了一个特殊的API,可以使用该API在函数内部,判断该函数是否使用了new来调用。
new.target
// 该表达式,得到的是:如果没有使用new来调用函数,则返回undefined;如果使用new调用函数,则得到的是new关键字后面的函数本身
function Person(firstName, lastName) {
// 判断是否是使用new的方式来调用函数
// ES6 判断方法
// console.log(new.target);
if(new.target === undefined) {
throw new Error("该函数没有使用new来调用");
}
this.firstName = firstName;
this.lastName = lastName;
this.fullName = `${firstName} ${lastName}`;
}
const p1 = new Person("p1", "p");
console.log(p1); // Person {firstName: "p1", lastName: "p", fullName: "p1 p"}
const p3 = Person.call(p1, "p3", "p");
console.log(p3); // Uncaught Error: 该函数没有使用new来调用
const p2 = Person("p2", "p");
console.log(p2);
箭头函数
回顾:this指向
- 1.通过对象调用函数,this指向对象
- 2.直接调用函数,this指向全局对象
- 3.如果通过new调用函数,this指向新创建的对象
- 4.如果通过apply、call、bind调用函数,this指向指定的数据
- 5.如果是DOM事件函数,this指向事件源
const obj = {
count: 0,
start: function() {
console.log(this); // this --> obj
setInterval(function() {
console.log(this); // this --> window
this.count ++;
console.log(this.count); // NaN
},1000)
}
}
obj.start();
const obj = {
count: 0,
start: function() {
// console.log(this); // this --> obj
var self = this;
setInterval(function() {
console.log(self); // this --> obj
self.count ++;
console.log(self.count);
},1000)
}
}
obj.start();
使用语法
箭头函数是一个函数表达式,理论上,任何使用函数表达式的场景都可以使用箭头函数。
完整写法:
(参数1, 参数2) => {
// 函数体
}
const obj = {
count: 0,
start: function() {
console.log(this); // this --> obj
setInterval(() => {
console.log(this); // this --> obj
this.count ++;
console.log(this.count);
},1000)
}
}
obj.start();
如果参数只有一个,可以省略小括号:
参数 => {
// 函数体
}
如果箭头函数只有一条返回语句,可以省略大括号,和return关键字:
参数 => 返回值
注意细节
- 箭头函数的函数体中的this,取决于箭头函数定义的位置的this指向,而与如何调用无关。
1. 箭头函数中,不存在 this、arguments、new.target ,如果使用了,则使用的是函数外层的对应的 this、arguments、new.target 。
2. 箭头函数没有原型。
3. 箭头函数不能作为构造函数使用。
应用场景
1. 临时性使用的函数,并不会刻意调用它。比如:事件处理函数、异步处理函数、其他临时性的函数。
2. 为了绑定外层this的函数。
3. 在不影响其他代码的情况下,保持代码的简洁。最常见的是数组方法中的回调函数。
const num = [3, 5, 5, 3, 2];
const result = num.filter(num => num % 2 === 0).map(num => num * 2);
console.log(result); // [4]