JSON.stringify 的解析

JSON.stringify(value[, replacer [, space]]) 方法将一个 JavaScript 对象或值转换为 JSON 字符串,如果指定了一个 replacer 函数,则可以选择性地替换值,或者指定的 replacer 是数组,则可选择性地仅包含数组指定的属性。

Note 1

JSON.stringify 方法对对象进行序列化时,如果对象的属性是 undefined/Symbol 值或任意函数时,不会对其进行处理。

这会导致被序列化的对象中的属性并不会按照原定的顺序被输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const someObj = {
_id: 'id',
productCode: 'P28120068',
productDetail: {
name: 'Star Insure',
price: 1200,
amount: 1000000
},
created_at: 'Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)',
updated_at: 'Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)',
nullValue: null,
undefinedValue: undefined, // JSON.stringify 不会序列化 undefined
symbolValue: Symbol('TypeScript'), // JSON.stringify 不会序列化 Symbol
arrowLog: () => console.log(this), // JSON.stringify 不会序列化 箭头函数
funLog: function() { console.log(this) }, // JSON.stringify 不会序列化 函数
}

/**
* Note 1 : undefined、Symbol、任何函数作为对象的属性值时,JSON.stringify 不对其(忽略)进行序列化
* {
* "_id":"id",
* "productCode":"P28120068",
* "productDetail":{"
* name":"Star Insure",
* "price":1200,
* "amount":1000000
* },
* "created_at":"Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)",
* "updated_at":"Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)",
* "nullValue":null
* }
*/
console.log(JSON.stringify(someObj));

Note 2

JSON.stringify 方法对数组进行序列化时,如果数组元素的值是 undefinedSymbol 值或任意函数时,会被序列化为 null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const someArr = [
null,
undefined, // JSON.stringify 将数组中的 undefined 序列化为 null
Symbol('TypeScript'), // JSON.stringify 将数组中的 Symbol 序列化为 null
() => console.log(this), // JSON.stringify 将数组中的 箭头函数 序列化为 null
function() { console.log(this) } // JSON.stringify 将数组中的 函数 序列化为 null
]

/**
* Note 2 : undefined, Symbol, 任意函数作为数组的元素时,JSON.stringify 将其序列化为 null
* [null,null,null,null,null]
*/
console.table(JSON.stringify(someArr));

Note 3

如果 undefinedSymbol 值或任意函数作为单独的值被 JSON.stringify 序列化为 undefined

1
2
3
4
5
6
/**
* Note 3 : undefined, Symbol, 任意函数作为单独的值时,JSON.stringify 序列化会返回 undefined
*/
console.log(JSON.stringify(undefined)); // undefined
console.log(JSON.stringify(Symbol('TypeScript'))); // undefined
console.log(JSON.stringify(() => console.log(this))); // undefined

Note 4

如果目标值有 toJSON 方法,则 toJSON 来决定 JSON.stringify 返回的值。

1
2
3
4
5
console.log(JSON.stringify({
name: 'Jimmy',
age: 20,
toJSON: () => 'this is a toJSON function',
})); // this is a toJSON function

Note 5

NaN, InInfinitynull 会被JSON.stringify 序列化为 null

1
2
3
4
5
6
7
8
9
/**
* NaN, InInfinity 及 null 会被 JSON.stringify 序列化为 null
*/
console.log(NaN); // NaN
console.log(JSON.stringify(NaN)); // null
console.log(Infinity); // Infinity
console.log(JSON.stringify(Infinity)); // null
console.log(null); // null
console.log(JSON.stringify(null)); // null

Note 6

数字、字符串和布尔值的包装值在被 JSON.stringify 序列化时会解包成对应的基础值。

1
2
3
const packArr = [ new Number(1), new String('TypeScript'), new Boolean(false)];
console.log(packArr); // [ [Number: 1], [String: 'TypeScript'], [Boolean: false] ]
console.log(JSON.stringify(packArr)); // [1,"TypeScript",false]

Note 7

JSON.stringify 在序列化对象时,仅会序列化可枚举的属。

1
2
3
4
5
const enumerableObj = Object.create(null, {
name: { value: 'jack', enumerable: true },
age: { value: 20, enumerable: false }
});
console.log(JSON.stringify(enumerableObj)); // { name: 'jack' }

Note 8

如果 JSON.stringify 的目标对象发生循环引用时,会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 对于相互引用的对象,会报错
* TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'refA' -> object with constructor 'Object'
--- property 'refB' closes the circle
*/
const objA = {
name: 'Object A',
refB: null,
};

const objB = {
name: 'Object B',
refA: objA,
};

objA.refB = objB;

Note 9

Symbol 值为键的属性值,在被 JSON.stringify 序列化时会被忽略。

1
2
3
4
5
6
7
8
9
/**
* 以 Symbol 值为键的属性值,在被 JSON.stringify 序列化时会被忽略
*/
const symbolObj = {
name: 'Symbol',
[Symbol.for('Age')]: 20,
};
console.log(symbolObj); // { name: 'Symbol', [Symbol(Age)]: 20 }
console.log(JSON.stringify(symbolObj)); // {"name":"Symbol"}

Note 10

JSON.stringify 的第二个参数是函数时, 可单独对某个值进行处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const someObj = {
_id: 'id',
productCode: 'P28120068',
productDetail: {
name: 'Star Insure',
price: 1200,
amount: 1000000
},
created_at: 'Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)',
updated_at: 'Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)',
nullValue: null,
undefinedValue: undefined, // JSON.stringify 不会序列化 undefined
symbolValue: Symbol('TypeScript'), // JSON.stringify 不会序列化 Symbol
arrowLog: () => console.log(this), // JSON.stringify 不会序列化 箭头函数
funLog: function() { console.log(this) }, // JSON.stringify 不会序列化 函数
}

/**
* 当 JSON.stringify 的第二个参数是函数时, 可单独对某个值进行处理
*/
console.log(JSON.stringify(someObj, (k, v) => {
console.log(v)
if (typeof v === 'undefined') return 'undefined';
return JSON.stringify(v);
}));

JSON.stringify 的第二个参数是数组时,可在数组中加入属性名,来规定返回哪些属性的序列化值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const someObj = {
_id: 'id',
productCode: 'P28120068',
productDetail: {
name: 'Star Insure',
price: 1200,
amount: 1000000
},
created_at: 'Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)',
updated_at: 'Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)',
nullValue: null,
undefinedValue: undefined, // JSON.stringify 不会序列化 undefined
symbolValue: Symbol('TypeScript'), // JSON.stringify 不会序列化 Symbol
arrowLog: () => console.log(this), // JSON.stringify 不会序列化 箭头函数
funLog: function() { console.log(this) }, // JSON.stringify 不会序列化 函数
}

/**
* 当 JSON.stringify 的第二个参数是数组时,可在数组中加入属性名,来规定返回哪些属性的序列化值
* 但当值是 undefined、Symbol、任何函数时无效
*/
console.log(JSON.stringify(someObj, ['productCode', 'arrowLog'])); // { productCode: 'P28120068' }

Reflow and Redraw

回流和重绘以及性能的优化

在解释回流和重绘的概念之前,我们首先需要知道两个概念:DOM 和 CSSOM。

  • DOM (Document Object Model) 文档对象模型

    DOM 是 HTML 和 XML 文档的编程接口,它提供了对文档的结构化表述,并使得开发人员能够从程序中访问它,改变文档的结构、样式和内容。DOM 将文档解析为一个由节点和对象(包含属性和方法的对象)组成的结构集合。

  • CSSOM (CSS Object Model) CSS对象模型

    CSSOM 是一组允许 JavaScript 操作 CSS 的 API,非常类似于 DOM,它允许用户动态的修改 CSS 样式。

DOM 和 CSSOM 构建

浏览器渲染页面时会构建 DOM 和 CSSOM 树,这个过程可简化如下:

  1. 转换 - 浏览器从磁盘或网络读取 HTML 的原始字节,并按照其指定的编码方式将其解析为字符
  2. 符号化(令牌化) - 浏览器将字符串转换为 HTML 标准规定的各种具有特殊含义和规则的令牌
  3. 词法分析- 将令牌转换为定义了其规则和属性的对象
  4. DOM构建 - HTML 标记定义了不同的标记之间的关系,根据标签创建的对象在结构树中根据由原始标记定义的父子关系相互连接。

整个过程最后形成一个文档对象结构模型,即 DOM。CSSOM 的构建与 DOM 的构建非常类似,不再重复。

渲染树的构建

最后 CSSOM 和 DOM 会结合形成渲染树。渲染树用来计算每个可见元素的布局,并在绘制到屏幕的过程中作为输出,简化后的过程如下:

  1. 从 DOM 树的根节点开始,遍历每个可见的节点
    • 有些节点是不可见的(比如 <script> 标签、<meta> 标签 等),这些节点会被忽略, 因为它们并不影响渲染的输出
    • 一些节点通过 CSS 被隐藏了,它们也会被忽略
  2. 为每一个可见的节点找到匹配的 CSSOM 规则并在节点上应用这些规则
  3. 将节点连同内容和计算好的样式发送出去

渲染树构建完成后,即进入 “布局” 阶段。

布局

在渲染树中我们已经计算好了哪个节点应该被显示和它们的计算样式,但是对于它们在设备的视口中的精确位置和大小还没有进行计算,对其进行计算就是 ”布局“,也被称为 回流

为了计算页面中每个对象的精确位置和大小,浏览器从渲染树的根节点开始遍历。

布局过程的输出是一个“盒子模型”,它精确地捕获视口中每个元素的确切位置和大小:所有相对测量值都转换为屏幕上的绝对像素。

绘制

布局完成后,我们已经知道了节点的可见性、计算样式和几何结构,浏览器将会把渲染树中的每个节点转换为屏幕上的实际像素。这个步骤通常被称为 ”绘制“ 或 ”栅格化“;

由于浏览器需要进行大量的工作进行绘制,这个阶段可能会花费一些时间。

回流

当渲染树中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。

可能会引起回流的操作如下

  • 页面首次渲染
  • 浏览器窗口大小发生改变
  • 元素尺寸或位置发生改变
  • 元素内容发生改变
  • 元素字体大小发生变化
  • 添加或删除可见的 DOM 元素
  • 激活 CSS 伪类
  • 查询某些属性或调用某些方法

常用且可能导致回流的属性和方法

  • clientWidth , clientHeight, clientTop, clientLeft
  • offsetWidth, offsetHeight, offsetTop, offsetLeft
  • scrollWidth, scrollHeight, scrollTop, scrollLeft
  • scrollIntoView(), scrollIntoViewIfNeeded()
  • getComputedStyle()
  • getBoundingClientReact()
  • scrollTo()

重绘

当页面中元素样式的改变并不影响它在文档流中的位置时(例如:colorbackground-colorvisibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。

性能影响

回流比重绘的代价要高的多。有时候仅仅回流一个单一的元素,它的父元素及任何与它相关的元素都会产生回流。

浏览器会对频繁的回流或重绘操作进行优化:

浏览器会维护一个队列,把所有引起回流和重绘的操作放入队列中,如果队列中的任务数量或者时间间隔达到一个阈值的时候,浏览器就会将队列清空,进行一次批处理,这样可以把多次回流和重绘变成一次。

当你访问下列属性是,浏览器会立刻清空队列:

  • clientWidth , clientHeight, clientTop, clientLeft
  • offsetWidth, offsetHeight, offsetTop, offsetLeft
  • scrollWidth, scrollHeight, scrollTop, scrollLeft
  • width, height
  • getComputedStyle()
  • getBoundingClientRect()

因为队列中可能会有影响到这些属性或方法返回值的操作,即使你希望获取的信息与队列中操作引发的改变无关,浏览器也会强行清空队列,确保你拿到的值是最精确的。

如何避免回流

CSS

  • 避免使用table布局。
  • 尽可能在DOM树的最末端改变class
  • 避免设置多层内联样式。
  • 将动画效果应用到position属性为absolutefixed的元素上。
  • 避免使用CSS表达式(例如:calc())。

JavaScript

  • 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
  • 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
  • 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
  • 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
  • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。

防抖和节流

防抖和节流

处理频繁触发的事件时,如果没有进行限制,每次触发都调用处理函数进行处理不仅会浪费资源,还会降低用户体验,所以需要限制处理函数的调用频率,这便是用到防抖和节流的地方。

函数防抖

当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。

函数防抖的一个典型应用场景是根据 input 的输入内容搜索相关信息,如果 input 内容每次改变都发起搜索请求,则会在短时间内发次多个请求,这种情况下,对事件处理函数进行防抖处理,在给定的时间段 T 内,input 内容没有改变,才发起搜索请求。

1
2
3
4
5
6
7
8
9
10
// 一个简单的防抖函数
function debounce(fn, wait) {
let timeout = null;
return function() {
if (timeout !== null) {
clearTimeOut(timeout);
}
timeout = setTimeOut(fn, wait);
};
}

函数节流

当持续触发事件时,保证一定时间段内只调用一次事件处理函数。

函数节流适合的处理的情况是更高频次的触发事件处理函数的情况,比如 resizemousemovescroll 等,强制事件处理函数以固定频率执行。

  • 时间戳实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 利用时间戳实现节流函数
function throttle(fn, delay) {
let prev = Date.now();
return function() {
let context = this;
let args = arguments;
let now = Data.now();
if (now - prev >= delay) {
fn.apply(context, args);
prev = Date.now();
}
};
}

// 事件处理函数
function handler() {}

// 事件监听
window.addEventListener('scroll', throttle(handler, 500))
  • 定时器实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 利用定时器实现节流函数
function throttle(fn, delay) {
let timer = null;
return function() {
let context = this;
let args = arguments;
if (!timer) {
timer = setTimeOut(function() {
fn.apply(context, args);
timer = null;
}, delay)
}
};
}

// 事件处理函数
function handler() {}

// 事件监听
window.addEventListener('scroll', throttle(handler, 500))
  • 混合实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function throttleFun(fun, duration) {
let timer = null;
let startT = Date.now();
return function() {
const context = this;
const args = arguments;
const remaining = duration - ( startT - Date.now());
clearTimeout(timer);
if (remaining <= 0) {
fun.apply(context, args);
startT = Date.now();
} else {
timer = setTimeout(fun, remaining);
}
}
}

防抖和节流的区别

防抖将几次操作合并为一次操作,节流则保证一段时间内只触发一次函数。

函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。

Object Oriented Programming

面向对象编程 (Object Oriented Programming)

面向对象编程是一种基于 对象 概念的编程范例,对象可以以字段的形式包含数据(通常被称为属性),以程序的形式包含代码(通常被称为方法)。

对象的一个特点就是它的方法可以访问和修改与之关联的对象的数据字段。在面向对象编程中,计算机程序被设计为由能够互相交互的对象们组成。面向对象编程语言是多种多样,最受欢迎的是基于类(Class)的那些。

面向对象编程的特征

通常,面向对象编程被理解为一种将程序分解为封装数据及相关操作的模块的编程范例。它的特征如下:

  • 类与对象

    支持面向对象编程的语言通常使用继承类来达到代码重用或可扩展性。

    • 类 :定义了事物的抽象特点
    • 对象 :类的实例
  • 动态配置( dynamic dispatch )与消息传递机制

    对象的任务是通过选择执行合适的程序代码响应方法调用,通常这种选择是在运行时查找与对象关联的表中的方法来实现的。这被称之为动态分配,它将对象与抽象数据类型(或模块)区分开来,后者具有所有实例的操作静态实现。

    方法调用也被称之为消息传递。它被概念化为传递给对象进行分配的消息(即方法的名称和输入的参数)。

  • 封装

    封装是面向对象编程的一个概念,它将数据和对数据的操作绑定在一起,并保护它们免受外部的干扰和误用(仅让允许的类和对象进行访问)。数据封装引入了重要的面向对象编程数据隐藏概念。

    如果一个类不允许调用代码直接访问对象内部的数据,而只允许通过它提供的方法进行访问,这种强大的抽象或信息隐藏被称之为封装。

    具备封装性的面向对象编程隐藏了某一方法对的具体执行过程,取而代之的是通过消息传递机制传递消息给它。

  • 继承

    几乎所有支持类的语言都支持继承。继承使得类能够以层级结构表示 is-a-type-of 的关系。父类的所有数据和方法都能被子类以同样的名字所继承,这使得相同的代码和数据复用变得更加简单,而且还是镜像真实世界关系的一种直观方式。

    子类可以重写父类定义的方法。在一些语言中存在多重继承,这可能会造成重写冲突。

    抽象类不能实例化为对象,它们的存在只是为了可以被继承到其它可以被实例化的具体类中。

    继承的过程就是一个从一般到特殊的过程。

  • 多态

    多态是指有继承产生的相关但不同的类,对统一消息会做出不同的响应。它能极大减少类型之间的耦合。

  • 抽象

JavaScript 与面向对象编程

JavaScript 的核心是支持面向对象的,同时它也提供了强大灵活的 OOP 语言能力。

命名空间

命名空间是一个容器,它允许开发人员在一个独特的、特定于应用程序的名称下捆绑所有功能。在 JavaScript 中,命名空间是一个包含方法、属性和对象的对象,它和普通对象在语言层面上并无差别。

使用命名空间可以最大程度上减少应用程序名称冲突的可能性。

自定义对象

JavaScript 是基于原型的语言,并没有声明类的语句。它使用函数做类,定义一个类和定义一个函数相同。

1
2
3
function Person() {}
// or
let Person = function() {}
对象

对象是类的实例。

1
2
3
4
function Person() {}
// 创建为初始化的实例
let person1 = new Person();
let person2 = Object.create(Person)
构造器

构造器在实例化时被调用,它是对象中的一个方法,常用于给对象的属性赋值或者为调用函数做准备。在 JavaScript 中函数就可以作为构造器使用,因此不需要特别地定义一个构造器方法,每个声明的函数都可以在实例化后被调用执行。

属性

属性就是类中包含的变量。每一个对象实例有若干个属性. 为了正确的继承,属性应该被定义在类的原型属性 (函数)中。

可以使用 this 关键字调用类的属性,它是对当前对象的引用。

1
2
3
4
5
function Person(name) {
this.name = name;
}

let person = new Person('Li Lei');
方法

在JavaScript中方法通常是一个绑定到对象中的普通函数, 这意味着方法可以在其所在上下文之外被调用。

1
2
3
4
5
6
7
function Person(name) {
this.name = name;
}

Person.prototype.sayHello = function() {
console.log('My name is ' + this.anme);
}

在方法调用的过程中,this 的值取决于我们如何调用方法。可以通过 Function#callFuncation#apply 显式的指定 this 的值。

继承

在 Javascript 中,继承通过赋予子类一个父类的实例并专门化子类来实现。在现代浏览器中你可以使用 Object.create() 实现继承。

1
2
3
4
5
6
7
8
function Person() {}

function Student() {
Person.call(this);
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
封装

类的实例调用存在于类中的方法时,并不清楚它是如何实现并运行的,这就是封装。

抽象

抽象是允许模拟工作问题中通用部分的一种机制。这可以通过继承(具体化)或组合来实现。
JavaScript通过继承实现具体化,通过让类的实例是其他对象的属性值来实现组合。

多态

就像所有定义在原型属性内部的方法和属性一样,不同的类可以定义具有相同名称的方法;方法是作用于所在的类中。并且这仅在两个类不是父子关系时成立(继承链中,一个类不是继承自其他类)。

CSS 备忘

媒体查询

1
2
// 基本形式
@media <media-type> and|not|only (<media-feature>) { css-code ... }

媒体类型

类型 描述
all 所有设备
screen 电脑屏幕,平板电脑,智能手机等(默认值,可省略)
print 打印机和打印预览
speech 屏幕阅读器等发声设备

媒体特征

https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries


元素定位

  • 相对定位 relative

    遵循相对定位的元素相对于在文档中的正常位置偏移给定的值,但不会影响其它元素的偏移。

    它原先占据的位置将会被保留。

  • 绝对定位 absolute

    相对定位的元素并未脱离文档流,而绝对定位的元素则脱离了文档流。在不知文档流中的其它元素时,绝对定位的元素不占据空间,它相对于最近的非 static 祖先元素定位。当这样的祖先元素不存在时, 则相对于 ICB(Initial Container Block) 定位。

  • 固定定位 fixed

    固定定位元素与绝对定位相似,但其包含块为 viewport

    但当固定定位的元素的祖先的 transform 属性为非 none 时,定位参考更改为该祖先元素。

    viewport 是一个网页的用户可视区域

  • 粘性定位 sticky

    粘性定位可以被认为是相对定位和固定定位的结合:元素在跨越特定阈值前为相对定位,之后为固定定位。

    必须指定 top, right, bottom, left 四个阈值其中之一,才能使粘性定位生效,否则其行为和相对定位相同。


CSS 选择器

简单选择器

  • 类型选择器

    element { style properties }

    选择所有匹配给定的节点名称的元素

  • 类选择器

    .className { style properties }

    选择所有拥有给定的类属性的元素

  • ID 选择器

    #idname

    基于 id 属性的值来选择一个元素,在一个文档中拥有指定的 ID 的元素应该只有一个

  • 通用选择器

    *

    选择所有元素

  • 属性选择器

    [attr] [attr=value] [attr~=value] [attr|=value] [attr^=value] [attr$=value] [attr*=value]

    根据给定的属性的值来选择元素

使用连接符

  • 兄弟选择

    A ~ B

    选择在 A 之后的所有 B 元素,且 A、B 拥有共同的父元素

  • 相邻兄弟选择

    A + B

    选择紧跟在 A 元素后的 B 元素,且 A 、B 拥有共同的父元素

  • 后代选择

    A B

    选择作为 A 元素的后代的 B 元素

  • 子元素选择

    A > B

    选择作为 A 元素直接后代的 B 元素

伪类

selector:pseudo-classes { style property }

用于向某些选择器添加特殊的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 表示被用户激活(触发)的元素,常用于 <a> 和 <button>
:active
// 表示任何 radio、checkbox 或 option 元素被选中
:checked
// 表示任何表单元素的默认状态
:default
// 表示任何被禁用的元素
:disabled
// 表示任何没有子元素的元素
:empty
// 表示元素处于可用状态,即可被激活、选中、按下、输入或获得焦点等
:enabled
// 用于 @page at-rule,表示被打印文档的第一页
:first
// 表示一组兄弟元素中的第一个
:first-child
// 表示一组兄弟元素中同类型的第一个元素
:first-of-type
// 表示用户使用指向设备和元素交互但是没有激活它的状态
:hover
// 表示任何处于 indeterminate 状态的表单元素
:indeterminate
// 表示 <input> 元素的当前值是否处于由 min 和 max 属性限制的的范围之内
:in-range
:out-range
// 表示 <input> 或其它表单元素的内容是否校验失败
:invalid
:valid
// 一组兄弟元素中的最后一个元素
:last-child
// 一组兄弟元素中同类型的最后一个元素
:last-of-type
// 表示一个元素还未被访问,匹配任何未被访问的拥有 href 属性的 <a> <area> 或 <link> 元素
:link
// 选择任何非指定元素的元素
:not()
// 选择一组兄弟元素中的指定位置的元素
:nth-child()
// 选择一组兄弟元素中的最末尾位置的元素
:nth-last-child
// 选择一组兄弟元素中的同类型的最后一个元素
:nth-last-of-type
// 在一组兄弟元素中选择同类型的元素中指定位置的元素
:nth-of-type()
// 选择一个没有任何兄弟元素的元素
:only-child
// 选择一个没有任何同类型兄弟元素的元素
:only-of-type
// 根据是否设置了 required 属性来选择 <input> <select> 或 <textarea> 元素
:optional
:required
//
:target

伪元素

element::pseudo-elements { style property }

用于向某些选择器添加特殊的效果

1
2
3
4
5
6
7
8
9
10
// 在指定的元素中的末尾创建一个子元素,经常使用 content 属性来向指定的元素添加装饰
::after
::before
//
::first-line
::first-letter
// 选中已经被用户高亮的部分
::selection
//
::slotted