
获得徽章 17
- #知识点记录# 在 JavaScript 中,undefined 其实是一个变量,这个变量只有一个值就是 undefined。对 null 和 undefined 进行普通相等性检查会得到 true,而进行严格相等检查才会得到 false。在 ECMAScript 中,undefined 并不是一个保留字段,我们可以将 undefined 作为一个局部变量使用,就像局部作用域中任何其他普通变量一样,没有任何特殊性,我们可以为其赋予任何类型的值。
```js
(function() {
var undefined = 'not is undefined';
console.log(undefined); //"not is undefined"
console.log(typeof undefined) // "string"
})()
```
这时我们可以看到,我们所理解的 undefined 的类型和值都被改变了,这在正常开发中容易导致难以查出的 bug。
那么我们怎样做才能确保万无一失呢?在 ECMAScript 规范中有一个叫 void 的运算符:
The void operator evaluates the given expression and then returns undefined.
void 运算符 对给定的表达式进行求值,然后返回 undefined。
也就是说,void 会对其后面的表达式求值,无论结果是多少,都会返回原始值 undefined。因此我们可以用 void 0 来代替 undefined 进行判断,因为 void 0 始终返回的都是原始值 undefined。
```js
let data;
console.log(data === void 0); //true
```展开8点赞 - #知识点记录# 今天看了 React.PureComponent 的源码,理解了一下浅比较和深比较。PureComponent 是对 shouldComponentUpdate 的浅比较实现,为什么是浅比较?这就是在性能和效果上做的取舍。
对象浅比较就是比较引用,引用相同,则为 true。深比较是比较值,即使是两个引用不同的对象,他们的值也会被一个一个拿出来进行比较。
在 React 中,在状态也就是 state 发生变化时,会对虚拟 DOM 进行 diff,然后重建对应节点。React 中始终贯穿数据不可变的思想,进行 diff 的时候,拿的是两个数据进行比对,它们的 reference 是不同的。如果进行深比较,会把对应的值拿出来,一一比对值。
而对于浅比较,在源码实现中是用的 Object.is(),这个方法只适用于比较基本类型,而对于对象这种引用类型,如果对象的引用不同就为 false。而 React 里面虚拟 DOM 的 diff,数据的引用就是不同的。所以 PureComponent 进行浅比较,state 里面的对象的变化它比较不出来,也就不会阻止 render。管他什么几层嵌套对象,只要是对象就比较不出来。因为引用不同。
所以 PureComponent ,只能比较出 state、props 里基本数据类型的变化,对象的变化比不出来,也就无法阻止对应的 render。所以,在实际开发中,使用 PureComponent,要注意对应组件里的 state 的数据结构是什么样的,否则达不到通过 PureComponent 进行性能优化的目的。
鄙人拙见,如有纰漏,烦请指出,十分感谢!展开等人赞过88 - #知识点记录# __proto__ 是 [[Prototype]] 的因历史原因而留下来的 getter/setter。
请注意,__proto__ 与 [[Prototype]] 不一样。__proto__ 是 [[Prototype]] 的 getter/setter。属性 [[Prototype]] 是内部的而且是隐藏的,但是有很多设置它的方式,其中之一就是使用特殊的名字 __proto__。
__proto__ 的存在是历史的原因。在现代编程语言中,将其替换为函数 Object.getPrototypeOf/Object.setPrototypeOf 也能 get/set 原型。
并且呢,在现代编程中不推荐再使用 __proto__ 了,而应该使用现代方法进行替代,具体有哪些现代方法请见:zh.javascript.info
展开赞过评论1 - If the function code is strict code, set the ThisBinding to thisArg.
Else if thisArg is null or undefined, set the ThisBinding to the global object.
Else if Type(thisArg) is not Object, set the ThisBinding to ToObject(thisArg).
Else set the ThisBinding to thisArg.
详见:www.ecma-international.org
展开1点赞 - #知识点记录# 浏览器会将 setTimeout 或 setInterval 的五层或更多层嵌套调用(调用五次之后)的最小延时限制在 4ms。这是历史遗留问题。并且,这个仅针对浏览器环境,如果你单纯 server 环境是没这个问题的。赞过12
- #知识点记录# 谈谈 WeakMap 和 WeakSet:首先说 Map 和 Set,Map 类似于 Object,是一个带键的数据项的集合,但是 Map 允许任何类型的键,并且保留了数据的插入顺序。Set 是一个特殊的类型集合 - “值的集合”(没有键),它的每一个值只能出现一次。在 Map 和 Set 中迭代总是按照值插入的顺序进行的,所以我们不能说这些集合是无序的,但是我们不能对元素进行重新排序,也不能直接按其编号来获取元素。
WeakMap 与 Map 不同,WeakMap 的键必须是对象。WeakSet 与 Set 不同,WeakSet 只能存储对象。
WeakMap 和 WeakSet 主要用于管理/存储“额外”的对象数据,主要解决了 Map 和 Set 的内存泄漏问题。比如我声明了一个对象,存在了 Map 里面当 key,然后在外面把对象清除了,但是 GC 不会回收对象,因为 Map 的 key 里面还有对对象引用。但 Weak 了之后就解决了这个问题,也就是 key 里的 Object 不影响垃圾回收机制。
但 Weak 后也有局限性,最明显的就是不能迭代,并且无法获取所有当前内容。展开评论点赞 - #知识点记录# 谈谈垃圾回收。最原始的垃圾回收算法就是引用技术,判断一个对象有多少个引用来决定是否应该回收,但是这样就会有孤岛问题,也就是循环引用的问题。现代的 JavaScript 垃圾回收算法是通过“标记清除(mark-and-sweep)”的方式,把概念转换成 reachable 和 unreachable 来解决这个问题。从 root 开始查找,回收 unreachable 的对象。这里的 root 是指当前函数的局部变量和参数,嵌套调用时当前调用链上所有函数的变量与参数,还有全局变量等。如果一个值可以通过引用或引用链从根访问任何其他值,则认为该值是可达的。
我觉得对于垃圾回收,主要是理解标记清除就好。当然除了这个之外,还有:
- 分代回收(Generational collection):有些对象用完就没用了,有些像全局变量,是长期使用的,针对这种差别优化垃圾回收的检测算法;
- 增量回收(Incremental collection):有时候需要清除的垃圾很多,马上进行垃圾回收,会影响 Web 性能,那么就会分批次,一部分一部分清除;
- 闲时回收(Idle-time collection):也是为了优化性能,降低垃圾回收对正常网页的影响。
这些都是 V8 引擎的知识,优化算法,对于前端正常来说,就是一定要理解标记清除和孤岛。那么在语言相关的内容都掌握了之后,我准备再好好看看 V8 底层的相关内容。展开等人赞过24 - #知识点记录# JavaScript 引擎在扫描代码时,也就是预编译阶段,对于发现的变量声明,要么将它们提升到作用域顶部,也就是 var 声明的,要么将声明放在暂时性死区,也就是 let 和 const,在执行阶段时,执行到声明语句后,才会从暂时性死区移除。
函数参数的声明是在预编译阶段的第一步,对于 Chrome 来说,是获得函数参数及其值,放在变量对象里,对于 Firefox 来说,是把整个参数对象 arguments 放在变量对象里,接下来获取函数声明,然后是变量声明。展开5点赞 - #知识点记录# 在 JavaScript 中,函数里的变量和参数应该就是在这个函数里的,属于这个函数的词法环境对象的属性。而函数也是执行环境,但这个函数是全局词法环境对象的属性。
闭包也是一个函数,它也是定义在全局里的,只是它的定义时间是依赖它的包含函数的,之所以闭包能访问外部函数的变量,是因为外部函数内的代码执行,执行到它,它被定义时,它把外部函数的变量对象保存在了自己的[[scope]]属性上。
这个内部函数被返回,然后执行时,第一创建自己的执行环境,然后把[[scope]]里保存的作用域复制过来,把自己的变量对象放在最前端,建立自己的作用域链,所以它依然可以访问外部函数的变量,所以无论闭包还是词法环境,搜索变量的过程都是在作用域链上搜索。
至于它能搜索到哪些变量,取决于它所在的执行环境,也就是this。
引用群友回答。展开评论点赞