JS学习笔记
null是一个表示“无值”的原始值;undefined是一个表示“未定义”的原始值。在需要进行数值类型转换时,undefined会转换为NaN,无法参与运算;null会转换为0,可以参与运算。使用typeof操作符判断类型时,undefined会返回"undefined",null会返回"object"(历史原因)。
typeof可以测试出number、string、boolean、symbol、undefined及function,而对于null、数组及对象,typeof均返回为object。
instanceof操作符可以用来检测对象是否属于某个构造函数的实例。但是instanceof对于null 和原生类型(如 number、string)不适用。
Object.prototype.toString.call方法可以准确地区分null、数组以及其他对象类型。
- 将一个
Object类型转换为Number类型时,如果对象定义了valueOf()方法,会先调用这个方法。valueOf()方法应该返回一个基本类型的值(Number、String、Boolean、Null、Undefined、Symbol),该值将会被转换成相应的数字。如果返回的是一个对象,会尝试调用toString()方法。如果 toString()方法返回一个字符串,那么这个字符串将会被转换成相应的数字。如果toString()返回的是一个不能被解析成数字的字符串,那么转换结果将是NaN。
js中为了便于基本类型操作,提供了3个特殊的引用类型:Boolean、Number、String,它们具有基本类型特殊行为。每当读取一个基本类型的时候,js内部会自动创建一个基本包装类型的临时对象,以便调用对象上的方法。例如:'a'.indexOf('a')在调用过程中会先let str = new String('a'),然后调用indexOf,调用完毕后str = null, 销毁该临时对象。
- 在使用
JSON.parse(JSON.stringify(origin))进行深拷贝时:
- 无法实现对
函数、RegExp等特殊对象的克隆;
- 克隆后的对象的
constructor会被抛弃,所有的构造函数会指向Object,原型链关系断裂;
- 对象中如果存在
循环引用,会抛出异常。
structuredClone()方法是用于创建一个对象或数组的深拷贝的新方法。
- 如果传入一个类的实例,
structuredClone()会返回一个普通的对象,因为结构化拷贝会丢弃对象的原型链。
- 无法实现对
函数、DOM 节点的克隆。
- 变量提升与暂时性死区:
变量和函数的声明会被“提升”到当前作用域的顶部,使得其在显式声明之前可以被访问。在同一作用域中,如果存在相同名称的函数声明和变量声明,函数声明会覆盖变量声明;
- 暂时性死区表示的是当程序流程进入一个新的作用域时,在此作用域内使用
let或const声明的变量会先在作用域中被创建出来,但此时还未进行赋值,如果此时对该变量进行求值运算,变量是不能被访问的,访问会抛出异常。
扩展运算符与rest运算符:当运算符出现在函数的形参上或者在赋值等号的左侧,则表示它为rest运算符;当运算符出现在函数的实参上或者出现在赋值等号的右侧,则表示它为扩展运算符。
- 对于对象的解构形式来说,如果省略了
var/let/const声明符,就必须把整个赋值表达式用()括起来,因为如果不这样做,语句左侧的{}作为语句中的第一个元素就会被当作是一个块语句而不是一个对象。
Set中没有索引的概念,它实际是一个键值对的集合,其中每个元素都是自己的键,并且键就是值。
- 将
Map转换为 JSON 字符串通常涉及到两步:首先将Map转换成可以被JSON.stringify()处理的 JavaScript 数据类型(如对象或数组),然后使用JSON.stringify()方法将之序列化为 JSON 字符串。
- 当
Map的键名都是字符串时:可以直接使用扩展运算符将其转换为对象,{ ...map }。
- 当
Map的键名包含非字符串时:因为扩展运算符要求键是字符串,所以需要手动将Map转换成一个数组,然后再序列化。Array.from(map, (key, value) => ({[String(key)]: value}))。
for...of循环是 ES6 引入的一种新的迭代方式,它专门用于迭代具有迭代协议(即实现了 Iterator 接口)的对象,如数组、字符串、Set、Map 等。相对于 for...in 循环:
for...of直接获取可迭代对象的值,而不是键名。这对于遍历数组、字符串等非常方便。
for...in会迭代对象自身的所有可枚举属性,包括原型链上的属性,而for...of只关心对象是否实现了迭代协议,因此只遍历数据集合中的值。
- 如果在数组的原型上添加了一个属性,
for...in会遍历这个额外的属性,而for...of则不会受到这种影响。
- 在
for...of循环中可以使用break、continue和return关键字,使得控制循环更加灵活。
- 模块导入:
import、import()、require()和require.ensure()。import和import()是 ES6 中引入的新的模块化导入语法,import编译时加载导入模块,静态加载;import()运行时加载导入模块,动态加载,基于Promise的异步加载;require()是 Node.js 中使用的模块化导入方式,require()运行时加载导入模块,动态加载,同步加载模块。require.ensure()是require的一个方法,动态加载,基于Promise的异步加载。
exec与match的区别:exec是一个RegExp方法,每进行一次匹配,下次会往后进行搜索,可用RegExp.$1获取匹配的子表达式;match是一个String方法,加上全局标志'g'时会直接匹配全部的正则表达式而不会关注子表达式,不加'g'的会先匹配全部的表达式再匹配子正则表达式。
Object.is(a, b)用于判断两个值是否绝对相等,例如0与-0,使用===判断返回true,使用Object.is(0, -0)返回false;或者NaN与NaN,使用===判断返回false,使用Object.is(NaN, NaN)返回true。能使用==和===时就尽量不使用Object.is(),因为前者效率更高、更为通用。
Math.sign(num)返回数字符号;Math.trunc(num)返回数字的整数部分。
str1.localeCompare(str2):比较 str1 和 str2,返回-1、0、1。
- 如果
finally子句中包含了一个return语句,那么try或catch块中的return语句的效果会被覆盖。这是因为finally块中的return语句决定了整个函数的返回值。
JSON字符串必须使用双引号。
- 事件处理函数中
return false不仅可以阻止事件冒泡,而且可以阻止事件默认行为。event.stopPropagation()则只阻止事件冒泡。
event.eventPhase属性可用于确定事件流当前所处得阶段。如果事件处理程序在捕获阶段被调用,则eventPhase等于1;如果事件处理程序在目标阶段上被调用,则eventPhase等于2;如果事件处理程序在冒泡阶段被调用,则eventPhase等于3。不过需要注意,虽然“到达目标”是在冒泡阶段发生的,但其eventPhase仍然等于2。
- 使用
addEventListener注册事件,该函数的第三个参数可以是布尔值,也可以是对象(高版本浏览器)。对于布尔值useCapture参数来说,该参数默认值为false,useCapture决定了注册的事件是捕获事件还是冒泡事件。对于对象参数options来说,可以使用以下几个属性:
capture:布尔值,和useCapture作用一样;
once:布尔值,值为true表示该回调只会调用一次,调用后会移除监听;
passive:布尔值,表示永远不会调用preventDefault(),设置为true时,浏览器可以优化性能,避免某些不必要的计算。
DOM3新增了两个用于比较节点的方法:isSameNode()和isEqualNode()。这两个方法都接收一个节点参数,如果这个节点与参数节点相同或相等,则返回true。节点相同,意味着引用同一个对象;节点相等,意味着节点类型相同,拥有相等的属性,而且 attributes 和 childNodes 也相等。
DOM操作在 JS 代码中是代价比较高的,NodeList对象尤其需要注意,NodeList对象是“实时更新”的,这意味着每次访问它都会执行一次新的查询。
location.reload():用于重新加载当前页面。如果页面自上次请求以来没有修改过,浏览器可能会从缓存中加载页面,想强制从服务器重新加载,可以使用location.reload(true)。
arguments的内容是函数执行时传入函数的所有参数;arguments.length是传入的所有参数个数;函数名.length是第一个具有默认值之前的参数的个数。
script标签中defer和async的区别:
script:会阻碍 HTML 解析,只有下载好并执行完脚本才会继续解析 HTML;
script async:解析 HTML 过程中进行脚本的异步下载,下载成功立马执行,有可能会阻断 HTML 的解析。多个async脚本的下载是并行, 但执行不按照页面中的脚本先后顺序。哪个async脚本先下载完, 哪个async脚本就先立刻执行;
script defer:完全不会阻碍 HTML 的解析,解析 HTML 过程中进行脚本的异步下载,解析HTML完成之后,再按照顺序执行脚本。多个defer脚本的下载是并行, 但按照顺序依次执行。
window.onload()必须等到页面内包括图片的所有元素和资源加载完毕后才能执行,也就是图片的时间点2;$(document).ready()是DOM加载完毕后就执行,不必等到整个网页资源加载完毕,也就是在图片的时间点1。且$(document).ready()可以同时编写多个,并且都能执行,window.onload()只会执行最后一个。

- 解析URL:
new URL(url);解析和操作 URL 的查询参数:new URLSearchParams(paramsString)。
Math.min不传参数,返回Infinity。Math.max不传参数,返回-Infinity。
- 在 JavaScript 中,
异步任务是由宿主环境(host environment)来执行的。这里的“宿主环境”通常指的是Web 浏览器、Node.js 运行时环境或者其他支持 JavaScript 的平台。宿主环境负责管理异步操作,例如定时器、网络请求、文件系统操作等,并在适当的时机调度这些异步任务。
XMLHttpRequest使用步骤:
- 创建
XMLHttpRequest对象;
- 使用
open()创建 xml 请求;
- 使用
send()发送请求;
onreadystatechange():监听readyState属性的变化。
- 0:被创建未调用
open();
- 1:已调用
open();
- 2:已调用
send();
- 3:正在接受请求结果;
- 4:请求接受完毕。
XMLHttpRequest中progress事件会在浏览器接收数据的过程中周期性调用。onprogress事件处理程序会接收一个event对象,通过它的target属性同样可以获取到XMLHttpRequest对象的实例,而且在event对象中增加了3个有用的属性,分别是lengthComputable、loaded和total。
lengthComputable是一个布尔值,表示进度信息是否可用,false时表示请求的长度无法计算,通常发生在响应体大小未知,此时total无法提供有效的总字节数;
loaded表示已经接收到的字节数,它的值是根据Content-Length响应头部确定的预期字节数;
total相应响应的实际字节数。通过loaded和total属性值就可以计算出接收响应的数据百分比,从而实现进度条的操作。axios中的onUploadProgress和onDownloadProgress事件分别在上传和下载数据时触发,可用于跟踪进度。
axios发送请求时,添加cancelToken配置项可以用于取消请求。[源码分析]

axios.interceptors:axios拦截器。
axios.interceptors.request.use()方法:添加请求拦截器。
axios.interceptors.response.use()方法:添加响应拦截器。
axios.interceptors.request.eject(拦截器变量)方法:移除请求拦截器。
axios.interceptors.response.eject(拦截器变量)方法:移除响应拦截器。
location对象是 JavaScript 中的一个内置对象,用于管理和操作当前页面的 URL。通过使用location对象的属性,可以方便地获取和设置 URL 的各个部分。
localStorage和sessionStorage设置值时,如果传入的是布尔值,取出时为字符串。
- 处理
Promise失败的方法有两种,一种是使用then()函数的第二个参数,另一种是使用catch()函数,不同的是then()函数的第二个参数不能捕获第一个参数中抛出的异常,而catch()。函数可以捕获。
try...catch语句不能直接捕获Promise构造函数中的错误。如果你想捕获Promise构造函数中的错误,你应该使用.catch()方法。如果你需要使用try...catch语句来捕获错误,你需要确保错误是在.catch()方法内部再次抛出的。
具名函数表达式NFE的特性:(function b() { b = 20 console.log(b) })()。
- 作为函数名的标识符只能在函数体内部访问,在函数体外部访问不到;
- 绑定为函数名的标识符不能再绑定为其他值,即函数体内对函数名的重新赋值无效。
JS 闭包:inner函数是一个闭包,它保留了对外部函数outer作用域的引用。使用闭包的同时比较容易形成循环引用,如果闭包的作用域链中保存着一些 DOM 节点,就有可能造成内存泄漏,内存泄漏是循环引用造成的而非闭包。在现代浏览器中垃圾回收机制能识别并处理大多数循环引用。

- Window 上的原生事件:
error:在资源加载失败或无法使用时触发,如script执行时报错;
unhandledrejection:当Promise被reject且没有reject处理器的时候,会触发unhandledrejection事件;可能发生在 window 下或 Worker 中。
Worker接口是Web Workers API的一部分,指的是一种可由脚本创建的后台任务,任务执行中可以向其创建者收发信息。要创建一个Worker,只须调用Worker(URL)构造函数,函数参数URL为指定的脚本。
dangerouslySetInnerHTML是一个 JavaScript 的属性,它允许你直接将HTML插入到一个元素的内部。这个属性通常在需要动态地改变一个元素的HTML内容时使用。然而,它并不是一个标准的 JavaScript 方法,而是在许多 JavaScript 框架(如React)中使用的非标准方法。类似于Vue中的v-html,都存在被XSS攻击的风险。
MutationObserver用来监视DOM变动,比如节点的增减、属性的变动、文本内容的变动都会触发MutationObserver事件。它与事件有一个本质不同:事件是同步触发,也就是说,DOM的变动立刻会触发相应的事件;MutationObserver则是异步触发,DOM的变动并不会马上触发,而是要等到当前所有DOM操作都结束才触发,属于微任务。
visibilitychange事件:当其选项卡的内容变得可见或被隐藏时,会在document上触发visibilitychange事件。addEventListener("visibilitychange", (event) => {})。
IntersectionObserver提供了一种异步观察目标元素与其祖先元素或顶级文档视口交叉状态的方法,会在观察元素与视口的交叉状态发生变化时被调用。可通过它来实现监听元素是否进入或离开可视区域。

- JS 中对象的属性分为
数据属性和访问器属性,其特性是为了实现 JS 引擎内部使用。
- 数据属性:
[[Configurable]]:表示能否通过delete删除属性;
[[Enumerable]]:表示属性能否可以被枚举;
[[Writable]]:表示能否修改属性的值;
[[Value]]:表示这个属性的属性值,默认为undefined。
- 访问器属性:
[[Get]]:在读取属性时调用的函数;
[[Set]]:在写入属性时调用的函数;必须使用Object.defineProperty(属性所属对象,属性名,描述符对象)定义。
- 通过
Object.defineProperty()等定义的属性,其enumerable值默认为false,即不可枚举属性。而通过直接的赋值和属性初始化定义的属性,其enumerable值默认为true,即可枚举属性。但值得注意的是,对于基本包装类型的原型对象的属性,如Object、Array、Number等,它们是不可枚举的。
- 属性遍历:
for...in用于遍历自身和继承的可枚举属性(不包含Symbol属性);
Object.keys()函数返回一个数组,包含对象自身所有可枚举属性,不包含继承属性和Symbol属性;
Object.entries()返回一个给定对象自身可枚举属性的键值对数组,其中每个元素都是一个数组,第一个元素是对象的键,第二个元素是对象的值;
Object.getOwnPropertyNames()函数返回一个数组,包含对象自身所有可枚举属性和不可枚举属性,不包含继承属性和Symbol属性;
Object.getOwnPropertySymbols()函数返回一个数组,包含对象自身所有Symbol属性,不包含其他属性;
Reflect.ownKeys()函数返回一个数组,包含可枚举属性、不可枚举属性以及Symbol属性,不包含继承属性。
Object.assign()函数无法复制对象的不可枚举属性和继承属性,但可以复制可枚举的Symbol属性。
- 移动端 H5 监听软键盘打开和收起:通过
navigator.userAgent区分安卓和IOS。安卓通过监听resize事件;IOS 通过监听scroll事件中scrollTop的改变大小(页面不存在其他滚动)。页面销毁之前移除事件监听。