Vue 模板语法
1 插值语法
功能:用于解析标签体内容
写法:,xxx是js表达式,直接读取js中的所有data属性
2 指令语法
功能: 用于解析标签(包括标签属性,标签内容等)
举例:v-bind:href=”xxx”,简写为:href=”xxx”,xxx同样可以写js表达式,且可以直接读取到data里的所有属性
写法:v-xxx
1 | function HoF0(fn){ |
1 | function once(fn){ |
1 | function throttle(fn,time=500){ |
1 | function debouce(fn,dur){ |
1 | function iterative(fn){ |
过程抽象
HOF
装饰器
命令式/声明式
1 | function*draw(cards){ |
监视属性watch:
1 当被监视的属性变化时(也可以监视计算属性),回调函数自动调用,进行相关操作
2 监视的属性必须存在才能好进行监视
3 监视的两种写法:
(1)在Vue.createApp时传入watch配置
(2)通过vm.$watch监视
4 深度监视
(1)Vue中的watch默认不监视对象内部值的改变(一层)
(2)配置deep为true,可以检测对象内部值改变(多层)
监视多级结构中某个属性的变化:’number.a’
监视多级结构中所有属性第1变化,用deep:true
PS:(1)Vue自身可以检测对象内部值的改变但是Vue提供的watch默认不可以
(2)使用watch时根据数据的具体结构,决定是否采用深度监视
vm.$watch(expOrFn,callback,[options])
返回值:unwatch函数
用处:观察Vue实例上的一个表达式或者一个函数计算结果的变化,回调函数得到的参数为新值和旧值。表达式只接受简单的键路径,对于复杂表达式,用函数取代
1 | //HTML |
在带有immediate选项时不能在第一次回调时取消侦听给定的property
1 | const unwatch=vm.$watch( |
要在回调函数里调用一个取消侦听的函数应该先检查其函数的可用性
1 | let unwatch=null; |
watch监视的是reactive定义的响应式数据则无法正确获得oldValue,watch监视的是reactive定义的响应式数据则强制开启深度监视,只能获取newValue,无法获取oldValue
监视reactive定义的某个对象中的属性(也是对象),deep有效,仍然无法获取oldValue
1 | setup(){ |
1 | function addLoadEvent(func){ |
arr.reduce(callback(accumulator,currentValue,index,array),InitValue)
callback:执行数组中的每个值的函数,包括4个参数:
accumulator
currentValue
数组中正在处理的元素。
index
可选
数组中正在处理的当前元素的索引。 如果提供了initialValue
,则起始索引号为0,否则从索引1起始。
array
可选
调用reduce()
的数组
initialValue
可选
作为第一次调用 callback
函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
返回累计处理后的结果
1 | var sum=[0,1,2,3].reduce(function(accumulator,currentValue){ |
1 | var flattened=[[0,1],[2,3],[4,5],[6,7]].reduce(function(a,b){return a.concat(b);},[]) |
1 | var names=['Alice','Bob','Ann','Alice','Bob']; |
1 | var people=[ |
1 | var friends=[{ |
1 | let myArray=[6,6,6,2,3,3,4,4,4,5]; |
Promise可以通过 new 操作符来实例化。创建新期约时需要传入
执行器(executor)函数作为参数
1 | let p = new Promise(() => {}); |
在把一个期约实例传给 console.log()时,控制台输出(可能因浏览器不同而略有差异)表明该实例处于待定(pending)状态。如前所述,期约是一个有状态的对象,可能处于如下 3 种状态之一:
待定(pending)是期约的最初始状态。在待定状态下,期约可以落定(settled)为代表成功的兑现(fulfilled)状态,或者代表失败的拒绝(rejected)状态。无论落定为哪种状态都是不可逆的。只要从待定转换为兑现或拒绝,期约的状态就不再改变。而有时候也称为“解决”,resolved)
兑现(fulfilled)每个期约只要状态切换为兑现,就会有一个私有的内部值(value)
拒绝(rejected)如果期约被拒绝,程序就会期待期约状态改变时可以拿到拒绝的理由
期约主要有两大用途。首先是抽象地表示一个异步操作。期约的状态代表期约是否完成。某些情况下,这个状态机就是期约可以提供的最有用的信息。
期约的状态是私有的,所以只能在内部进行操作。内部操作在期约的执行器函数中完成。执行器函数主要有两项职责:初始化期约的异步行为和控制状态的最终转换。其中,控制期约状态的转换是
通过调用它的两个函数参数实现的。这两个函数参数通常都命名为 resolve()和 reject()。调用resolve()会把状态切换为兑现,调用 reject()会把状态切换为拒绝。另外,调用 reject()也会抛出错误
1 | let p1=new Promise((resolve,reject)=>resolve()); |
在前面的例子中,并没有什么异步操作,因为在初始化期约时,执行器函数已经改变了每个期约的状态,执行器函数是同步执行的。这是因为执行器函数是期约的初始化程序。
添加 setTimeout 可以推迟切换状态:
1 | let p = new Promise((resolve, reject) => setTimeout(resolve, 1000)); |
无论 resolve()和 reject()中的哪个被调用,状态转换都不可撤销了。于是继续修改状态会静默失败,如下所示:
1 | let p = new Promise((resolve, reject) => { |
下面两个期约实例实际上是一样的:
let p1 = new Promise((resolve, reject) => resolve());
let p2 = Promise.resolve();
这个解决的期约的值对应着传给 Promise.resolve()的第一个参数。使用这个静态方法,实际上可以把任何值都转换为一个期约
1 | setTimeout(console.log,0,Promise.resolve()); |
对这个静态方法而言,如果传入的参数本身是一个期约,那它的行为就类似于一个空包装。因此,Promise.resolve()可以说是一个幂等方法
1 | let p = Promise.resolve(7); |
注意,这个静态方法能够包装任何非期约值,包括错误对象,并将其转换为解决的期约。因此,也可能导致不符合预期的行为:
1 | let p = Promise.resolve(new Error('foo')); |
与 Promise.resolve()类似,Promise.reject()会实例化一个拒绝的期约并抛出一个异步错误(这个错误不能通过 try/catch 捕获,而只能通过拒绝处理程序捕获)。下面的两个期约实例实际上是一样的:
let p1 = new Promise((resolve, reject) => reject());
let p2 = Promise.reject();
这个拒绝的期约的理由就是传给 Promise.reject()的第一个参数。这个参数也会传给后续的拒绝处理程序:
1 | let p = Promise.reject(3); |
关键在于,Promise.reject()并没有照搬 Promise.resolve()的幂等逻辑。如果给它传一个期约对象,则这个期约会成为它返回的拒绝期约的理由:
1 | setTimeout(console.log, 0, Promise.reject(Promise.resolve())); |
1 | try { |
第一个 try/catch 抛出并捕获了错误,第二个 try/catch 抛出错误却没有捕获到,这里的同步代码之所以没有捕获期约抛出的错误,是因为它没有通过异步模式捕获错误。从这里就可以看出期约真正的异步特性:它们是同步对象(在同步执行模式中使用),但也是异步执行模式
的媒介。
在前面的例子中,拒绝期约的错误并没有抛到执行同步代码的线程里,而是通过浏览器异步消息队列来处理的。因此,try/catch 块并不能捕获该错误。代码一旦开始以异步模式执行,则唯一与之交互
的方式就是使用异步结构——更具体地说,就是期约的方法。
Promise.prototype.then()是为期约实例添加处理程序的主要方法。这个 then()方法接收最多 两个参数:onResolved 处理程序和 onRejected 处理程序。这两个参数都是可选的,如果提供的话,则会在期约分别进入“兑现”和“拒绝”状态时执行。传给 then()的任何非函数类型的参数都会被静 默忽略。如果想只提供 onRejected 参数,那就要在 onResolved 参数的位置上传入 undefined。这 样有助于避免在内存中创建多余的对象
1 | function onResolved(id) { |
1 |
|
Promise.prototype.then()方法返回一个新的期约实例:
1 | let p1 = new Promise(() => {}); |
这个新期约实例基于 onResovled 处理程序的返回值构建。换句话说,该处理程序的返回值会通过 Promise.resolve()包装来生成新期约。如果没有提供这个处理程序,则 Promise.resolve()就会 包装上一个期约解决之后的值。如果没有显式的返回语句,则 Promise.resolve()会包装默认的返回 值 undefined。
1 | let p1 = Promise.resolve('foo'); |
onRejected 处理程序也与之类似:onRejected 处理程序返回的值也会被 Promise.resolve() 包装。乍一看这可能有点违反直觉,但是想一想,onRejected 处理程序的任务不就是捕获异步错误吗? 因此,拒绝处理程序在捕获错误后不抛出异常是符合期约的行为,应该返回一个解决期约
1 | let p1 = Promise.reject('foo'); |
Promise.prototype.catch()方法用于给期约添加拒绝处理程序。这个方法只接收一个参数: onRejected 处理程序。事实上,这个方法就是一个语法糖,调用它就相当于调用 Promise.prototype. then(null, onRejected)
1 | let p = Promise.reject(); |
Promise.prototype.catch()返回一个新的期约实例:
1 | let p1 = new Promise(() => {}); |
Promise.prototype.finally()方法用于给期约添加 onFinally 处理程序,这个处理程序在期 约转换为解决或拒绝状态时都会执行。这个方法可以避免 onResolved 和 onRejected 处理程序中出 现冗余代码。但 onFinally 处理程序没有办法知道期约的状态是解决还是拒绝,所以这个方法主要用清理代码
1 | let p1 = Promise.resolve(); |
Promise.prototype.finally()方法返回一个新的期约实例:
1 | let p1 = new Promise(() => {}); |
这个新期约实例不同于 then()或 catch()方式返回的实例。因为 onFinally 被设计为一个状态 无关的方法,所以在大多数情况下它将表现为父期约的传递
1 | let p1 = Promise.resolve('foo'); |
当期约进入落定状态时,与该状态相关的处理程序仅仅会被排期,而非立即执行。跟在添加这个处 理程序的代码之后的同步代码一定会在处理程序之前先执行。即使期约一开始就是与附加处理程序关联 的状态,执行顺序也是这样的。这个特性由 JavaScript 运行时保证,被称为“非重入”(non-reentrancy) 特性。
1 | let synchronousResolve; |
在这个例子中,即使期约状态变化发生在添加处理程序之后,处理程序也会等到运行的消息队列让 它出列时才会执行。
到了落定状态后,期约会提供其解决值(如果兑现)或其拒绝理由(如果拒绝)给相关状态的处理 程序。拿到返回值后,就可以进一步对这个值进行操作。比如,第一次网络请求返回的 JSON 是发送第 二次请求必需的数据,那么第一次请求返回的值就应该传给 onResolved 处理程序继续处理。当然,失 败的网络请求也应该把 HTTP 状态码传给 onRejected 处理程序。
在执行函数中,解决的值和拒绝的理由是分别作为 resolve()和 reject()的第一个参数往后传 的。然后,这些值又会传给它们各自的处理程序,作为 onResolved 或 onRejected 处理程序的唯一 参数。
1 | let p1 = new Promise((resolve, reject) => resolve('foo')); |
then()和 catch()的 onRejected 处理程序在语义上相当于 try/catch。出发点都是捕获错误之 后将其隔离,同时不影响正常逻辑执行。为此,onRejected 处理程序的任务应该是在捕获异步错误之 后返回一个解决的期约。下面的例子中对比了同步错误处理与异步错误处理:
1 | console.log('begin synchronous execution'); |
每个后续的处理程序都会等待前一个期约解决,然后实例化一个新期约并返回它。这种结构可以简 洁地将异步任务串行化,解决之前依赖回调的难题
1 | function delayedResolve(str) { |
Promise.all
方法用于将多个 Promise 实例,这个静态方法接收一个可迭代对象,将参数转为 Promise 实例,再包装成一个新的 Promise 实例。
1 | //永远待定 |
如果所有期约都成功解决,则合成期约的解决值就是所有包含期约解决值的数组,按照迭代器顺序:
1 | let p = Promise.all([ |
Promise.race()静态方法返回一个包装期约,是一组集合中最先解决或拒绝的期约的镜像。这个 方法接收一个可迭代对象,返回一个新期约
1 | // 解决先发生,超时后的拒绝被忽略 |
1 | function addTwo(x){return x+1;} |
Promise 对象的回调链,不管以then
方法或catch
方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为 Promise 内部的错误不会冒泡到全局)。因此,我们可以提供一个done
方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。
1 | asyncFunc() |
finally()f方法用于指定不管Promise对象最后状态如何,都会执行的操作,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。
1 | server.listen(0) |
将图片的加载写成一个Promise,一旦加载完成,Promise的状态发生变化
1 | //加载图片 |
把它当成一种数据格式,而不是编程语言。JSON 不属于 JavaScript,
它们只是拥有相同的语法而已。JSON 也不是只能在 JavaScript 中使用,它是一种通用数据格式。很多语言都有解析和序列化 JSON 的内置能力。
JavaScript 字符串与 JSON 字符串的主要区别是,JSON 字符串必须使用双引号(单引号会导致语法错误)。布尔值和 null 本身也是有效的 JSON 值
与 JavaScript 对象字面量相比,JSON 主要有两处不同。首先,没有变量声明(JSON 中没有变量)。其次,最后没有分号(不需要,因为不是 JavaScript 语句)。同样,用引号将属性名包围起来才是有效的JSON。属性的值可以是简单值或复杂数据类型值,后者可以在对象中再嵌入对象.
1 | { |
数组在 JSON 中使用 JavaScript 的数组字面量形式表示.
JavaScript
1 | let values = [25, "hi", true]; |
Json
1 | [25,"hi",true] |
stringfy():在序列化 JavaScript 对象时,所有函数和原型成员都会有意地在结果中省略。此外,值为 undefined的任何属性也会被跳过。最终得到的就是所有实例属性均为有效 JSON 数据类型的表
1 | let book = { |
结果:
{“title”:”Professional JavaScript”,”authors”:[“Nicholas C. Zakas”,”Matt Frisbie”],
“edition”:4,”year”:2017}
还可以接收两个参数。这两个参数可以用于指定其他序列化 JavaScript 对象的方式。第一个参数是过滤器,可以是数组或函数;第二个参数是用于缩进结果 JSON 字符串的选项。
1 | let book = { |
1 |
|
JSON.stringify()方法的第三个参数控制缩进和空格。在这个参数是数值时,表示每一级缩进的空格数。
1 | let book = { |
toJSON():在要序列化的对象中添加 toJSON()方法,序列化时会基于这个方法返回适当的 JSON 表示:
1 | let book = { |
toJSON()方法可以与过滤函数一起使用,在把对象传给 JSON.stringify()时会执行如下步骤。
(1) 如果可以获取实际的值,则调用 toJSON()方法获取实际的值,否则使用默认的序列化。
(2) 否则,使用默认序列化时,如果提供了第二个参数,则应用过滤。
(3) 第(2)步返回的每个值都会相应地进行序列化。
(4) 如果提供了第三个参数,则相应地进行缩进。
UTF-8 标准规定,0xD800
到0xDFFF
之间的码点,不能单独使用,必须配对使用。比如,\uD834\uDF06
是两个码点,但是必须放在一起配对使用,代表字符𝌆
。这是为了表示码点大于0xFFFF
的字符的一种变通方法。单独使用\uD834
和\uDFO6
这两个码点是不合法的,或者颠倒顺序也不行,因为\uDF06\uD834
并没有对应的字符。
JSON.stringify()
的问题在于,它可能返回0xD800
到0xDFFF
之间的单个码点。
1 | JSON.stringify('\u{D834}') // "\u{D834}" |
为了确保返回的是合法的 UTF-8 字符,ES2019 改变了JSON.stringify()
的行为。如果遇到0xD800
到0xDFFF
之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给应用自己决定下一步的处理。
1 | JSON.stringify('\u{D834}') // ""\\uD834""JSON.stringify('\uDF06\uD834') // ""\\udf06\\ud834"" |
JSON.parse():接收一个参数,这个函数称为还原函数,还原函数接收两个参数,属性名key和属性名value,如果还原函数返回undefined,则结果中删除相应键,如果返回了其他任何值,则该值就会成为相应键的值插入到结果中。
1 | let book = { |
JavaScript 字符串由 16 位码元(code unit)组成。对多数字符来说,每 16 位码元对应一个字符。换
句话说,字符串的 length 属性表示字符串包含多少 16 位码元
JavaScript 字符串使用了两种 Unicode 编码混合的策略:UCS-2 和 UTF-16。对于可以采用 16 位编码
的字符(U+0000~U+FFFF),这两种编码实际上是一样的。
1 | let message = "abcde"; |
这个对应关系在扩展到 Unicode 增补字符平面时就不成立了。即 16 位只能唯一表示,65 536 个字符。这对于大多数语言字符集是足够了,在 Unicode 中称为基本多语言平面(BMP)。为了表示更多的字符,Unicode 采用了一个策略,即每个字符使用另外 16 位去选择一个增补平面。这种每个字符使用两个 16 位码元的策略称为代理对。
为正确解析既包含单码元字符又包含代理对字符的字符串,可以使用 codePointAt()来代替charCodeAt()。跟使用 charCodeAt()时类似,codePointAt()接收 16 位码元的索引并返回该索引位置上的码点(code point)。码点是 Unicode 中一个字符的完整标识。比如,”c”的码点是 0x0063,而”☺”的码点是 0x1F60A。码点可能是 16 位,也可能是 32 位,而 codePointAt()方法可以从指定码元位置识别完整的码点。
1 | let message = "ab☺de"; |
1 | // Unicode "Latin small letter A"的编码是 U+0061 |
fromCodePoint():这个方法接收任意数量的码点,返回对应字符拼接起来的字符串
1 | console.log(String.fromCharCode(97, 98, 55357, 56842, 100, 101)); // ab☺de |
Unicode提供了 4种规范化形式,可以将类似上面的字符规范化为一致的格式,无论底层字符的代码是什么。这 4种规范化形式是:NFD(Normalization Form D)、NFC(Normalization Form C)、
NFKD(Normalization Form KD)和 NFKC(Normalization Form KC)。可以使用 normalize()方法对字符串应用上述规范化形式,使用时需要传入表示哪种形式的字串:”NFD”、”NFC”、”NFKD”或”NFKC”。
通过比较字符串与其调用 normalize()的返回值,就可以知道该字符串是否已经规范化了
1 | let a1 = String.fromCharCode(0x00C5), |
未规范化:
1 | let a1 = String.fromCharCode(0x00C5), |
选择同一种规范化形式可以让比较操作符返回正确的结果:
1 | let a1 = String.fromCharCode(0x00C5), |
DOMTokenList接口的toggle()方法从列表中删除一个给定的标记并返回false,如果标记不存在,则添加并且函数返回true
tokenList.toggle(token,force);
token:标记列表中你想探查并切换的DOMSring
force(可选):Boolean值,设置后会将方法变成单向操作,如果设置为false,则会删除标记列表中匹配的给定标记,且不会再添加,如设置为true,则将在标记中添加给定标记,且不会再度删除
为布尔值
HTML
1 | <span class="a b">classList is 'a b'</span> |
JavaScript
1 | var span = document.querySelector("span"); |