看完这篇文章,你将了解:

  • 正则对象的 exec() 方法
  • setTimeout 第三参数的含义
  • document.write 方法会将参数转为字符串
  • 事件捕获与冒泡(简要)
  • for 循环和 let 循环的区别
  • 几点细节
  • JS 动态规划

RegExp.exec()

这是一个正则表达式对象的方法,类似于 String.match(),返回目标字符串的匹配结果,于 match 不同的是:

  • match 是 String 对象的方法,而 exec 是正则对象的方法
  • 正则表达式若有 g, match 是一次返回所有结果,而 exec 是一个一个返回

比如:

此外,根据 MDN,exec 的返回值格式如下:

setTimeout 的第三参数

setTimeout 是有第三参数的。

事实上,第一参数代表回调,第二参数代表时间,而第三参数及以后的参数都会传给回调函数作为实参

比如:

function a(x, y) {
    console.log(x, y) // 2 3
}

setTimeout(a, 1000, 2, 3)

document.write 方法会调用 toString()

先看一段代码

var str1=new RegExp("e")
console.log(str1.exec("hello"))
// ["e", input: "hello", index: 1, groups: undefined]
document.write(str1.exec("hello"))
// e

这里我一开始以为文档会打印 [Object Object],结果是 e

为什么是这样呢?因为

  • document.write 的时候会将里面的参数先转成字符串
  • 执行结果是数组,而数组调用 toString 方法等价于 .join(',')
  • 数组的 join 方法只收集键值为序号的元素

可以在控制台验证

var str1=new RegExp("e")
console.log(str1.exec("hello").toString())
// e

var arr = ["e", "a"]
console.log(arr.toString())
// e,a

var arr = ["e"]
arr.input = "hello"
arr.index = 1
arr.groups = undefined
// ["e", input: "hello", index: 1, groups: undefined]
console.log(arr.toString())
// e

事件捕获与冒泡

浏览器对事件的处理顺序是 先捕获,后冒泡,一个事件是捕获还是冒泡,规定在 addEventListener 的第三参数

element.addEventListener(event, function, useCapture)

捕获为 true, 冒泡为 false, 默认为 false

也就是说,我们一般添加的事件视为冒泡,默认从内到外触发

window.history

  • forward: 加载 URL 历史列表的当前 URL 的下一个地址( === go(1))
  • back: 加载 URL 历史列表的当前 URL 的上一个地址( === go(-1))
  • go: 传入一个数字参数表示加载 URL 历史列表相对于当前 URL 的第几个地址
  • length:一个属性,指示当前 URL 历史列表的长度

let 循环和 var 循环的区别

先看两段代码

for(var i = 0; i < 5; i++){
    requestAnimationFrame(() => console.log(i));
}
// 5 5 5 5 5
for(let i = 0; i < 5; i++){
    requestAnimationFrame(() => console.log(i));
}
// 0 1 2 3 4

这两段代码涉及到两个知识点。一是 var 和 let 的区别,二是异步

先来看循环变量用 var 定义和 let 定义的区别

for(var i=0; i<10; i++) {}
i  // 10
// for 语句里 var 定义的变量会成为全局变量
for(let j=0; j<10; j++) {}
j // Error
// for 语句中 let 定义的变量不会在循环外生效

据我猜测,var 版本的循环,i 被定义为全局变量,而且循环结束后等于最后一次自增后的值,等定时器跑完后再回来看,读到的 i 是全局的 i,也就是 5。

而 let 版本的循环,读取的 i 总是限定于循环作用域的,虽然都是 i,但实际是 5 个不同的变量

细节

  • 1/0 的结果是 Infinity, 不是 NaN
  • xx in 数组是一个判断语句,判断 xx 是否是数组中的键(序号)。

    • 其实更准确点 in 运算符是对象所属,用来检查左边是否为右边的键
  • 1 && 2 的结果是 2

JS 动态规划

今天在力扣上完成了三道动态规划题。

一般来说,动态规划就两步:

  • 寻找状态转移方程
  • 利用状态转移方程编程计算所有状态

寻找状态转移方程就是找规律,找规律就是假设。

想知道 P(i, j) 等于多少,从头算起很难。那就先假设我知道 P(i - 1, j), P(i, j - 1), P(i - 1, j - 1) 等较小问题的答案时,会怎么计算 P(i, j)。假设未知为已知,用当成已知的未知推导更大的未知

编程计算所有状态,用数组。遍历数组求各个结点下的状态值即可。

遍历的时候注意确保顺序正确,确保每个当前遍历结点所需的已知结点已提前经过

更多参考

最长回文串题解 (这位老哥教会我用跨度遍历数组,见下图)

动态规划三种入手思路 (还是这位老哥,教会我计算最大子序列和)

用跨度遍历数组的顺序,也就是从子串长度遍历

金句

  • setTimeout 的第三参数及以后的参数会被当成回调函数的实参传入
  • document.write 方法执行时会先将参数转为字符串
  • 点击事件默认从内到外触发
  • for(var i=0; i<10; i++) {} 循环执行后 i 会成为全局变量且等于 10
  • 动态规划一般分两步:寻找状态转移方程, 计算所有状态