# 逻辑运算符小记
&&
与运算 &&
做了如下的事:
- 从左到右依次计算操作数。
- 在处理每一个操作数时,都将其转化为布尔值。如果结果是
false
,就停止计算,并返回这个操作数的初始值。 - 如果所有的操作数都被计算过(例如都是真值),则返回最后一个操作数。
换句话说,与运算返回第一个假值,如果没有假值就返回最后一个值。
||
或运算 ||
做了如下的事情:
- 从左到右依次计算操作数。
- 处理每一个操作数时,都将其转化为布尔值。如果结果是
true
,就停止计算,返回这个操作数的初始值。 - 如果所有的操作数都被计算过(也就是,转换结果都是
false
),则返回最后一个操作数。
返回的值是操作数的初始形式,不会做布尔转换。
换句话说,一个或运算 ||
的链,将返回第一个真值,如果不存在真值,就返回该链的最后一个值。
??
a ?? b
的结果是:
- 如果
a
是已定义的,则结果为a
, - 如果
a
不是已定义的,则结果为b
。
+ 号可以隐式转换将字符变成数字
# 查找字符串小记
方法 | 选择方式…… | 负值参数 |
---|---|---|
slice(start, end) |
从 start 到 end (不含 end ) |
允许 |
substring(start, end) |
从 start 到 end (不含 end ) |
负值被视为 0 |
substr(start, length) |
从 start 开始获取长为 length 的字符串 |
允许 start 为负数 |
# Map 和 Set
Map
—— 是一个带键的数据项的集合。
方法和属性如下:
new Map([iterable])
—— 创建 map,可选择带有[key,value]
对的iterable
(例如数组)来进行初始化。map.set(key, value)
—— 根据键存储值,返回 map 自身。map.get(key)
—— 根据键来返回值,如果map
中不存在对应的key
,则返回undefined
。map.has(key)
—— 如果key
存在则返回true
,否则返回false
。map.delete(key)
—— 删除指定键对应的值,如果在调用时key
存在,则返回true
,否则返回false
。map.clear()
—— 清空 map 。map.size
—— 返回当前元素个数。
与普通对象 Object
的不同点:
- 任何键、对象都可以作为键。
- 有其他的便捷方法,如
size
属性。
Set
—— 是一组唯一值的集合。
方法和属性:
new Set([iterable])
—— 创建 set,可选择带有iterable
(例如数组)来进行初始化。set.add(value)
—— 添加一个值(如果value
存在则不做任何修改),返回 set 本身。set.delete(value)
—— 删除值,如果value
在这个方法调用的时候存在则返回true
,否则返回false
。set.has(value)
—— 如果value
在 set 中,返回true
,否则返回false
。set.clear()
—— 清空 set。set.size
—— 元素的个数。
在 Map
和 Set
中迭代总是按照值插入的顺序进行的,所以我们不能说这些集合是无序的,但是我们不能对元素进行重新排序,也不能直接按其编号来获取元素。
# Rest 和 spread
-
Array.from
适用于类数组对象也适用于可迭代对象。 -
Spread 语法只适用于可迭代对象。
当我们在代码中看到
"..."
时,它要么是 rest 参数,要么是 spread 语法。有一个简单的方法可以区分它们:
- 若
...
出现在函数参数列表的最后,那么它就是 rest 参数,它会把参数列表中剩余的参数收集到一个数组中。 - 若
...
出现在函数调用或类似的表达式中,那它就是 spread 语法,它会把一个数组展开为列表。
使用场景:
- Rest 参数用于创建可接受任意数量参数的函数。
- Spread 语法用于将数组传递给通常需要含有许多参数的函数。
我们可以使用这两种语法轻松地互相转换列表与参数数组。
- 若
# 闭包
函数嵌套函数
# Promise 和 async/await
# !Promise
用于处理异步操作,避免回调地狱。
Promise
类有 6 种静态方法:
-
Promise.all(promises)
—— 等待所有 promise 都 resolve 时,返回存放它们结果的数组。如果给定的任意一个 promise 为 reject,那么它就会变成Promise.all
的 error,所有其他 promise 的结果都会被忽略。 -
Promise.allSettled(promises)
(ES2020 新增方法)—— 等待所有 promise 都 settle 时,并以包含以下内容的对象数组的形式返回它们的结果:
status
:"fulfilled"
或"rejected"
value
(如果 fulfilled)或reason
(如果 rejected)。
-
Promise.race(promises)
—— 等待第一个 settle 的 promise,并将其 result/error 作为结果返回。 -
Promise.any(promises)
(ES2021 新增方法)—— 等待第一个 fulfilled 的 promise,并将其结果作为结果返回。如果所有 promise 都 rejected,Promise.any
则会抛出AggregateError
错误类型的 error 实例。 -
Promise.resolve(value)
—— 使用给定 value 创建一个 resolved 的 promise。 -
Promise.reject(error)
—— 使用给定 error 创建一个 rejected 的 promise。
# async
放置在一个函数前面,总是返回一个 promise。其他值将自动被包装在一个 resolved 的 promise 中。
# await
只在 async 中工作,关键字 await
让 JavaScript 引擎等待直到 promise 完成(settle)并返回结果。
# async/await 总结
函数前面的关键字 async
有两个作用:
- 让这个函数总是返回一个 promise。
- 允许在该函数内使用
await
。
Promise 前的关键字 await
使 JavaScript 引擎等待该 promise settle,然后:
- 如果有 error,就会抛出异常 —— 就像那里调用了
throw error
一样。 - 否则,就返回结果。
这两个关键字一起提供了一个很好的用来编写异步代码的框架,这种代码易于阅读也易于编写。
有了 async/await
之后,我们就几乎不需要使用 promise.then/catch
,但是不要忘了它们是基于 promise 的,因为有些时候(例如在最外层作用域)我们不得不使用这些方法。并且,当我们需要同时等待需要任务时, Promise.all
是很好用的。
# 打包
import
命名的导出时需要花括号,而 import
默认的导出时不需要花括号。
# Document
文档对象模型(Document Object Model),简称 DOM,将所有页面内容表示为可以修改的对象。
document
对象是页面的主要 “入口点”。我们可以使用它来更改或创建页面上的任何内容。
浏览器对象模型(Browser Object Model),简称 BOM,表示由浏览器(主机环境)提供的用于处理文档(document)之外的所有内容的其他对象。
# DOM
有 6 种主要的方法,可以在 DOM 中搜索元素节点:
方法名 | 搜索方式 | 可以在元素上调用? | 实时的? |
---|---|---|---|
querySelector |
CSS-selector | ✔ | - |
querySelectorAll |
CSS-selector | ✔ | - |
getElementById |
id |
- | - |
getElementsByName |
name |
- | ✔ |
getElementsByTagName |
tag or '*' |
✔ | ✔ |
getElementsByClassName |
class | ✔ | ✔ |
目前为止,最常用的是 querySelector
和 querySelectorAll
,但是 getElement(s)By*
可能会偶尔有用,或者可以在旧脚本中找到。
此外:
elem.matches(css)
用于检查elem
与给定的 CSS 选择器是否匹配。elem.closest(css)
用于查找与给定 CSS 选择器相匹配的最近的祖先。elem
本身也会被检查。
让我们在这里提一下另一种用来检查子级与父级之间关系的方法,因为它有时很有用:
- 如果
elemB
在elemA
内(elemA
的后代)或者elemA==elemB
,elemA.contains(elemB)
将返回 true。
# 修改文档
-
创建新节点的方法:
document.createElement(tag)
—— 用给定的标签创建一个元素节点,document.createTextNode(value)
—— 创建一个文本节点(很少使用),elem.cloneNode(deep)
—— 克隆元素,如果deep==true
则与其后代一起克隆。
-
插入和移除节点的方法:
node.append(...nodes or strings)
—— 在node
末尾插入,node.prepend(...nodes or strings)
—— 在node
开头插入,node.before(...nodes or strings)
—— 在node
之前插入,node.after(...nodes or strings)
—— 在node
之后插入,node.replaceWith(...nodes or strings)
—— 替换node
。node.remove()
—— 移除node
。
文本字符串被 “作为文本” 插入。
-
在
html
中给定一些 HTML,elem.insertAdjacentHTML(where, html)
会根据where
的值来插入它:"beforebegin"
—— 将html
插入到elem
前面,"afterbegin"
—— 将html
插入到elem
的开头,"beforeend"
—— 将html
插入到elem
的末尾,"afterend"
—— 将html
插入到elem
后面。
# 事件处理
有 3 种分配事件处理程序的方式:
- HTML 特性(attribute):
onclick="..."
。 - DOM 属性(property):
elem.onclick = function
。 - 方法(method):
elem.addEventListener(event, handler[, phase])
用于添加,removeEventListener
用于移除。
HTML 特性很少使用,因为 HTML 标签中的 JavaScript 看起来有些奇怪且陌生。而且也不能在里面写太多代码。
DOM 属性用起来还可以,但我们无法为特定事件分配多个处理程序。在许多场景中,这种限制并不严重。
最后一种方式是最灵活的,但也是写起来最长的。有少数事件只能使用这种方式。例如 transtionend
和 DOMContentLoaded
(上文中讲到了)。 addEventListener
也支持对象作为事件处理程序。在这种情况下,如果发生事件,则会调用 handleEvent
方法。
无论你如何分类处理程序 —— 它都会将获得一个事件对象作为第一个参数。该对象包含有关所发生事件的详细信息。
# 坐标
-
相对于窗口的坐标:
clientX
和clientY
。 -
相对于文档的坐标:
pageX
和pageY
。简而言之,相对于文档的坐标
pageX/Y
以文档的左上角为参照物,并且同一位置的坐标不随页面的滚动而改变。相对于窗口的坐标clientX/Y
以当前窗口的左上角为参照物,并且同一位置的坐标会随着页面的滚动而改变。
# 加载资源
async
和 defer
有一个共同点:加载这样的脚本都不会阻塞页面的渲染。因此,用户可以立即阅读并了解页面内容。
但是,它们之间也存在一些本质的区别:
顺序 | DOMContentLoaded |
|
---|---|---|
async |
加载优先顺序。脚本在文档中的顺序不重要 —— 先加载完成的先执行 | 不相关。可能在文档加载完成前加载并执行完毕。如果脚本很小或者来自于缓存,同时文档足够长,就会发生这种情况。 |
defer |
文档顺序(它们在文档中的顺序) | 在文档加载和解析完成之后(如果需要,则会等待),即在 DOMContentLoaded 之前执行。 |
在实际开发中, defer
用于需要整个 DOM 的脚本,和 / 或脚本的相对执行顺序很重要的时候。
async
用于独立脚本,例如计数器或广告,这些脚本的相对执行顺序无关紧要。
# 二进制数据
基本的二进制对象是 ArrayBuffer
—— 对固定长度的连续内存空间的引用。
如要操作 ArrayBuffer
,我们需要使用 “视图” 对象。
也就是 TypedArray
视图对象本身并不存储任何东西。它是一副 “眼镜”,透过它来解释存储在 ArrayBuffer
中的字节。
例如:
-
Uint8Array
—— 将ArrayBuffer
中的每个字节视为 0 到 255 之间的单个数字(每个字节是 8 位,因此只能容纳那么多)。这称为 “8 位无符号整数”。 -
Uint16Array
—— 将每 2 个字节视为一个 0 到 65535 之间的整数。这称为 “16 位无符号整数”。 -
Uint32Array
—— 将每 4 个字节视为一个 0 到 4294967295 之间的整数。这称为 “32 位无符号整数”。 -
Float64Array
—— 将每 8 个字节视为一个5.0x10-324
到1.8x10308
之间的浮点数。
因此,一个 16 字节 ArrayBuffer
中的二进制数据可以解释为 16 个 “小数字”,或 8 个更大的数字(每个数字 2 个字节),或 4 个更大的数字(每个数字 4 个字节),或 2 个高精度的浮点数(每个数字 8 个字节)。
# Fetch
基本语法:
let promise = fetch(url, [options]) |
-
url
—— 要访问的 URL。 -
options
—— 可选参数:method,header 等。
典型的 fetch 请求由两个 await
调用组成:
let response = await fetch(url, options); // 解析 response header | |
let result = await response.json(); // 将 body 读取为 json |
或者以 promise 形式:
fetch(url, options) | |
.then(response => response.json()) | |
.then(result => /* process result */) |
响应的属性:
response.status
—— response 的 HTTP 状态码,response.ok
—— HTTP 状态码为 200-299,则为true
。response.headers
—— 类似于 Map 的带有 HTTP header 的对象。
获取 response body 的方法:
-
response.text()
—— 读取 response,并以文本形式返回 response, -
response.json()
—— 将 response 解析为 JSON 对象形式, -
response.formData()
—— 以FormData
对象(multipart/form-data
编码,参见下一章)的形式返回 response, -
response.blob()
—— 以 Blob(具有类型的二进制数据)形式返回 response, -
response.arrayBuffer()
—— 以 ArrayBuffer(低级别的二进制数据)形式返回 response。
# xhr
可以用于跟踪上传文件的进度
# WebSocket
WebSocket 是一种在浏览器和服务器之间建立持久连接的现代方式。
- WebSocket 没有跨源限制。
- 浏览器对 WebSocket 支持很好。
- 可以发送 / 接收字符串和二进制数据。
WebSocket 的 API 很简单。
WebSocket 方法:
socket.send(data)
,socket.close([code], [reason])
。
WebSocket 事件:
open
,message
,error
,close
。
WebSocket 自身并不包含重新连接(reconnection),身份验证(authentication)和很多其他高级机制。因此,有针对于此的客户端 / 服务端的库,并且也可以手动实现这些功能。
# Cookie
Cookie 是直接存储在浏览器中的一小串数据。
document.cookie
提供了对 cookie 的访问
- 写入操作只会修改其中提到的 cookie。
- name/value 必须被编码。
- 一个 cookie 最大不能超过 4KB。每个域下最多允许有 20+ 个左右的 cookie(具体取决于浏览器)。
Cookie 选项:
path=/
,默认为当前路径,使 cookie 仅在该路径下可见。domain=site.com
,默认 cookie 仅在当前域下可见。如果显式地设置了域,可以使 cookie 在子域下也可见。expires
或max-age
设定了 cookie 过期时间。如果没有设置,则当浏览器关闭时 cookie 就会失效。secure
使 cookie 仅在 HTTPS 下有效。samesite
,如果请求来自外部网站,禁止浏览器发送 cookie。这有助于防止 XSRF 攻击。
# localStorage 和 sessionStorage
Web 存储对象 localStorage
和 sessionStorage
允许我们在浏览器中保存键 / 值对。
key
和value
都必须为字符串。- 存储大小限制为 5MB+,具体取决于浏览器。
- 它们不会过期。
- 数据绑定到源(域 / 端口 / 协议)。
localStorage |
sessionStorage |
---|---|
在同源的所有标签页和窗口之间共享数据 | 在当前浏览器标签页中可见,包括同源的 iframe |
浏览器重启后数据仍然保留 | 页面刷新后数据仍然保留(但标签页关闭后数据则不再保留) |