JavaScript基础知识
1. 新特性
1.1 ES6
ECMAScript 2015(简称ES6)为JavaScript语言带来了许多新的特性和改进,使得JavaScript编程更加方便、高效和优雅。以下是ES6中的一些主要新特性:
- 块级作用域
- ES6引入了
let
和const
关键字,用于在块级作用域中声明变量。与var
不同,let
和const
声明的变量只在当前代码块内有效,避免了变量提升和全局命名冲突的问题。
- ES6引入了
- 箭头函数
- 箭头函数是ES6中一种新的函数声明方式,使用箭头(
=>
)取代传统的function
关键字。箭头函数具有更简洁的语法,并且自动绑定了上下文(即this
的值),解决了回调函数中this
指向问题。
- 箭头函数是ES6中一种新的函数声明方式,使用箭头(
- 默认参数
- ES6允许在函数定义时为参数提供默认值。如果调用函数时未传递某个参数,将使用默认值。这可以简化函数的使用,并且可以传递部分参数而不是全部参数。
- 解构赋值(Destructuring Assignment)
- 解构赋值允许从数组或对象中提取值,并赋值给变量。在ES6中,可以使用解构赋值语法快速获取数组或对象中的元素,从而简化了代码编写和数据交换。
- 扩展运算符(Spread Operator)
- 扩展运算符(
...
)可以将数组或对象展开,提取出其中的元素。在函数调用或数组和对象字面量中,使用扩展运算符可以将数组或对象展开成独立的元素,或将多个元素合并成数组或对象。 - 使用场景:数组和对象的复制、克隆及合并;在函数调用时,将数组或对象中的元素作为函数的多个参数。
- 扩展运算符(
- 模板字符串(Template Literals)
- 模板字符串是一种更方便的字符串拼接方式,使用反引号(``)定义字符串,并可以在其中插入变量和表达式,提高了代码的可读性和可维护性。
- 类和模块
- ES6引入了类的语法糖,使得面向对象编程更加简洁和易用。类可以通过
extends
关键字实现继承,使用super
关键字调用父类的方法。此外,ES6还引入了模块化的概念,通过import
和export
关键字可以方便地导入和导出模块。
- ES6引入了类的语法糖,使得面向对象编程更加简洁和易用。类可以通过
- Symbol类型
- ES6新增了一种原始数据类型
Symbol
,每个Symbol
类型的值都是独一无二的。这可以用于作为对象的唯一标识符,以避免属性名的冲突。 - Symbol 值只能显式转换为字符串,隐式转换会抛出错误。
- ES6新增了一种原始数据类型
- Map和Set
- ES6引入了
Map
和Set
两种新的数据结构。Map
对象保存键值对,并且任何值(对象或原始值)都可以作为一个键或一个值。Set
对象类似于数组,但成员的值都是唯一的,没有重复的值。
- ES6引入了
- Promise对象
- Promise是异步编程的一种解决方案,它代表了一个最终可能可用(也可能不可用)的值或异步操作的最终完成(或失败)及其结果值。通过使用Promise,可以更好地组织异步代码,避免回调地狱(callback hell)的问题。
- 其他新特性
Proxy
:允许在对象和函数调用等操作前后添加自定义的行为。Reflect
:提供了一组可以操作对象的内置方法,可以替代一些对象方法的实现。for...of
循环:用于遍历可迭代对象(如数组、Map和Set)中的元素。Promise.allSettled
:用于处理多个Promise的状态并返回一个包含每个Promise状态的数组(注意:这个特性可能不是所有ES6实现都包含的,它可能是在后续的ECMAScript规范中引入的)。
这些新特性使得ES6在语法和功能上都有了很大的提升,使得JavaScript成为更加强大和灵活的编程语言。
1.2 ES7
ECMAScript 2016 (简称ES7),是JavaScript的第七个版本,它引入了一些重要的新特性和改进。以下是ES7的主要新特性:
Array.prototype.includes() 方法
功能:用于判断一个数组是否包含一个指定的值,并返回一个布尔值。如果包含指定的值则返回
true
,否则返回false
。优点:这个方法可以代替
indexOf()
方法,使代码更加简洁和易于阅读。indexOf()
返回的是元素的索引,如果不存在则返回-1
,而includes()
直接返回布尔值。javascriptconst arr = [1, 2, 3, 4, 5]; console.log(arr.includes(3)); // true console.log(arr.includes(6)); // false
指数运算符(**)
功能:用于计算一个数的幂,可以代替
Math.pow()
方法。语法:
x ** y
,其中x
表示底数,y
表示指数。javascriptconsole.log(2 ** 3); // 8 console.log(Math.pow(2, 3)); // 8
对象解构的剩余和展开属性
功能:允许在对象解构中使用剩余和展开属性,使代码更加简洁和易于维护。
javascriptconst { a, b, ...rest } = { a: 1, b: 2, c: 3, d: 4 }; console.log(a); // 1 console.log(b); // 2 console.log(rest); // { c: 3, d: 4 }
异步函数
功能:允许使用 async/await 关键字来编写异步代码,使异步编程更加容易和直观。
特点:
async
函数返回一个 Promise 对象,await
表达式会暂停async
函数的执行,等待 Promise 的结果出来后再继续执行。javascriptasync function fetchData() { try { const response = await fetch('https://jsonplaceholder.typicode.com/todos/1'); const data = await response.json(); console.log(data); } catch (error) { console.log(error); } } fetchData();
共享内存和原子操作
功能:允许在多个线程之间共享数据,并使用原子操作来确保数据的一致性和可靠性。
应用场景:使 JavaScript 可以更好地处理并发和多线程编程。
javascriptconst buffer = new SharedArrayBuffer(8); const view = new Int32Array(buffer); function increment() { Atomics.add(view, 0, 1); } increment(); console.log(view[0]); // 1
1.3 ES8
ECMAScript 2017(简称ES8),为JavaScript编程语言引入了一系列新特性和改进。以下是ES8的主要新特性:
- 异步迭代
- async/await:ES8进一步增强了异步编程的支持,允许在异步函数中使用
for await...of
循环来迭代异步可迭代对象(如异步生成器)。这使得处理异步数据流变得更加简单和直观。
- async/await:ES8进一步增强了异步编程的支持,允许在异步函数中使用
- 对象属性值的简写方法
- 对象字面量中的属性定义:在对象字面量中,如果属性名和属性值同名,可以省略属性值部分,只写属性名,并冒号后面跟一个函数体,这将自动把函数名作为属性名,函数体作为属性值。
- 字符串填充(padStart和padEnd)
- padStart()和padEnd():这两个方法用于在当前字符串的开头或结尾填充指定的字符串,直到达到指定的长度。如果原字符串的长度已经等于或超过了指定的长度,则返回原字符串。
- Object.entries() 和 Object.values()
- Object.entries():返回一个给定对象自身可枚举属性的键值对数组,其排列与通过手动遍历该对象属性所得到的顺序一致(区别在于该方法的返回是一个数组)。
- Object.values():返回一个给定对象自身可枚举属性值的数组,其排列与通过手动遍历该对象属性所得到的顺序一致(区别在于该方法的返回是一个数组,而不是一个对象)。
- 模板字面量中的标签模板(Tagged Templates)
- 标签模板:允许你使用函数来处理模板字面量。标签模板函数的第一个参数是一个包含模板字符串中所有静态文本部分的数组,后续参数则是模板字符串中每个插值表达式对应的值。
- 函数参数列表结尾允许逗号
- 函数参数和数组/对象字面量中的尾随逗号:在ES8中,函数参数列表、数组字面量和对象字面量的最后一个元素后面允许有一个尾随逗号。这增加了代码的灵活性和可读性,特别是在编辑大型代码库时,可以更方便地添加或删除元素。
- 共享内存和Atomics对象
- SharedArrayBuffer:允许在多个
Worker
线程之间共享一个ArrayBuffer。 - Atomics对象:提供了一组静态方法,用于在多线程环境下以原子方式操作共享内存中的数据。
- SharedArrayBuffer:允许在多个
2. DOM
3. BOM
4. var、let、const 的区别
在 JavaScript 中,var
、let
和 const
是用于声明变量的关键字,但它们之间存在一些重要的区别。以下是它们之间的主要差异:
- 作用域(Scope):
var
:具有函数作用域(在ES6之前)。这意味着在函数内部声明的var
变量在函数外部是不可见的,但在同一函数内的任何地方都是可见的。如果在函数外部声明,它将成为全局变量。let
和const
:具有块级作用域(block scope)。这意味着它们只在最近的封闭花括号{}
(例如函数体、循环体或任何代码块)内有效。
- 变量提升(Variable Hoisting):
var
:存在变量提升(hoisting)现象。这意味着即使你在函数或代码块的后面部分声明了一个var
变量,它也会被视为在函数或代码块的顶部就已经声明了(只是没有初始化)。let
和const
:不存在变量提升。这意味着在声明之前,你不能引用这些变量(否则会导致引用错误)。
- 重新声明:
var
:允许在同一作用域内多次声明同一个变量(但这样做通常是不好的编程实践)。let
:允许在同一作用域内多次声明同一个变量,但会产生语法错误(在严格模式下)。const
:不允许重新声明,也不允许重新赋值。
- 赋值:
var
和let
:允许重新赋值。const
:声明时必须赋值,且之后不允许重新赋值(但如果你声明的是一个对象或数组,你可以修改其内部属性或元素)。
- 暂时性死区(Temporal Dead Zone, TDZ):
var
:不存在TDZ。let
和const
:在声明之前的区域被称为暂时性死区(TDZ)。在这个区域内引用这些变量会导致引用错误。
- 全局声明:
var
:在全局作用域中声明时,会创建一个全局变量(这通常是不好的做法)。let
和const
:在全局作用域中声明时,它们不会成为全局对象的属性(在浏览器中,全局对象是window
)。因此,它们不会意外地创建全局变量。(特别注意在箭头函数中 this 可能会指向的属性,可能输出为 undefined)
总的来说,let
和 const
提供了比 var
更严格和更可预测的作用域和生命周期控制,因此在现代 JavaScript 编程中更受欢迎。通常,我们推荐尽可能使用 let
和 const
,并在确实需要改变变量值时选择 let
,而在知道变量的值在声明后不会改变时选择 const
。
5. Set、Map、WeakSet、WeakMap
5.1 Set、Map
Set 和 Map 是两种重要的数据结构,它们在许多编程语言中都有实现,尤其是在 JavaScript 中得到了广泛的应用。以下是对 Set 和 Map 的详细解释:
- Set
- 定义:
- Set是一种集合数据结构,它允许存储不重复的值。
- 这些值可以是原始值(如数字、字符串)或对象引用。
- 特性:
- Set中的元素是唯一的,即不会出现重复的值。
- Set中的元素是无序的,即它们不按照插入的顺序存储。
- Set的大小可以通过其
size
属性来获取(在JavaScript中)。
- 操作方法:
add(value)
:向Set中添加一个新元素。has(value)
:检查Set中是否包含某个元素。delete(value)
:从Set中删除一个元素。clear()
:清空Set中的所有元素。values()
、keys()
、entries()
:返回包含Set中所有元素、键(与值相同)和键值对的迭代器(在JavaScript中)。
- 应用场景:
- 当需要存储唯一值的集合时,可以使用Set。
- 可以用于去除数组中的重复元素。
- 可以用于实现数学中的集合运算,如并集、交集和差集。
- 定义:
- Map
- 定义:
- Map是一种键值对集合数据结构。
- 它允许任何类型的数据(对象或原始值)作为键。
- 特性:
- Map中的键是唯一的,但值可以重复。
- Map中的键值对是按照插入的顺序存储的。
- Map的大小可以通过其
size
属性来获取(在JavaScript中)。
- 操作方法:
set(key, value)
:向Map中添加一个新的键值对。get(key)
:根据键获取对应的值。has(key)
:检查Map中是否包含某个键。delete(key)
:从Map中删除一个键值对。clear()
:清空Map中的所有键值对。keys()
、values()
、entries()
:返回包含Map中所有键、值和键值对的迭代器(在JavaScript中)。forEach()
:遍历Map中的所有键值对,并对每个键值对执行指定的操作。
- 应用场景:
- 当需要存储键值对并希望保持插入顺序时,可以使用Map。
- 可以用于实现对象的键值对存储和检索。
- 可以用于在需要快速查找、插入和删除操作的场景中。
- 定义:
- 总结
- Set和Map都是重要的数据结构,它们在存储和操作数据方面提供了不同的功能和特性。
- Set主要用于存储唯一值的集合,而Map则用于存储键值对并保持插入顺序。
- 在选择使用Set还是Map时,应根据具体的应用场景和需求来决定。
5.2 Map、WeakMap
Map 和 WeakMap 都是 JavaScript 中用于存储键值对的数据结构,但它们在键的类型、键的引用方式、内存管理、遍历与大小以及使用场景等方面存在显著差异。以下是对两者的详细比较:
- 键的类型
- Map:Map 的键可以是任意类型,包括原始数据类型(如字符串、数字)和对象。这提供了一种更加灵活的键值对存储方式。
- WeakMap:WeakMap 的键只能是对象类型(包括继承自 Object 的类型和 Symbol 类型,但 null 除外)。它不接受原始数据类型作为键。
- 键的引用方式
- Map:Map 中的键是强引用。这意味着,只要 Map 存在,其键就不会被垃圾回收机制回收,即使没有其他地方引用这些键。
- WeakMap:WeakMap 中的键是弱引用。这意味着,当键对象没有其他强引用时,垃圾回收机制可以回收这些键对象。这种弱引用方式有助于避免内存泄漏。
- 内存管理
- Map:由于 Map 中的键是强引用,因此即使键对象不再需要,只要它们仍在Map中作为键存在,就不会被垃圾回收。这可能导致内存泄漏,特别是当 Map 中存储了大量不再需要的键值时。
- WeakMap:WeakMap 的弱引用特性使其能够自动释放不再需要的键和键值对。当键对象没有其他引用时,与其关联的键值对也会自动被垃圾回收机制回收。这有助于优化内存使用并减少内存泄漏的风险。
- 遍历与大小
- Map:提供了遍历方法和
size
属性来获取其成员的数量和进行遍历。 - WeakMap:没有遍历方法和
size
属性,因为其成员的数量取决于垃圾回收机制的运行情况。
- Map:提供了遍历方法和
- 使用场景
- Map:Map 适用于需要长期保留键值对的情况,特别是当键是原始数据类型或需要确保键不会被垃圾回收时。它提供了丰富的 API 方法(如set、get、has、delete等)来操作键值对。
- WeakMap:WeakMap 适用于以下场景:
- 存储与对象关联的元数据或缓存数据,当对象不再需要时,与其关联的元数据或缓存数据也会自动被回收。
- 在事件处理中,使用 WeakMap 可以自动解除事件监听器,从而避免内存泄漏。
- 在实现私有属性和方法时,可以使用 WeakMap 将私有数据关联到公共接口上,而不会增加对象的内存占用。
6. Proxy
7. JSON
JSON(JavaScript Object Notation,JavaScript对象表示法)是一种轻量级的数据交换格式。它易于人阅读和编写,同时也便于机器解析和生成。JSON 格式独立于编程语言,但其语法与 JavaScript 对象表示法相似,因此在前端开发特别是使用 JavaScript 时非常常见。
基本数据类型和结构:
JSON 支持几种基本类型的数据,包括字符串、数字、布尔值、数组、对象和 null。例如:
json{ "name": "John", "age": 30, "isStudent": false, "courses": ["Math", "Science"], "address": { "city": "New York", "zipcode": "10001" } }
使用场景:
- 主要用于数据交换,特别是在 Web 浏览器和服务器之间。例如,当客户端向服务器发送请求或从服务器接收数据时,通常会使用 JSON。
- 存储配置文件。许多现代的应用程序使用 JSON 格式的配置文件,因为它们易于阅读和修改。
相比其他格式的优势:
- 与 XML 相比,JSON 更轻量级,阅读和编写都更加简洁。
- 支持更丰富的数据类型,例如数组和内嵌对象,直观地表示复杂的数据结构。
解析和生成:
在 JavaScript 中,使用
JSON.parse()
将 JSON 字符串解析为 JavaScript 对象,使用JSON.stringify()
将 JavaScript 对象转换为 JSON 字符串。javascriptconst jsonString = '{"name": "John", "age": 30}'; const jsonObject = JSON.parse(jsonString); console.log(jsonObject.name); // 输出: John const obj = { name: "Doe", age: 22 }; const str = JSON.stringify(obj); console.log(str); // 输出: {"name":"Doe","age":22}
注意事项:
- JSON 的键名必须用双引号括起来,字符串值也需要使用双引号。
- JSON 的表示法不支持对函数、日期对象或正则表达式的直接序列化和反序列化。
安全性:
- 在解析来自不受信任的数据源的 JSON 时,需小心防范恶意代码注入。可以使用标准库或框架中内置的解析方法来确保安全性和正确性,而不是使用
eval()
方法来解析 JSON。
- 在解析来自不受信任的数据源的 JSON 时,需小心防范恶意代码注入。可以使用标准库或框架中内置的解析方法来确保安全性和正确性,而不是使用