本篇主要说一些基础知识点,关于多项赋值顺序,对象引用等,期间插入一点es6只是以及解决问题的思路。
开头先来做一道面试题
part1
1 | var a={n:1}; |
最后输出的是什么? 先不说答案,我们来分析一下
L2(第二行) 我们把a赋值给b, 由于a是对象类型,这就意味着b和a指向同一个内存地址
L3 a.x = a = { n: 2}
这里我们有个疑惑,这句语句执行顺序是 a = {n: 2} && a.x = {n:2}
还是 a.x = {n:2} && a= {n:2}
还是这种 a = {n: 2} && a.x = a
我们这里可以借助 Object.defineProperty
或 ES6的 Proxy
来验证多项赋值的顺序是怎样的
1 | const obj = new Proxy({}, { |
所以我们可以得出 赋值的顺序是从右边开始到左边的。而且是直接 a = {n: 1}, a.x = {n:1 }
,而不是 a.x = a
这样去赋值
现在我们再借助 Proxy 来分析一开始part1这道题,用obj.a, obj.b 来代替原题目的 a和b。
1 | var obj = new Proxy({}, { |
可以看到 obj.a.x = obj.a = {n: 2}
这段语句执行时,会先输出一个 getting a 再输出 __setting a__。
这就意味着在对 obj.a.x
赋值时,程序是先获取 obj.a
指向的对象的内存地址,此时触发了 __getting a__,然后再对右边 obj.a
进行赋值,触发了 __setting a__, 赋值完最后一步才是对 obj.a.x
赋值 {n:2 }
。
重点: 在对obj.a.x赋值的时刻已经获取了obj.a该对象指向的内存地址,所以后面a就算指向其他地址,也和这里的obj.a.x无关。此时指向该地址的还有obj.b
我们再用三张图来捋一捋整理的思路
执行 obj.a = {n: 1}; obj.b = obj.a
后obj对应的引用是这样的
执行 obj.a.x = xxx
时
执行obj.a.x = obj.a = {n:2}
后
至此,这道面试题相信大家都有答案了,可以自己去控制台验证一下。 假如这时候再执行 obj.a.n = 3
, 打印obj.b
会输出什么呢?
part2
接下来我们来看另一道题,关于对象循环引用的
1 | var a = { n: 1} |
这里的a明显是循环引用,那么我们要怎样才能判断一个对象是否是循环引用呢?
其实这道题我一开始除了递归判断外没有很好的解决方案,后面是群里一个叫话费
的大佬说(这道题也是他出的)直接用 JSON.stringify
,微信小游戏的源码里面就是这么去判断。
JSON.stringify
如果遇到参数里有循环引用的,就会抛出一个循环调用的错误 Uncaught TypeError: Converting circular structure to JSON
那如果不用JSON.stringify或者想要自己实现一个去检测循环调用,该怎么写呢?(面试官和部门前端leader最喜欢这么问)
一般遇到这种,最简单的方法就是去找这个方法的 polyfill, json3。我找的是 json3的 polyfill 里面大概是遍历对象存到stack数组,再在解析的时候去判断是否有循环引用的情况。 json3.js#L482
照着他的思路大概写了一个,其实就是前面说到的简单递归判断
1 | var stack = []; |
第一次讲关于语言知识点的,如果发现有错误的地方 欢迎指出~
最后谢谢惠顾,请笑纳