-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
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
第 53 题:输出以下代码的执行结果并解释为什么 #93
Comments
结果: 首先,a和b同时引用了{n:2}对象,接着执行到a.x = a = {n:2}语句,尽管赋值是从右到左的没错,但是.的优先级比=要高,所以这里首先执行a.x,相当于为a(或者b)所指向的{n:1}对象新增了一个属性x,即此时对象将变为{n:1;x:undefined}。之后按正常情况,从右到左进行赋值,此时执行a ={n:2}的时候,a的引用改变,指向了新对象{n:2},而b依然指向的是旧对象。之后执行a.x = {n:2}的时候,并不会重新解析一遍a,而是沿用最初解析a.x时候的a,也即旧对象,故此时旧对象的x的值为{n:2},旧对象为 {n:1;x:{n:2}},它被b引用着。 上面是之前写的解释,最近看周爱民老师的文章的时候,发觉这部分解释有不少地方没说到本质上,有的还是错误的,所以我重新结合老师的文章研究了一下,修改如下: var a = {n:1};
a.x = a ={n:2};
console.log(a.x);
所以,旧对象(丧失了引用的最初对象)和新对象(往后通过 // 旧对象
a:{
n:1,
x:{n:2}
}
// 新对象
a:{
n:2
} 现在,执行 Note:也就是说,在引擎从左到右计算表达式的过程中,尽管可能遇见类似 这个代码块所做的事情,实际上是向旧有对象添加一个指向新对象的属性,并且如果我们想要在后续仍然持有对旧对象的访问,可以在赋值覆盖之前新建一个指向旧对象的变量。 |
undefined |
把 a.x = a = {n: 2}, 换成 b.x = a = {n: 2} 的时候,是不是会好理解了,虽然确实是这样。 |
以前有做过一个一样的题,等号运算符和 https://youzixr.github.io/2019/03/05/JS-%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95/ |
运算符优先级还真没注意,涨姿势了 |
答案如上 |
var a = {n: 1}; // a保持对{n:1}对象的引用
var b = a; // b保持对{n:1}对象的引用
a.x = a = {n: 2}; // a的引用被改变
a.x // --> undefined
b.x // --> {n: 2} 1、 |
进阶系列中有的 |
为啥不会重新解析a啊 |
有一个网站可以将 JavaScript 代码的执行过程,用可视化的方式呈现出现。具体链接如下:tylermcginnis 从可视化的执行过程来看,并没有之前上面答案所说的对象增加 x 属性的这个过程,也即 const a = {};
const b = 1;
a.x = b; 第三行代码 这个问题考察的知识点主要有以下这些:
let a = { n: 1 };
const b = a;
a.x = a = { n: 2 }; 执行完第一行以及第二行代码之后,变量 在执行第三行代码之前,你要知道 至于想搞清楚自己到底有没有理解这个,可以尝试想一下:如果 let a = { n: 1 };
const b = a;
// `.` 的优先级低于 `=` 的优先级
a.x = a = { n: 2 };
console.log(a); // 报错 简单的分析一下, |
连续赋值var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x)
console.log(b.x)
a = { n : 1 }
b = a
a.x = undefined
a // {n: 1 , x: undefined}
b // 也指向上行的堆内存
a.x = a = {n: 2};
{n: 1 , x: undefined}.x = {n: 2}
a = {n: 2}
b = {
n: 1,
x: {
n: 2
}
} |
之前我也一直想为什么不会重新解析a,然后看了上面很多分析,但还是有些看不明白,然后看到点的优先级大于等号的优先级,那我可以这么理解吗? |
来凑个热闹 ...
|
@yeyi361936738 我不认同你的这个观点
你这句话恰好说明你不懂 . 的优先级大于 = 是什么意思 @onloner2012 @ruyanzhang 至于为什么不会重新解析 let a = {};
let b;
[b = a] = [, a = {n: 2}]; 现在继续来解释下面这一段代码 const a = {};
a.x = void 1024; 由于 顺便说一句,往对象上新增一个属性或者修改已存在属性的属性值,不一定能成功。以下是一些🌰:
Object.defineProperties(
Object.prototype,
{
a: {
value: 1,
writable: false,
},
},
);
const obj = {};
obj.a = 2;
Object.defineProperties(
Object.prototype,
{
a: {
get() {
return 1;
},
},
},
);
const obj = {};
obj.a = 2;
const empty = {};
Object.isExtensible(empty); // === true
Object.preventExtensions(empty);
Object.isExtensible(empty); // === false
// Sealed objects are by definition non-extensible.
const sealed = Object.seal({});
Object.isExtensible(sealed); // === false
// Frozen objects are also by definition non-extensible.
const frozen = Object.freeze({});
Object.isExtensible(frozen); // === false 至于为什么对下面这句表述所加粗的地方存在质疑,是因为有🌰的加持
let a = { n: 1 };
const b = a;
const proxy = new Proxy(a, {
set(target, key, value, receiver){
console.info([
`需要把对象 ${JSON.stringify(target)} 的 ${key} 属性的值改为 ${JSON.stringify(value)}`,
`此时的 a 对象以及 b 对象分别为 ${JSON.stringify(a)}、${JSON.stringify(b)}`,
`target 对象是否等于 b 对象:${target === b}`
].join('\n'));
return Reflect.set(target, key, value, receiver);
},
});
proxy.x = a = { n: 2 };
console.log(a.x);
console.log(b.x); |
a.x = a = { n:2 }
|
我在想,问题的表达式能不能改写成这样 var a = { n : 1};
var b = a;
a.x;
a = { n: 2 };
b.x = a;
console.log(a.x);
console.log(b.x); 因为 |
可以看下你楼上的答案:#93 (comment)
a = {n:2}
b = {n:1, x: {n:2}} |
一开始有点疑惑, |
console.log(a.x); // undefined
console.log(b.x); //{n:2} |
. 操作符的优先级比赋值高。 解构一下发生了什么:
关键在于,. 操作符的计算会先执行,也就是先把 b.x 引用到了新对象 { n:2 }, 作者:叔叔张 |
这一篇讲的很清除,如果再加上a = {n: 2}的返回值为{n: 2}就更好了 |
总感觉大家在强行解释 ,为啥没人觉得这种设计有问题?同样是赋值,a.x 比 a 为啥优先级就一定要高? |
var a = {n: 1}; console.log(a.x) |
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x)
console.log(b.x) |
考察对象的指针和符号的优先级
得出 a = { n: 2 },
b = {
n: 1,
x: { n: 2 }
} 最终结果打印出来是: undefined { n: 2 } |
|
第一步a、b均指向{n: 1} js连续赋值规则论证如下: 上述例子可转换为: (function() {
var obj = { a: { c: { n: 1 } } };
var b = obj.a.c;
obj.a.c.x = obj.a.c = { n: 2 };
console.log(obj.a.c.x);
console.log(b.x);
})(); 添加监听后为: (function() {
var obj = {};
var model = { 'a': { c: { n: 1 } } };
model['a.c'] = model['a'].c;
Object.defineProperty(obj, 'a', {
set: function(val) {
console.log('set a');
model['a'] = val;
},
get: function() {
console.log('get a');
return model['a'];
}
});
Object.defineProperty(obj.a, 'c', {
set: function(val) {
console.log('set a.c');
model['a.c'] = val;
},
get: function() {
console.log('get a.c');
return model['a.c'];
}
});
var b = obj.a.c;
Object.defineProperty(obj.a.c, 'x', {
set: function(val) {
console.log('set a.c.x');
model['a.c.x'] = val;
},
get: function() {
console.log('get a.c.x');
return model['a.c.x'];
}
});
console.log('***start***');
obj.a.c.x = obj.a.c = { n: 2 };
// obj.a.c = obj.a.c.x = {n: 2};
console.log('***end***');
console.log(obj.a.c.x);
console.log(b.x);
})();
...
***start***
get a
get a.c
get a
set a.c
set a.c.x
***end***
...
undefined
...
{n: 2}
...
***start***
get a
get a
get a.c
set a.c.x
set a.c
***end***
...
undefined
...
{n: 2} 根据观察结果先从左往右查找赋值位置,位置查找完后再从右往左对查找到的位置进行赋值 注:此处的从右往左进行赋值并不是简单的每项逐一赋值。而是以拆开的形式统一赋值,如x = y = z实际为y = z, x = z。此处在MDN上有相关说明 如需验证可执行: (function() {
var a = {};
var b = {};
Object.defineProperty(a, 'x', {
get: function() {
return 1
}
});
b = a.x = { n: 2 };
console.log(b);
})(); {n: 2} |
简单理解: var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x) // {n: 2}
console.log(b.x) // {n: 1, x: {n: 2}}
console.log(b.x === a) // true |
这是连续赋值的坑
The text was updated successfully, but these errors were encountered: