# 逻辑运算符小记

&&

运算 && 做了如下的事:

  • 从左到右依次计算操作数。
  • 在处理每一个操作数时,都将其转化为布尔值。如果结果是 false ,就停止计算,并返回这个操作数的初始值。
  • 如果所有的操作数都被计算过(例如都是真值),则返回最后一个操作数。

换句话说,与运算返回第一个假值,如果没有假值就返回最后一个值。

||

运算 || 做了如下的事情:

  • 从左到右依次计算操作数。
  • 处理每一个操作数时,都将其转化为布尔值。如果结果是 true ,就停止计算,返回这个操作数的初始值。
  • 如果所有的操作数都被计算过(也就是,转换结果都是 false ),则返回最后一个操作数。

返回的值是操作数的初始形式,不会做布尔转换。

换句话说,一个或运算 || 的链,将返回第一个真值,如果不存在真值,就返回该链的最后一个值。

??

a ?? b 的结果是:

  • 如果 a 是已定义的,则结果为 a
  • 如果 a 不是已定义的,则结果为 b

+ 号可以隐式转换将字符变成数字

# 查找字符串小记

方法 选择方式…… 负值参数
slice(start, end) startend (不含 end 允许
substring(start, end) startend (不含 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 —— 元素的个数。

MapSet 中迭代总是按照值插入的顺序进行的,所以我们不能说这些集合是无序的,但是我们不能对元素进行重新排序,也不能直接按其编号来获取元素。

# Rest 和 spread

  • Array.from 适用于类数组对象也适用于可迭代对象。

  • Spread 语法只适用于可迭代对象。

    当我们在代码中看到 "..." 时,它要么是 rest 参数,要么是 spread 语法。

    有一个简单的方法可以区分它们:

    • ... 出现在函数参数列表的最后,那么它就是 rest 参数,它会把参数列表中剩余的参数收集到一个数组中。
    • ... 出现在函数调用或类似的表达式中,那它就是 spread 语法,它会把一个数组展开为列表。

    使用场景:

    • Rest 参数用于创建可接受任意数量参数的函数。
    • Spread 语法用于将数组传递给通常需要含有许多参数的函数。

    我们可以使用这两种语法轻松地互相转换列表与参数数组。

# 闭包

函数嵌套函数

# Promise 和 async/await

# !Promise

用于处理异步操作,避免回调地狱。

Promise 类有 6 种静态方法:

  1. Promise.all(promises) —— 等待所有 promise 都 resolve 时,返回存放它们结果的数组。如果给定的任意一个 promise 为 reject,那么它就会变成 Promise.all 的 error,所有其他 promise 的结果都会被忽略。

  2. Promise.allSettled(promises)
    

    (ES2020 新增方法)—— 等待所有 promise 都 settle 时,并以包含以下内容的对象数组的形式返回它们的结果:

    • status : "fulfilled""rejected"
    • value (如果 fulfilled)或 reason (如果 rejected)。
  3. Promise.race(promises) —— 等待第一个 settle 的 promise,并将其 result/error 作为结果返回。

  4. Promise.any(promises) (ES2021 新增方法)—— 等待第一个 fulfilled 的 promise,并将其结果作为结果返回。如果所有 promise 都 rejected, Promise.any 则会抛出 AggregateError 错误类型的 error 实例。

  5. Promise.resolve(value) —— 使用给定 value 创建一个 resolved 的 promise。

  6. Promise.reject(error) —— 使用给定 error 创建一个 rejected 的 promise。

# async

放置在一个函数前面,总是返回一个 promise。其他值将自动被包装在一个 resolved 的 promise 中。

# await

只在 async 中工作,关键字 await 让 JavaScript 引擎等待直到 promise 完成(settle)并返回结果。

# async/await 总结

函数前面的关键字 async 有两个作用:

  1. 让这个函数总是返回一个 promise。
  2. 允许在该函数内使用 await

Promise 前的关键字 await 使 JavaScript 引擎等待该 promise settle,然后:

  1. 如果有 error,就会抛出异常 —— 就像那里调用了 throw error 一样。
  2. 否则,就返回结果。

这两个关键字一起提供了一个很好的用来编写异步代码的框架,这种代码易于阅读也易于编写。

有了 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

目前为止,最常用的是 querySelectorquerySelectorAll ,但是 getElement(s)By* 可能会偶尔有用,或者可以在旧脚本中找到。

此外:

  • elem.matches(css) 用于检查 elem 与给定的 CSS 选择器是否匹配。
  • elem.closest(css) 用于查找与给定 CSS 选择器相匹配的最近的祖先。 elem 本身也会被检查。

让我们在这里提一下另一种用来检查子级与父级之间关系的方法,因为它有时很有用:

  • 如果 elemBelemA 内( elemA 的后代)或者 elemA==elemBelemA.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 种分配事件处理程序的方式:

  1. HTML 特性(attribute): onclick="..."
  2. DOM 属性(property): elem.onclick = function
  3. 方法(method): elem.addEventListener(event, handler[, phase]) 用于添加, removeEventListener 用于移除。

HTML 特性很少使用,因为 HTML 标签中的 JavaScript 看起来有些奇怪且陌生。而且也不能在里面写太多代码。

DOM 属性用起来还可以,但我们无法为特定事件分配多个处理程序。在许多场景中,这种限制并不严重。

最后一种方式是最灵活的,但也是写起来最长的。有少数事件只能使用这种方式。例如 transtionendDOMContentLoaded (上文中讲到了)。 addEventListener 也支持对象作为事件处理程序。在这种情况下,如果发生事件,则会调用 handleEvent 方法。

无论你如何分类处理程序 —— 它都会将获得一个事件对象作为第一个参数。该对象包含有关所发生事件的详细信息。

# 坐标

  1. 相对于窗口的坐标: clientXclientY

  2. 相对于文档的坐标: pageXpageY

    简而言之,相对于文档的坐标 pageX/Y 以文档的左上角为参照物,并且同一位置的坐标不随页面的滚动而改变。相对于窗口的坐标 clientX/Y 以当前窗口的左上角为参照物,并且同一位置的坐标会随着页面的滚动而改变。

# 加载资源

asyncdefer 有一个共同点:加载这样的脚本都不会阻塞页面的渲染。因此,用户可以立即阅读并了解页面内容。

但是,它们之间也存在一些本质的区别:

顺序 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-3241.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 是直接存储在浏览器中的一小串数据。

document.cookie 提供了对 cookie 的访问

  • 写入操作只会修改其中提到的 cookie。
  • name/value 必须被编码。
  • 一个 cookie 最大不能超过 4KB。每个域下最多允许有 20+ 个左右的 cookie(具体取决于浏览器)。

Cookie 选项:

  • path=/ ,默认为当前路径,使 cookie 仅在该路径下可见。
  • domain=site.com ,默认 cookie 仅在当前域下可见。如果显式地设置了域,可以使 cookie 在子域下也可见。
  • expiresmax-age 设定了 cookie 过期时间。如果没有设置,则当浏览器关闭时 cookie 就会失效。
  • secure 使 cookie 仅在 HTTPS 下有效。
  • samesite ,如果请求来自外部网站,禁止浏览器发送 cookie。这有助于防止 XSRF 攻击。

# localStorage 和 sessionStorage

Web 存储对象 localStoragesessionStorage 允许我们在浏览器中保存键 / 值对。

  • keyvalue 都必须为字符串。
  • 存储大小限制为 5MB+,具体取决于浏览器。
  • 它们不会过期。
  • 数据绑定到源(域 / 端口 / 协议)。
localStorage sessionStorage
在同源的所有标签页和窗口之间共享数据 在当前浏览器标签页中可见,包括同源的 iframe
浏览器重启后数据仍然保留 页面刷新后数据仍然保留(但标签页关闭后数据则不再保留)