术语索引
https://developer.mozilla.org/en-US/docs/Glossary
- 可枚举性 enumerable: 可枚举是集合的属性,隐式要求每个元素都是唯一的,
for ... in
- 迭代 iterate: 可迭代 迭代器是借鉴 C++等语言的概念,迭代器的原理就像指针一样,它指向数据集合中的某个元素,你可以获取它指向的元素,也可以移动它以获取其它元素。迭代器类似于数组中下标的拓展,各种数据结构,如链表(List)、集合(Set)、映射(Map)都有与之对应的迭代器。迭代器是专门为了遍历这一操作设计的。迭代是按照某种顺序逐个访问列表中的每一项,是将输出做为输入,再次进行相同动作处理。
for ...of
- 递归 recursion: 自己调用自己,自己包含自己。从计算机角度讲,递归是迭代的特例
- 遍历:按规则访问非线性结构中的每一项。强调非线性结构(树, 图). 而迭代一般适用于线性结构(数组, 队列).
遍历、枚举、迭代
递归是重复调用函数自身实现循环。迭代是函数内某段代码实现循环,而迭代与普通循环的区别是:循环代码中参与运算的变量同时是保存结果的变量,当前保存的结果作为下一次循环计算的初始值。
数组对象其实是一个特殊对象,他拥有普通对象的全部特性,所以不止负值索引,小数索引,连字符串索引都是可以的,只是用这些索引向数组添加属性时,数组的 length 不会增加罢了,而只有当索引是正整数或其对应数字串时,length 才会增加
operator 操作符 细节
Object 的几个遍历方法 对比
for..of
遍历enumerable
,string-keyed
属性for..in
遍历enumerable
,string-keyed
属性(包括inherit-property
) for in 的顺序问题,准确的说是基于不同浏览器有不同的实现方式。对于大多数是这样的:先遍历出整数属性String(Math.trunc(Number(prop)) === prop
(integer properties,按照升序),然后其他属性按照创建时候的顺序遍历出来...obj
拷贝own
,enumerable
属性(包括Symbol-keyed
)Object.assign(target,...sources)
同…拷贝own
,enumerable
属性(包括Symbol-keyed
) 内部对 target 执行 set,对 source 执行 get。正因为 source-obj 会 invoke property 的 getter所以 prototype 不适合做 target,请改用Object.getOwnPropertyDescriptor()
配合Object.defineProperty()
Object.keys()
返回own
,enumerable
,string-keyed
属性Object.getOwnPropertyNames()
返回own
,string-keyed
属性(包括non-enumerable
) non-enumerable 属性的创建只有一种方式:Object.defineProperty(obj1, 'non-enumerable-key1', {value: 'xx', enumerable: false})
Object.getOwnPropertySymbols()
同上,但是只是Symbol-keyed
属性
Object.is(v1,v2) 判断的还是两个 v 的指针
this 详解
- 给当前元素的某一个事件行为绑定函数,方法执行时的 this 就是绑定的当前元素本身
- 自执行函数中的 this 一般都是 window,严格模式下是 undefined
- 回调函数中的 this 一般都是 window,严格模式下是 undefined
- 箭头函数在创建执行上下文的时候,在初始化上下文环境时,不会创建 this 属性,所以在箭头函数中如果使用到 this 会根据作用域链向上级上下文寻找,箭头函数中所使用的 this,都是来自上级函数作用域链,即上级函数被谁调用这个 this 就是谁
- 构造函数时的 this 是指实例
箭头函数
- 箭头函数有作用域(词法作用域),词法作用域简单来讲就是,一切变量(包括 this)都根据作用域链来查找。.箭头函数中的 this 因为绑定了词法作用域,所以始终指向自身外的第一个 this(由于自身没有声明 this,所以会去作用域链上找 this),也就是始终等于调用它的函数的 this(以为这个 this 离它最近)
- 不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 Rest 参数代替。
- 不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误。
- 不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。
- 准确的说 this、super、arguments、new.target 都没有自己的,全都来自父作用域,父作用域能找到的 就不会 undefined
骚操作
实现 sleep 阻塞目的:
- while()限定时间条件达到
const expire = Date.now() + 1000 while(Date.now()<expire) // after that, do something
- promise 异步
const wait = (timeout) => { return new Promise((resolve) => setTimeout(resolve, timeout)); }; wait(2000).then(() => setRefreshing(false)); /** new promise((reslove, reject) = > { setTimeout (()=>{ reslove() }, 2000) }) */
冷知识
赋值详详详详解
神奇的 JS 连续赋值语句
JS 赋值语句 永远返回值,声明语句 永远返回 undefined
JavaScript 中赋值语句的返回值就是等号右边的值,例子中 obj.getThis 的值是一个匿名函数,非严格模式下 this 会指向 windowvar obj = { name: "My Object", getThis: function () { console.log(this); }, }; (obj.getThis = obj.getThis)(); // window // if (x = 10) // 会判断为10,隐式转换为true
解构赋值
- 解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象,字符串被转为类数组对象,数值和布尔型则转为包装对象。由于 undefined 和 null 无法转为对象,所以对它们进行解构赋值,都会报错。
const [a, b, c, d, e] = "hello";
a; // "h"
b; // "e"
c; // "l"
d; // "l"
e; // "o"
//字符串转为的对象有length属性
let { length: len } = "hello";
len; // 5
let { toString: s } = 123;
s === Number.prototype.toString; // true
let { toString: s } = true;
s === Boolean.prototype.toString; // true
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
- 数组是特殊的对象,所以可以对数组进行对象属性的解构
let arr = [1, 2, 3];
let { 0: first, [arr.length - 1]: last } = arr;
first; // 1
last; // 3
- 数组的解构赋值也可以这样使用
let [,m] = [2,3]
- 设置解构赋值的默认值
function move({ x = 0, y = 0 } = {}) {
return [x, y];
}
- 如果解构赋值语句不是变量声明语句(前面没有 var,let,const),即对已经声明的变量进行进行解构赋值需要注意加上括号 注:数组的解构赋值[x,y]=[y,x]好像不用加括号
let x;
({ x } = { x: 1 }); // 和立即执行函数一样,该语句的前面一行最好加上分号,否则可能会被当做函数调用
- 解构赋值 可以直接提取 JSON 数据,当作 object 处理
按位运算 妙用
- http://www.matrix67.com/blog/archives/263
- http://www.matrix67.com/blog/archives/264
- http://www.matrix67.com/blog/archives/266
- http://www.matrix67.com/blog/archives/268
switch case 不加 break 会怎样
看一段代码:
switch (direction) {
case "U":
case "R":
case "D":
case "L":
}
在这个语句中,如果 direction=U。那么四个 case 语句都将执行。如果 direction=R。将执行剩下三个语句。
原因是 switch 语句原理是跳转到 caseX 位置执行剩下的语句,直到最后或者遇见 break 为止。
比如 React 源码中有过这样使用
switch (type) {
case "string":
case "number":
invokeCallback = true;
break;
case "object":
switch (children.$$typeof) {
case REACT_ELEMENT_TYPE:
case REACT_PORTAL_TYPE:
invokeCallback = true;
}
}
事件 event 捕获和冒泡详解
https://zh.javascript.info/bubbling-and-capturing
DOM 事件标准描述了事件传播的 3 个阶段:
捕获阶段(Capturing phase)—— 事件(从 Window)向下走近元素。
目标阶段(Target phase)—— 事件到达目标元素。没有被单独处理:捕获阶段和冒泡阶段的处理程序都在该阶段被触发。
冒泡阶段(Bubbling phase)—— 事件从元素上开始冒泡。
如果我们在同一阶段有多个事件处理程序,并通过 addEventListener 分配给了相同的元素,则它们的运行顺序与创建顺序相同
注册事件 handler 的三种方式
- 使用 dom 对象的同名 property 赋值:
document.querySelector('button').onclick = function (e){}
- 使用
addEventListener()
方法 - 在 html 的 dom 的 attribute 中 直接
inline event handler
不建议,html 和 js 这样 mix up 在一起不易管理也低效,只能用于一个 handler 只 handle 单个元素的情况
inline event 也有点优点
实质上,当事件处理程序被指定为 HTML 属性,指定的代码被包装在有 以下参数 的函数中。_该 inline handler 函数的入参:event / this_
EventTarget 接口
https://wangdoc.com/javascript/events/eventtarget.html
- addEventListener(type, listener[, options={capture, once, passive, signal} / useCapture=false ])
- removeEventListener(type, listener[, options={capture} / useCapture=false])
- dispatchEvent()
cancelled = !target.dispatchEvent(event instance)
事件代理
ul.addEventListener('click', function (event) {event.target blabla...})
取消各种
- event.preventDefault 取消浏览器对当前事件的默认行为。比如点击链接的默认跳转页面
- event.stopPropagation 阻止事件在 DOM 中继续传播(addEventListener 时 默认未指定触发阶段的情况下,这里即取消冒泡阶段),防止再触发定义在别的节点上的监听函数。但是不包括在当前节点上其他的事件监听函数
- event.stopImmediatePropagation 阻止同一个事件的其他监听函数被调用。取消冒泡阶段 基础上,取消自己的
自定义事件
new CustomEvent(typeArg, {detail: null})
判断类型的 4 种方法
https://www.cnblogs.com/onepixel/p/5126046.html
- typeof
- instanceof 内部实质:
A.__proto__ === B.prototype
, 有个极端抬扛的情况是,一个网页有多个 iframe 即多个全局执行环境时,iframe1 的 arr1 肯定不会 === iframe2 的 Array 的,这时候用 Array.isArray(arr)来判断 - constructor 找到构造函数
- Object.toString() 使用:
Object.prototype.toString.call(true)
解释:If this method is not overridden in a custom object, toString() returns “[object type]” 如[].toString.call('sss') // "[object String]"
symbol 的应用场景
promise
深入理解try catch(error) finally
- try 的 {} 是必须的,即使只有一行
- try 之后至少一个(catch 或 finally)
- 可以嵌套 try,最里面 throw 了但是没有 catch 的话,会向上一层层找 catch
- If the finally-block returns a value, this value becomes the return value of the entire
try-catch-finally
statement;否则就按前面最后一个执行的 block 的 return - 最佳实践
- react 的 顶层 try catch - Error Boundaries:
componentDidCatch(error, errorInfo) {}
&static getDerivedStateFromError(error) {}
- react 中的错误捕获 & 错误边界: 错误边界不能捕获的异常有两个
- 事件处理(使用 try catch);
- 异步代码(局部的话就
async await
+try catch
; 全局的解决方案是:window.addEventListener(‘error’, e=>{}) 和 window.addEventListener(“unhandledrejection”,e=>{})) - 服务端渲染
- 自身错误(将边界组件和业务组件分离,各司其职)
fetch & xmlhttprequest & axios 对他们的封装 & websocket & request/response header body, http1/2 协议
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
fetch & Ajax & axois 对比 及 xss 和 csrf 攻击 安全问题
https://juejin.cn/post/6844904184748195848
跨域 和 csrf 攻击
https://juejin.cn/post/6844903934310498312