学习目标
1.1 ECMAScript6 介绍
ECMAScript6 (简称ES6)是与2015年6月正式发布的JavaScript语言的标准,正式名为ECMAScript 2015(ES2015)。它的目标是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
1.1.1 ECMAScript 的概念
ECMAScript是一种由欧洲计算机制造商协会(Ecma International,简称Ecma)标准化的脚本语言规范。以下是对ECMAScript概念的详细解释:
一、定义与背景
- 定义:ECMAScript是定义脚本编程语言的标准,它为脚本语言定义了语法、类型、对象模型等核心内容,确保不同环境下脚本语言的兼容性和一致性。
- 起源:ECMAScript最初由Netscape公司发起,作为Netscape Navigator浏览器的脚本语言(最初名为LiveScript,后改名为JavaScript)。后来,Ecma国际组织将其标准化为ECMA-262标准。
二、与JavaScript的关系
- ECMAScript是JavaScript的核心语言规范,而JavaScript是ECMAScript标准的具体实现之一。
- ECMAScript定义了基础语法和语义规则,而JavaScript在此基础上添加了Web相关的API和功能,例如DOM操作、事件处理、浏览器交互等。
三、版本演进
- 自1997年发布第一版ECMAScript以来,该标准经历了多次更新和演进,包括ECMAScript 2、3、5、6(也称为ES2015)、7、8、9、10、11、12等版本。
- 每个新版本都引入了新的语言特性和功能,以满足不断增长的Web开发需求。例如,ECMAScript 6引入了箭头函数、类、模块化等特性,极大地丰富了ECMAScript的编程能力。
四、主要特性
- 动态类型:ECMAScript是一种动态类型语言,变量的类型在运行时确定,而不是在编译时确定。这使得编程更灵活,但也需要开发人员注意类型转换和类型错误。
- 原型继承:ECMAScript使用原型继承作为对象之间的主要机制。每个对象都有一个原型对象,可以从中继承属性和方法。这种原型链的设计使得对象之间可以共享属性和方法,实现代码的重用。
- 函数式编程:ECMAScript支持函数作为一等公民,可以将函数作为参数传递给其他函数,也可以将函数作为返回值。这种函数式编程的特性使得编写高阶函数和实现函数组合变得更加容易。
- 闭包:ECMAScript支持闭包,即函数可以访问其定义时的作用域外部的变量。闭包在事件处理、模块化等方面发挥着重要作用,可以帮助开发人员管理状态和数据。
- 异步编程:ECMAScript通过回调函数、Promise、async/await等机制支持异步编程。这使得在Web应用程序中处理异步操作(如网络请求、定时器等)变得更加简单和高效。
- 模块化:ECMAScript支持模块化编程,可以将代码分割为多个模块,每个模块负责特定功能。这种模块化的设计有助于代码的组织和维护,同时也提高了代码的可重用性和可扩展性。
五、应用场景
- Web前端开发:ECMAScript是Web前端开发的核心技术之一,用于实现各种动态效果和交互逻辑。
- 服务器端开发:随着Node.js的兴起,ECMAScript也被广泛用于服务器端开发,包括构建RESTful API、后端服务等。
- 移动应用开发:使用React Native等框架,开发者可以使用ECMAScript来构建跨平台的移动应用。
1.1.2 各浏览器对ES6的支持
ES6(ECMAScript 2015)引入了许多新的语法和特性,以增强JavaScript编程语言的功能。目前,各大浏览器对ES6的支持情况如下:
一、桌面端浏览器
- Chrome:从Chrome 51版起,便可以支持97%的ES6新特性。Chrome浏览器对ES6新特性的支持较为友好,且随着版本的更新,支持度也在不断提升。
- Firefox:Firefox从53版起便可以支持97%的ES6新特性。Firefox浏览器同样对ES6新特性有较好的支持,是开发者常用的浏览器之一。
- Safari:从Safari 10版起,便可以支持99%的ES6新特性。Safari浏览器在iOS和macOS平台上都有广泛的应用,对ES6的支持也相对较高。
- Edge:Edge 15可以支持96%的ES6新特性,而Edge 14可以支持93%的ES6新特性。需要注意的是,早期的IE浏览器(如IE7~11)基本不支持ES6。但随着Edge浏览器的推出和不断更新,其对ES6的支持度也在逐渐提升。
二、移动端浏览器
对于移动端浏览器,2016年以后发布的Safari on iOS和Chrome等全部都支持ES6。这些浏览器在移动设备上有着广泛的应用,对ES6的支持使得开发者可以在移动应用中使用更多的ES6特性。
三、兼容性解决方案
尽管现代浏览器对ES6的支持度已经很高,但仍有一些老旧浏览器或特定环境下的浏览器可能不支持ES6。为了兼容这些浏览器,开发者通常会使用Babel等转码工具将ES6代码转码为ES5代码。这样可以确保代码在不同浏览器环境下都能正常运行。
1.1.3 ES6转码ES5支持老版浏览器
为了兼容老版浏览器,特别是那些不支持ES6的浏览器(如Internet Explorer 11及以下版本),开发者通常会将ES6代码转码为ES5代码。以下是对这一过程的详细解释:
一、转码工具
- Babel:Babel是一个广泛使用的JavaScript编译器,可以将ES6+代码转换为向后兼容的JavaScript版本(如ES5)。通过使用Babel,开发者可以确保他们的代码在老旧浏览器上也能正常运行。
二、转码过程
- 安装Babel:首先,开发者需要在他们的项目中安装Babel。这通常通过npm(Node Package Manager)来完成。
- 配置Babel:安装完成后,开发者需要配置Babel以指定要转换的代码和转换规则。这通常通过在项目中创建一个.babelrc文件或在package.json中添加一个babel字段来完成。
- 编写ES6代码:在配置好Babel后,开发者可以开始编写ES6代码。这些代码将包含ES6的新特性,如箭头函数、类、模块化等。
- 运行Babel进行转码:当开发者准备好将ES6代码转换为ES5代码时,他们可以运行Babel。Babel将读取源代码,应用转换规则,并生成兼容ES5的JavaScript代码。
三、Polyfill的使用
除了Babel之外,开发者可能还需要使用Polyfill来提供老旧浏览器缺少的ES6功能。Polyfill是一种浏览器端的技术,用于在旧版浏览器中提供那些原本不支持的现代功能。
- 安装Polyfill:开发者可以通过npm安装Polyfill库,如babel-polyfill或core-js。
- 引入Polyfill:在项目的入口文件中(如main.js),开发者需要引入Polyfill。这通常通过import语句来完成。
四、兼容性测试
在将ES6代码转码为ES5并应用Polyfill后,开发者需要进行兼容性测试以确保他们的代码在老旧浏览器上也能正常运行。这可以通过在目标浏览器上手动测试或使用自动化测试工具来完成。
五、注意事项
- 性能考虑:虽然Polyfill可以提供老旧浏览器缺少的功能,但它们可能会增加代码的体积并影响性能。因此,开发者需要权衡功能需求和性能要求。
- 持续更新:随着浏览器版本的更新和新特性的引入,开发者需要定期更新他们的Babel配置和Polyfill库以确保兼容性。
- 代码质量:在转码过程中,开发者需要确保生成的ES5代码保持与原始ES6代码相同的逻辑和功能。这可能需要额外的测试和调试工作。
1.2 let与const命令
ES5中使用的是var来声明变量,存在不少的问题,也不支持常量定义。在ES6中新增了let命令和const命令来弥补var的不足。
1.2.1let命令
let 命令是ECMAScript 6(ES6)中引入的用于声明块作用域的本地变量的关键字。与 var 关键字不同,let 声明的变量只在其所在的块或子块中可用,而不会污染全局作用域或函数作用域。以下是一些关于 let 命令的示例代码:
{
let a = 10;
console.log(a); // 输出: 10
}
console.log(a); // 报错: a is not defined,因为a的作用域仅限于其所在的块
块作用域
let 声明的变量具有块作用域,这意味着它们只在其所在的块(由 {} 包围的代码块)中有效。
if (true) {
let x = 5;
console.log(x); // 输出: 5
}
// console.log(x); // 报错: x is not defined,因为x的作用域仅限于if块内
不允许重复声明
在同一作用域内,let 不允许重复声明同一个变量。
let y = 10;
// let y = 20; // 报错: Identifier 'y' has already been declared
暂时性死区(Temporal Dead Zone, TDZ)
在 let 变量被声明之前的区域被称为暂时性死区。在这个区域内,访问该变量会导致引用错误(ReferenceError)。
console.log(z); // 报错: Cannot access 'z' before initialization,因为z处于暂时性死区
let z = 5;
用于循环中的计数器
在 for 循环中使用 let 声明的计数器变量每次迭代都会创建一个新的块作用域,从而避免了使用 var 时可能导致的变量污染问题。
for (let i = 0; i < 3; i++) {
console.log(i); // 输出: 0, 1, 2
}
// console.log(i); // 报错: i is not defined,因为i的作用域仅限于for循环块内
函数作用域与全局作用域
尽管 let 引入了块作用域,但在函数内部声明的 let 变量仍然遵循函数作用域的原则(在函数体内部有效),只是不再像 var 那样会“泄漏”到包含它的函数外部的全局作用域中。
function example() {
let foo = 'Hello, world!';
console.log(foo); // 输出: Hello, world!
}
example();
// console.log(foo); // 报错: foo is not defined,因为foo的作用域仅限于example函数内部
总之,let 命令提供了一种更灵活且更安全的变量声明方式,通过引入块作用域来避免变量污染和意外覆盖的问题。在编写现代JavaScript代码时,建议使用 let 和 const(用于声明常量)来替代 var。
1.2.2 const命令
const 是 ECMAScript 6(ES6)中引入的另一个用于声明变量的关键字,与 let 类似,const 声明的变量也具有块作用域,并且不会在全局作用域或函数作用域中创建可变的绑定。然而,与 let 不同的是,const 声明的变量必须被初始化,并且一旦赋值后,其值就不能被重新改变(尽管如果变量是一个对象或数组,其内容仍然可以被修改)。
以下是一些关于 const 命令的示例代码:
const PI = 3.14159;
console.log(PI); // 输出: 3.14159
// PI = 3.14; // 报错: Assignment to constant variable.
块作用域
与 let 一样,const 声明的变量也具有块作用域。
if (true) {
const message = "Hello, block scope!";
console.log(message); // 输出: Hello, block scope!
}
// console.log(message); // 报错: message is not defined
必须初始化
const 声明的变量在声明时必须被初始化。
// const name; // 报错: Missing initializer in const declaration
const age = 25; // 正确
不可重新赋值
一旦 const 变量被赋值,其值就不能被重新改变。
const country = "USA";
// country = "Canada"; // 报错: Assignment to constant variable.
对象和数组的内容可以修改
尽管 const 变量的值不能重新赋值,但如果该变量是一个对象或数组,其内容仍然可以被修改。
const person = { name: "John", age: 30 };
person.age = 31; // 正确,修改了对象的属性
console.log(person.age); // 输出: 31
const colors = ["red", "green", "blue"];
colors.push("yellow"); // 正确,修改了数组的内容
console.log(colors); // 输出: ["red", "green", "blue", "yellow"]
// person = { name: "Jane", age: 25 }; // 报错: Assignment to constant variable.
// colors = ["black", "white"]; // 报错: Assignment to constant variable.
函数声明与常量
在 ES6 中,可以在块作用域内使用 const 来声明函数,但这在严格模式下可能会有一些限制,特别是如果你尝试在一个已经声明了同名函数的外部作用域中这样做。然而,通常的做法是在顶层作用域或函数作用域内使用 const 来声明函数表达式(也称为箭头函数或匿名函数)。
const add = (a, b) => a + b;
console.log(add(2, 3)); // 输出: 5
总之,const 提供了一种声明不可重新赋值的变量的方法,这对于定义那些一旦设置就不应该改变的常量值非常有用。然而,需要注意的是,尽管 const 变量的引用不能改变,但如果该变量引用的是一个对象或数组,其内容仍然可以被修改。
1.3 变量的解构赋值
解构赋值是对赋值运算符的扩展。ES6允许按照一定模式从数组和对象中提取值,再对变量赋值,这被称为解构(Destructuring)。在代码编写上,解构赋值的写法更加简洁易懂,语言更加清晰明了。同时,变量解构赋值还有如下用途。
- 交换变量的值。
- 从函数返回多个值。
- 定义函数的参数。
- 提取JSON数据。
- 设置函数参数的默认值。
1.3.1 数组解构赋值
数组解构赋值是ES6(ECMAScript 6)中引入的一种语法,它允许你从数组中提取值,并将这些值赋给单独的变量。这种语法简洁明了,特别适用于处理函数返回多个值或从数组中提取特定元素的情况。
以下是一些数组解构赋值的示例代码:
const arr = [1, 2, 3];
// 从数组中提取值并赋给变量
const [a, b, c] = arr;
console.log(a); // 输出: 1
console.log(b); // 输出: 2
console.log(c); // 输出: 3
跳过某些值
如果你不想提取数组中的某个值,可以在解构时使用逗号来跳过它。
const arr = [1, 2, 3, 4];
// 跳过第二个值
const [x, , y, z] = arr;
console.log(x); // 输出: 1
console.log(y); // 输出: 3
console.log(z); // 输出: 4
不完全解构
如果解构的变量少于数组中的元素数量,剩余的元素将被忽略。
const arr = [1, 2, 3, 4];
// 只提取前两个值
const [first, second] = arr;
console.log(first); // 输出: 1
console.log(second); // 输出: 2
// arr中的剩余元素(3和4)将被忽略
使用默认值
如果数组中的某个位置没有值,你可以在解构时为变量提供默认值。
const arr = [1];
// 为第二个变量提供默认值
const [a, b = 10] = arr;
console.log(a); // 输出: 1
console.log(b); // 输出: 10(因为数组中第二个位置没有值,所以使用默认值)
嵌套数组解构
你还可以解构嵌套数组。
const nestedArr = [1, [2, 3], 4];
// 解构嵌套数组
const [a, [b, c], d] = nestedArr;
console.log(a); // 输出: 1
console.log(b); // 输出: 2
console.log(c); // 输出: 3
console.log(d); // 输出: 4
与函数结合使用
解构赋值经常与函数结合使用,以返回多个值。
function getPoint() {
return [1, 2];
}
// 从函数返回的数组中提取值
const [xPoint, yPoint] = getPoint();
console.log(xPoint); // 输出: 1
console.log(yPoint); // 输出: 2
注意事项
- 解构赋值时,如果数组的长度小于变量的数量,且没有提供默认值,那么未赋值的变量将是undefined。
- 如果尝试对一个非数组(如null或undefined)进行解构赋值,将会抛出一个错误。因此,在进行解构之前,最好先检查变量是否为数组。
1.3.2 对象解构赋值
对象解构赋值是ECMAScript 6(ES6)中引入的一种语法特性,它允许你从对象中提取属性,并将这些属性的值赋给单独的变量。这种语法非常有用,特别是在处理函数返回多个值或从复杂对象中提取所需属性时。
以下是一些对象解构赋值的示例代码:
const person = {
name: 'Alice',
age: 25,
job: 'Engineer'
};
// 从对象中提取属性并赋给变量
const { name, age, job } = person;
console.log(name); // 输出: Alice
console.log(age); // 输出: 25
console.log(job); // 输出: Engineer
重命名属性
在解构时,你可以为提取的属性指定一个新的变量名。
const person = {
firstName: 'Alice',
lastName: 'Smith',
age: 25
};
// 重命名属性并提取
const { firstName: name, age: yearsOld } = person;
console.log(name); // 输出: Alice
console.log(yearsOld); // 输出: 25
默认值
如果对象中的某个属性不存在,你可以在解构时为变量提供一个默认值。
const person = {
name: 'Alice',
age: 25
};
// 为不存在的属性提供默认值
const { name, job = 'Unknown' } = person;
console.log(name); // 输出: Alice
console.log(job); // 输出: Unknown(因为person对象中没有job属性)
嵌套对象解构
你还可以解构嵌套的对象。
const addressBook = {
contacts: {
Alice: {
phone: '123-456-7890',
email: '[email protected]'
},
Bob: {
phone: '987-654-3210',
email: '[email protected]'
}
}
};
// 解构嵌套对象
const { contacts: { Alice: aliceContact } } = addressBook;
console.log(aliceContact.phone); // 输出: 123-456-7890
console.log(aliceContact.email); // 输出: [email protected]
提取函数参数中的对象属性
解构赋值经常与函数结合使用,以便从对象参数中提取属性。
function greet({ name, age }) {
console.log(`Hello, my name is ${name} and I am ${age} years old.`);
}
const person = {
name: 'Alice',
age: 30
};
greet(person); // 输出: Hello, my name is Alice and I am 30 years old.
注意事项
- 解构赋值时,如果对象中没有对应的属性,且没有提供默认值,那么变量将是undefined。
- 如果尝试对一个非对象(如null或undefined)进行解构赋值,将会抛出一个错误。因此,在进行解构之前,最好先检查变量是否为对象。
- 在函数参数中使用解构赋值时,如果传入的对象缺少某些属性,这些属性对应的参数将是undefined,除非在解构时提供了默认值。
1.3.3 字符串解构赋值
在JavaScript中,解构赋值主要用于数组和对象,而字符串本身并不直接支持解构赋值,因为字符串不是由键值对或有序元素组成的集合,它们是由字符组成的序列。然而,你可以通过一些技巧将字符串转换为数组或对象,然后对其进行解构。
以下是一些将字符串转换为数组或对象,并进行解构赋值的示例代码:
将字符串转换为数组并进行解构
你可以使用split()方法将字符串转换为数组,然后进行解构赋值。
const str = "hello world";
// 将字符串转换为数组
const [firstWord, secondWord] = str.split(' ');
console.log(firstWord); // 输出: hello
console.log(secondWord); // 输出: world
使用对象模拟字符串解构(不推荐)
虽然这不是直接对字符串进行解构,但你可以创建一个对象来模拟字符串的解构过程。然而,这种方法并不直观,也不推荐在实际代码中使用。
const str = "hello";
// 创建一个对象来模拟字符串
const strObj = {
0: 'h',
1: 'e',
2: 'l',
3: 'l',
4: 'o',
length: 5
};
// 使用对象解构(但这不是真正的字符串解构)
// 注意:这里的解构是基于对象的属性名,而不是字符串的索引
// 而且,这种方法需要事先知道字符串的长度,并且非常不实用
const { 0: firstChar, 1: secondChar } = strObj;
console.log(firstChar); // 输出: h
console.log(secondChar); // 输出: e
然而,上面的代码并不是真正的字符串解构,而是基于对象的属性名进行解构。它并不实用,因为你需要事先知道字符串的确切长度,并且为每个字符创建一个属性,这在实际应用中是不可行的。
更实用的方法:使用数组解构和字符串方法
更实用的方法是继续使用split()方法将字符串转换为数组,然后进行解构。如果你需要解构字符串中的特定部分,可以结合使用其他字符串方法,如substring()、slice()或正则表达式。
const str = "hello123world";
// 使用正则表达式分割字符串,并解构赋值
const [, letters, numbers, , moreLetters] = str.match(/([a-z]+)(\d+)([a-z]*)/i);
console.log(letters); // 输出: hello
console.log(numbers); // 输出: 123
console.log(moreLetters); // 输出: world(注意:这里的moreLetters变量名可能不够准确,因为它只是匹配剩余的字母)
在这个例子中,我们使用了正则表达式来匹配和分割字符串。然而,这种方法依赖于正则表达式的准确性和字符串的格式。如果字符串的格式发生变化,正则表达式可能需要相应地调整。
总之,虽然JavaScript本身不支持对字符串进行直接解构赋值,但你可以通过将字符串转换为数组或对象(尽管后者不推荐),或者使用正则表达式和其他字符串方法来实现类似的功能。在实际应用中,最常用和推荐的方法是使用split()方法将字符串转换为数组,然后进行解构赋值。
1.3.4 函数参数解构赋值
函数参数解构赋值是JavaScript ES6中引入的一种语法糖,它允许你在函数定义时直接对传入的对象或数组参数进行解构,从而更方便地访问其属性或元素。以下是一些关于函数参数解构赋值的示例代码:
对象参数解构赋值
当函数参数是一个对象时,你可以使用解构赋值来直接提取对象的属性。
function greet({ name, age }) {
console.log(`Hello, my name is ${name} and I am ${age} years old.`);
}
const person = {
name: 'Alice',
age: 30
};
greet(person); // 输出: Hello, my name is Alice and I am 30 years old.
在这个例子中,函数greet接收一个对象参数,并使用解构赋值来提取name和age属性。
数组参数解构赋值
当函数参数是一个数组时,你可以使用解构赋值来直接访问数组的元素。
function sum([a, b, c]) {
return a + b + c;
}
const numbers = [1, 2, 3];
console.log(sum(numbers)); // 输出: 6
在这个例子中,函数sum接收一个数组参数,并使用解构赋值来提取数组的前三个元素。
默认值
你可以在解构赋值时为参数提供默认值,以防传入的对象或数组缺少某些属性或元素。
function describePerson({ name = 'Unknown', age = 0 }) {
console.log(`Name: ${name}, Age: ${age}`);
}
describePerson({}); // 输出: Name: Unknown, Age: 0
describePerson({ name: 'Bob' }); // 输出: Name: Bob, Age: 0
describePerson({ age: 40 }); // 输出: Name: Unknown, Age: 40
describePerson({ name: 'Charlie', age: 25 }); // 输出: Name: Charlie, Age: 25
在这个例子中,如果传入的对象缺少name或age属性,函数将使用默认值Unknown和0。
嵌套解构
你还可以对嵌套的对象或数组进行解构赋值。
function printCoordinates({ location: { latitude, longitude } }) {
console.log(`Latitude: ${latitude}, Longitude: ${longitude}`);
}
const place = {
location: {
latitude: 37.7749,
longitude: -122.4194
}
};
printCoordinates(place); // 输出: Latitude: 37.7749, Longitude: -122.4194
在这个例子中,函数printCoordinates接收一个对象参数,该参数有一个嵌套的对象location,然后进一步解构location对象来提取latitude和longitude属性。
函数默认参数与解构赋值结合
你还可以将函数默认参数与解构赋值结合使用。
function printDetails({ name, details: { age = 0, city = 'Unknown' } = {} } = {}) {
console.log(`Name: ${name}, Age: ${age}, City: ${city}`);
}
printDetails(); // 输出: Name: undefined, Age: 0, City: Unknown
printDetails({ name: 'David' }); // 输出: Name: David, Age: 0, City: Unknown
printDetails({ name: 'Eve', details: { age: 28 } }); // 输出: Name: Eve, Age: 28, City: Unknown
printDetails({ name: 'Frank', details: { city: 'New York' } }); // 输出: Name: Frank, Age: 0, City: New York
printDetails({ name: 'Grace', details: { age: 34, city: 'Los Angeles' } }); // 输出: Name: Grace, Age: 34, City: Los Angeles
在这个例子中,函数printDetails有一个复杂的默认参数结构,它处理了多个层级的默认值。如果传入的参数缺少某些属性或嵌套对象,函数将使用指定的默认值。
1.4 函数扩展
在ES5函数基础上,ES6增加了如参数默认值、rest参数、name属性等新特性,通过这些新特性可以更加便捷、灵活地进行函数操作。
1.4.1 函数参数默认值
在JavaScript中,ES6引入了函数参数默认值的功能,这允许你在定义函数时为参数指定默认值。如果调用函数时没有提供某个参数的值,那么该参数将自动采用其默认值。以下是一些关于函数参数默认值的示例代码:
function greet(name = 'Guest') {
console.log(`Hello, ${name}!`);
}
greet(); // 输出: Hello, Guest!
greet('Alice'); // 输出: Hello, Alice!
在这个例子中,函数greet有一个参数name,它的默认值是Guest
。如果调用greet()时没有提供name参数,那么它将使用默认值Guest
。
多个参数与默认值
function multiply(a = 1, b = 1) {
return a * b;
}
multiply(); // 输出: 1 (1 * 1)
multiply(2); // 输出: 2 (2 * 1)
multiply(2, 3); // 输出: 6 (2 * 3)
multiply(undefined, 3); // 输出: 3 (1 * 3),因为undefined不被视为有效值,所以使用默认值1
在这个例子中,函数multiply有两个参数a和b,它们的默认值都是1。如果调用时没有提供某个参数,它将使用该参数的默认值。注意,undefined和null都被视为未提供值,因此会使用默认值,但如果你显式地传递null或undefined作为参数值,它们将不会被替换为默认值(除非你在函数体内进行额外的检查和处理)。
带有默认值的复杂参数
function createUser({ name = 'Anonymous', age = 0, isAdmin = false } = {}) {
console.log(`Name: ${name}, Age: ${age}, Is Admin: ${isAdmin}`);
}
createUser(); // 输出: Name: Anonymous, Age: 0, Is Admin: false
createUser({}); // 输出: Name: Anonymous, Age: 0, Is Admin: false
createUser({ name: 'John' }); // 输出: Name: John, Age: 0, Is Admin: false
createUser({ name: 'Jane', age: 30 }); // 输出: Name: Jane, Age: 30, Is Admin: false
createUser({ name: 'Bob', isAdmin: true }); // 输出: Name: Bob, Age: 0, Is Admin: true
在这个例子中,函数createUser接收一个对象参数,该对象有三个属性:name、age和isAdmin,它们都有默认值。如果调用时没有提供整个对象或对象的某个属性,那么将使用相应的默认值。注意,这里使用了对象解构赋值和参数默认值的组合。
注意事项
- 如果函数参数列表中已经有一个参数使用了默认值,那么它后面的所有参数也必须使用默认值(或者没有默认值),因为JavaScript解析器无法确定哪些参数是省略的,哪些是被显式地设置为undefined。但是,这个规则在ES2018及更高版本中有所放宽,只要参数在函数体内被明确赋值,就可以不遵循这个规则。
- 如果函数参数是一个对象或数组,并且你想要为对象的某个属性或数组的某个元素设置默认值,你应该在函数体内进行这个操作,而不是在参数默认值中。这是因为参数默认值是在函数调用时立即求值的,而对象或数组的属性或元素可能需要在函数体内根据某些条件来设置。
1.4.2 rest参数与name属性
在JavaScript中,rest 参数(也称为剩余参数)是ES6中引入的一种语法,它允许我们将一个不定数量的参数表示为一个数组。这通常用于函数定义中,当你不确定会传递给函数多少个参数时。而 name 属性通常指的是函数或变量的名称属性,但在上下文中与 rest 参数结合时,可能是在讨论函数参数的命名或函数的 name 属性。
以下是一些关于 rest 参数和函数 name 属性的示例代码:
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 输出: 10
console.log(sum(5, 10)); // 输出: 15
在这个例子中,…numbers 是一个 rest 参数,它将所有传递给 sum 函数的参数收集到一个名为 numbers 的数组中。然后,我们使用 reduce 方法来计算数组中所有数字的和。
函数 name 属性示例
每个函数对象都有一个 name 属性,它包含了函数的名称。这个属性对于调试非常有用,因为它可以帮助你识别代码中的函数。
function add(a, b) {
return a + b;
}
console.log(add.name); // 输出: "add"
在这个例子中,add 函数有一个 name 属性,其值为 “add”。
Rest 参数与函数 name 属性结合
当使用 rest 参数时,函数的 name 属性仍然会反映函数的名称,但需要注意的是,如果你使用箭头函数来定义包含 rest 参数的函数,那么 name 属性可能会表现得不同。
function exampleFunction(...args) {
console.log(args);
}
const arrowFunction = (...args) => {
console.log(args);
};
console.log(exampleFunction.name); // 输出: "exampleFunction"
console.log(arrowFunction.name); // 输出: (可能因JavaScript引擎而异,但通常是类似 "arrowFunction" 或 "<anonymous>" 的值,尽管这里我们给箭头函数赋值给了名为 arrowFunction 的变量)
在这个例子中,exampleFunction 是一个使用 rest 参数的普通函数,其 name 属性为 “exampleFunction”。而 arrowFunction 是一个使用 rest 参数的箭头函数,其 name 属性可能因JavaScript引擎而异,但通常是一个基于变量名的值(如果可用)或 “”。
注意:箭头函数没有自己的 this、arguments、super 或 new.target 绑定,这些值会从外围作用域(通常是函数或全局对象)继承。此外,箭头函数也不支持 new 操作符或 syntax(即它们不能用作构造函数)。因此,在需要这些特性的情况下,你应该使用普通函数而不是箭头函数。
1.4.3 箭头函数
在JavaScript中,箭头函数(Arrow Function)是ES6(ECMAScript 2015)引入的一种更简洁的函数写法。它提供了一种更短、更直观的方式来写函数表达式。箭头函数不绑定自己的this、arguments、super或new.target。这些值会从外围作用域(通常是函数或全局对象)继承。
以下是箭头函数的一些基本示例代码:
const add = (a, b) => {
return a + b;
};
console.log(add(2, 3)); // 输出: 5
在这个例子中,add是一个箭头函数,它接受两个参数a和b,并返回它们的和。
省略花括号和return(当函数体只有一条语句时)
如果箭头函数的函数体只有一条语句,并且这条语句是return语句,那么你可以省略花括号{}和return关键字。
const multiply = (a, b) => a * b;
console.log(multiply(4, 5)); // 输出: 20
省略参数括号(当箭头函数只有一个参数时)
如果箭头函数只有一个参数,并且这个参数在函数体内被直接使用(没有解构或默认值),那么你也可以省略参数括号()。但是,如果函数体是一个花括号包裹的代码块,或者你需要显式地返回对象字面量,那么即使只有一个参数,也不能省略参数括号。
const square = x => x * x;
console.log(square(6)); // 输出: 36
箭头函数与this
箭头函数不绑定自己的this值。相反,它继承外围作用域中的this值。
function Person() {
this.age = 0;
setInterval(() => {
this.age++; // `this`在这里引用的是Person实例
console.log(this.age);
}, 1000);
}
const p = new Person(); // 每秒输出递增的年龄
在这个例子中,setInterval内部的箭头函数继承了外围作用域(即Person构造函数的作用域)中的this值,因此this.age能够正确地引用Person实例的age属性。
标签:ECMAScript6,console,log,name,基础,解构,const,第一章,函数 From: https://blog.csdn.net/2301_78884095/article/details/143555450注意事项
- 箭头函数不能用作构造函数(即不能使用new操作符来创建箭头函数的实例)。
- 箭头函数没有自己的arguments对象。如果需要访问函数的参数列表,可以使用剩余参数(…args)语法。
- 箭头函数不能在其内部使用yield关键字(即它们不能用作生成器函数)。
- 由于箭头函数不绑定自己的this、arguments等,因此在某些情况下可能会导致代码的可读性和可维护性降低。因此,在使用箭头函数时需要谨慎考虑上下文和预期的行为。