We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
本文为博客迁移过来,原文链接: 一道赋值面试题引发的思考:2018-7-31
本篇主要说一些基础知识点,关于多项赋值顺序,对象引用等,期间插入一点es6只是以及解决问题的思路。
开头先来做一道面试题
var a={n:1}; var b=a; a.x=a={n:2}; console.log(a.x); console.log(b.x);
最后输出的是什么? 先不说答案,我们来分析一下
L2(第二行) 我们把a赋值给b, 由于a是对象类型,这就意味着b和a指向同一个内存地址
L3 a.x = a = { n: 2}
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
a = {n: 2} && a.x = {n:2}
a.x = {n:2} && a= {n:2}
a = {n: 2} && a.x = a
我们这里可以借助 Object.defineProperty 或 ES6的 Proxy来验证多项赋值的顺序是怎样的
Object.defineProperty
Proxy
const obj = new Proxy({}, { set(target, key, value, r) { console.log(key, value) if (key === 'a') Reflect.set(target, key, 'isA', r); else Reflect.set(target, key, value, r); } }); obj.b = obj.a= {n: 1}; // 输出: // "a" {n: 1} // "b" {n: 1} obj.a; // isA obj.b; // {n: 1}
所以我们可以得出 赋值的顺序是从右边开始到左边的。而且是直接 a = {n: 1}, a.x = {n:1 },而不是 a.x = a 这样去赋值
a = {n: 1}, a.x = {n:1 }
a.x = a
现在我们再借助 Proxy 来分析一开始part1这道题,用obj.a, obj.b 来代替原题目的 a和b。
var obj = new Proxy({}, { get: function (target, key, receiver) { console.log(`getting ${key}!`); return Reflect.get(target, key, receiver); }, set: function (target, key, value, receiver) { console.log(`setting ${key}!`); return Reflect.set(target, key, value, receiver); } }); obj.a = {n: 1 };// getting a; obj.b = obj.a; // getting a; setting b; obj.a.x = obj.a = {n:2 }; // getting a; setting a;
可以看到 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 = {n: 2}
obj.a.x
obj.a
{n:2 }
重点: 在对obj.a.x赋值的时刻已经获取了obj.a该对象指向的内存地址,所以后面a就算指向其他地址,也和这里的obj.a.x无关。此时指向该地址的还有obj.b
我们再用三张图来捋一捋整理的思路
执行 obj.a = {n: 1}; obj.b = obj.a后obj对应的引用是这样的
obj.a = {n: 1}; obj.b = obj.a
执行 obj.a.x = xxx 时
obj.a.x = xxx
执行obj.a.x = obj.a = {n:2} 后
obj.a.x = obj.a = {n:2}
至此,这道面试题相信大家都有答案了,可以自己去控制台验证一下。 假如这时候再执行 obj.a.n = 3, 打印obj.b会输出什么呢?
obj.a.n = 3
obj.b
接下来我们来看另一道题,关于对象循环引用的
var a = { n: 1} a.b = a;
这里的a明显是循环引用,那么我们要怎样才能判断一个对象是否是循环引用呢?
其实这道题我一开始除了递归判断外没有很好的解决方案,后面是群里一个叫话费的大佬说(这道题也是他出的)直接用 JSON.stringify,微信小游戏的源码里面就是这么去判断。
话费
JSON.stringify
JSON.stringify 如果遇到参数里有循环引用的,就会抛出一个循环调用的错误 Uncaught TypeError: Converting circular structure to JSON
那如果不用JSON.stringify或者想要自己实现一个去检测循环调用,该怎么写呢?(面试官和部门前端leader最喜欢这么问)
一般遇到这种,最简单的方法就是去找这个方法的 polyfill, json3。我找的是 json3的 polyfill 里面大概是遍历对象存到stack数组,再在解析的时候去判断是否有循环引用的情况。 json3.js#L482
照着他的思路大概写了一个,其实就是前面说到的简单递归判断
var stack = []; function fn(obj) { var len; for (len = stack.length; len--;) { if (stack[len] === obj) throw TypeError() } stack.push(obj); for (let k in obj) { const value = obj[k] if (typeof value === 'object') fn(value); } }
第一次讲关于语言知识点的,如果发现有错误的地方 欢迎指出~ 最后谢谢惠顾,请笑纳
The text was updated successfully, but these errors were encountered:
No branches or pull requests
本篇主要说一些基础知识点,关于多项赋值顺序,对象引用等,期间插入一点es6只是以及解决问题的思路。
开头先来做一道面试题
part1
最后输出的是什么? 先不说答案,我们来分析一下
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
来验证多项赋值的顺序是怎样的所以我们可以得出 赋值的顺序是从右边开始到左边的。而且是直接
a = {n: 1}, a.x = {n:1 }
,而不是a.x = a
这样去赋值现在我们再借助 Proxy 来分析一开始part1这道题,用obj.a, obj.b 来代替原题目的 a和b。
可以看到
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
接下来我们来看另一道题,关于对象循环引用的
这里的a明显是循环引用,那么我们要怎样才能判断一个对象是否是循环引用呢?
其实这道题我一开始除了递归判断外没有很好的解决方案,后面是群里一个叫
话费
的大佬说(这道题也是他出的)直接用JSON.stringify
,微信小游戏的源码里面就是这么去判断。JSON.stringify
如果遇到参数里有循环引用的,就会抛出一个循环调用的错误 Uncaught TypeError: Converting circular structure to JSON那如果不用JSON.stringify或者想要自己实现一个去检测循环调用,该怎么写呢?(面试官和部门前端leader最喜欢这么问)
一般遇到这种,最简单的方法就是去找这个方法的 polyfill, json3。我找的是 json3的 polyfill 里面大概是遍历对象存到stack数组,再在解析的时候去判断是否有循环引用的情况。 json3.js#L482
照着他的思路大概写了一个,其实就是前面说到的简单递归判断
第一次讲关于语言知识点的,如果发现有错误的地方 欢迎指出~
最后谢谢惠顾,请笑纳
The text was updated successfully, but these errors were encountered: