基础知识

本文所谓的基础知识只包含重点内容和易错点,简单或常见知识点已省略。完整 JavaScript 内容可详见参考资料:JavaScript 教程(网道) ES6 标准入门教程(网道)

0. 基础

如何引入 JS 代码?

如果在 HTML 中引入 JavaScript 代码,则通过<script></script>包裹;如果在 HTML 中引入 JavaScript 文件,则通过<script src="文件地址"></script>引入。

如果脚本无需等待页面解析,且无依赖独立运行,那么应使用 async;如果脚本需要等待解析,且依赖于其它脚本,调用这些脚本时应使用 defer,将关联的脚本按所需顺序置于 HTML 中,脚本将按照在页面中出现的顺序加载。

<!-- defer 异步下载 JS 文件不阻塞 DOM 解析,HTML 标签解析完成后按顺序执行 JS 文件 -->
<script src="文件地址" defer></script>

<!-- async 异步下载 JS 文件不阻塞 DOM 解析,当下载完阻塞 DOM 解析转而执行 JS 文件 -->
<script src="文件地址" async></script>

JavaScript 的组成?

  • ECMAScript:JavaScript 的语法标准,包括变量、表达式、运算符、函数、if/for语句等;

  • 浏览器环境:

    • DOM:Document Object Model(文档对象模型),操作页面上的元素的API,比如让盒子移动、变色、改变大小、轮播图等等;

    • BOM:Browser Object Model(浏览器对象模型),操作浏览器部分功能的API,比如弹框、控制浏览器跳转、获取浏览器分辨率等等。

  • Node.js 环境

  • ......

1. 变量/常量

1.1 变量声明

  • ES5 中,使用 var 定义全局变量(如今开发中尽量不要出现var了)。

  • ES6 中,使用let定义局部变量,使用const定义常量。

var、let、const 的区别

  • var 声明的变量会挂载在 window 对象上(会污染),而 let 和 const 声明的变量不会

  • var 声明的变量存在变量提升,let 和 const 声明的变量不存在变量提升,要先声明再使用

  • var 声明不存在块级作用域,let 和 const 声明存在块级作用域

  • 同一作用域下,var 可以重复声明变量,let 和 const 不能重复声明变量

  • let 和 const 存在暂时性死区(声明语句必须放在使用之前)

  • const 一旦声明必须赋值,声明后不能再修改(当然如果是对象类型则另说)

1.2 解构赋值

ES6 允许按照一一对应的方式,从数组或者对象中提取值,再将提取出来的值赋值给变量。解构赋值的拷贝是浅拷贝,如果一个键的值是复合类型的值(数组、对象、函数),那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。

数组的解构赋值

对象的解构赋值

字符串的解构赋值

函数参数的解构赋值

应用

2. 数据类型

基本数据类型:参数赋值的时候,传数值

  • String 字符串

  • Number 数值

  • Boolean 布尔值

  • null 空值

  • undefined 未定义

  • symbol

引用数据类型:参数赋值的时候,传地址

  • Object 对象(Function, Array, Date, RegExp, String, Boolean, Number, Error 等)

JS 提供了 String/Boolean/Number 这三个基本包装对象,一般情况我们不会去使用这个基本包装对象的,其主要作用是当我们对一些基本数据类型的值去调用属性和方法时,浏览器会临时使用包装类将基本数据类型转换为引用数据类型,这样的话,基本数据类型就有了属性和方法,然后再调用对象的属性和方法;调用完以后,再将其转换为基本数据类型。例如'abc'.length字符串本身是没有属性和方法的。

2.1 数据类型转换/识别

数据类型

2.2 符号 Symbol

Symbol 是一种新的原始数据类型,每一个 Symbol 值都是不相等的,主要用作对象属性名(作为标识符),可以保证不会与其他属性名产生冲突,Symbol 值通过 Symbol 函数生成,let s = Symbol();

Symbol 作为属性名,遍历对象的时候,该属性不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。但是Object.getOwnPropertySymbols()方法,可以获取指定对象的所有 Symbol 属性名,Reflect.ownKeys()方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。

Symbol.for() 接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局。symbol() 不会搜索是否存在给定key值的symbol,直接创建新的symbol值。

Symbol.keyFor()方法返回一个已登记的 Symbol 类型值的key,注意通过symbol()生成的不登记,因此不适用。

2.3 ES6 Set/Map

2.2.1 Set

Set 类似于数组,但是成员的值都是唯一的,没有重复的值。

常见应用:

  • 数组去重

  • 存储用户搜索记录不需要重复数据,可以用 Set 来存储用户的搜索记录

  • ......

属性:

  • Set.prototype.constructor:构造函数,默认就是Set函数。

  • Set.prototype.size:返回Set实例的成员总数。

实例方法:

  • Set.prototype.add(value):添加某个值,返回 Set 结构本身。

  • Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。

  • Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。

  • Set.prototype.clear():清除所有成员,没有返回值。

遍历操作 - keys方法和values方法的行为完全一致:

  • Set.prototype.keys():返回键名的遍历器,

  • Set.prototype.values():返回键值的遍历器

  • Set.prototype.entries():返回键值对的遍历器

  • Set.prototype.forEach():使用回调函数遍历每个成员

2.2.2 Map

类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键,Map 结构提供了“值—值”的对应。

Map 与其他数据之间的转换

  • Map 与数组

  • Map 与对象

  • Map 与 JSON

2.2.3 WeakSet 和 WeakMap

WeakSet的成员只能是对象,而不能是其他类型的值。WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用。WeakSet 不能遍历,也没有size属性。

WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。WeakMap的键名所指向的对象,不计入垃圾回收机制。WeakMap不能遍历,也没有size属性。

2.4 易错点

如果在引号里面使用相同的引号,则需要用 \ 进行转义。

字符串 + 运算,如果前面两个以上为数值类型,则 + 作数字运算,直到遇到第一个字符串,则 + 又变成字符串拼接。

直接使用字面量创建字符串与对象创建是不同的。

undefined表示一个声明了没有赋值的变量,变量只声明的时候默认就是undefined;null 的数据类型是 object,null 表示一个空,变量值如果想变为 null,必须手动设置。

如果布尔对象无初始值或者其值为0、-0、null、undefined、""、NaN、false,则对象的值为 false。否则,其值为 true(即使当自变量为字符串 "false" 时)!

3. 操作/运算符

3.1 自增和自减

  • a++:先把 a 的值赋值给表达式,然后 a 再自增

  • ++a:a 先自增,然后再把自增后的值赋值给表达式

3.2 逻辑运算的妙用

与运算的返回结果:(以多个非布尔值的运算为例)

  • 如果第一个值为false,则执行第一条语句,并直接返回第一个值;不会再往后执行。

  • 如果第一个值为true,则继续执行第二条语句,并返回第二个值(如果所有的值都为true,则返回的是最后一个值)。

或运算的返回结果:(以多个非布尔值的运算为例)

  • 如果第一个值为true,则执行第一条语句,并直接返回第一个值;不会再往后执行。

  • 如果第一个值为false,则继续执行第二条语句,并返回第二个值(如果所有的值都为false,则返回的是最后一个值)。

在实际开发中,经常用到这种写法,是一种很好的「容错、容灾、降级」方案。

3.3 非数值的比较

  • 对于非数值进行比较时,会将其转换为数字然后再比较。

  • 如果符号两侧的值都是字符串时,不会将其转换为数字进行比较,而是比较Unicode编码

  • 任何值和NaN做任何比较都是false。

3.4 运算符优先级

优先级从高到低:

  • ()

  • ++--

  • !~+(单目)、-(单目)、typeofvoiddelete

  • %*/

  • +(双目)、-(双目)

  • <<>>>>>

  • <<=>>=

  • ==!=====!==

  • &^|&&||

  • ?:

  • =+=-=*=/=%=<<=>>=>>>=&=^=|=

  • ,

4. 语句

if 语句推荐使用 return 的写法

switch-case 的缺点在于 case 的值只能判断等于,而不能有其他判断,这个较 if 语句较弱,且 break 不写则会继续进行下一个 case。

循环结构有三种:while、do-while、for,一般while循环解决无法确定循环次数的,能够确定循环次数的多采用 for 循环。

continue 和 break

  • break立即跳出整个循环,即循环结束,开始执行循环后面的内容(直接跳到大括号)

  • continue立即跳出当前循环,继续下一次循环

还有一个非常容易被忽视的特性"标签",用于控制流,一个标签可以被用于 break , continue 语句去更精确地控制流。 通过标签可以跳出指定的循环,而不是仅仅就近原则循环。

5. 函数

5.1 函数声明

函数的作用域在函数定义时,就已经确定了,查找时采用的是就近原则

知识点:自调用函数

如果函数内部修改的不是参数对象的某个属性,而是替换整个参数,这样不会影响到原始值,赋值会指向另一个地址。

5.2 箭头函数

  1. 如果返回对象,需要再加一层 (),否则花括号被解释为代码块 let getTempItem = id => ({ id: id, name: "Temp" });

  2. 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

  3. 不可以当作构造函数,也就是说,不可以使用new命令。

  4. 不可以使用arguments对象,该对象在函数体内不存在,但可以通过剩余运算符来弥补。

  5. 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

5.3 默认值设置

函数设置默认值,函数的 length 属性,将返回没有指定默认值的参数个数。

与解构赋值同用时需要注意 - 函数参数的默认值先生效,然后解构赋值的默认值生效。

5.4 ... 剩余运算符

剩余参数允许我们将不确定数量的剩余的元素放到一个数组中,其区别于arguments变量,arguments对象是类似数组,需要先将其转变为数组 Array.prototype.slice.call()。

5.5 类数组 arguments

在调用函数时,浏览器每次都会传递进两个隐含的参数:

  • 函数的上下文对象 this

  • 封装实参的对象 arguments

arguments 是一个类数组对象,在调用函数时,我们所传递的实参都会在 arguments 中保存。

  • arguments.length:返回函数实参的个数

  • arguments[0] = 99:arguments 可以修改元素

  • arguments 是类数组对象,可以遍历,但不具有数组的 push()、pop() 等方法,需要通过 [...arguments] 或 Array.from(arguments) 进行转化为数组

5.6 立即调用的函数表达式 IIFE

IIFE的目的有两个:一是不必为函数命名,避免了污染全局变量;二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。

5.7 构造函数

构造函数是一种特殊的函数,主要用来创建和初始化对象,也就是为对象的成员变量赋初始值,普通函数是直接调用,而构造函数需要使用 new 关键字来调用。

构造函数可结合 Class 类 阅读

6. 对象

在 JavaScript 中,对象具有特征(属性)和行为(方法),其是是一组无序的相关属性和方法的集合。对象主要包括以下几类:

  • 内置对象(ES标准中定义的对象)

  • 宿主对象(如浏览器提供的对象)

  • 自定义对象(开发人员自定义对象)

6.1 ES6 属性扩展

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。

ES6中允许用表达式作为对象的属性名,方法名也适用,但是属性名表达式与简洁表示法,不能同时使用,方法名无关。

6.2 对象属性遍历

  • for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性) - 由于引入继承,不建议使用

  • Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。

  • Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

  • Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。

6.3 自定义对象

6.4 Object 对象

Object 对象

7. Class 类

传统生成实例对象是通过构造函数实现,引入了 Class(类)这个概念,可以通过class关键字定义类。实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上),类的所有方法都定义在类的prototype属性上面,如下面的 toString。

对某个属性设置存值函数和取值函数可以拦截该属性的存取行为:

7.1 静态成员/公有成员

私有属性和方法目前仍在提案中,暂不说明。

7.2 Class 继承

原型链/继承

8. Proxy 对象

Proxy 在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。具体的拦截操作详见 ES6 - proxy

9. Iterator 遍历器

Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口,即for...of;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

原生具备 Iterator 接口的数据结构:

  • Array

  • Map

  • Set

  • String

  • TypedArray

  • 函数的 arguments 对象

  • NodeList 对象

调用 Iterator 接口的场合:

  • 解构赋值

  • 扩展运算符

  • yield*

  • for...of

  • Array.from()

  • Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]])

  • Promise.all()

  • Promise.race()

for...of 遍历还有一个重要的特点,不同于 forEach 方法,它可以与 break、continue 和 return 配合使用。

10. 模块化

模块化

11. 异步相关内容

Promise 对象、Generator 函数、async 函数、定时器等相见异步操作章节。

异步操作

如果你对内容有任何疑问,欢迎提交 ❕issues✉️ email

最后更新于

这有帮助吗?