对象销毁不是我最喜欢的事情,我经常试图避免使用它。然而,在这种特殊的情况下,我太好奇了,不能忽视正在发生的事情。
现在我们可以做喜欢;
var o = {},
x = 1,
y = 2;
o = {x,y};
console.log(o); // <- {x: 1, y: 2}
一切都很好;
不过,我的情况有点复杂;
var p = function(o){
o.promise = new Promise((resolve,reject) => o = {resolve,reject});
console.log(o) // -< {resolve: ƒ, reject: ƒ}
return o;
}({});
console.log(p); // -< {resolve: ƒ, reject: ƒ}
p
..的promise
属性在哪里。。?所以我用古典的方式来做;
var q = function(o){
o.promise = new Promise((resolve,reject) => ( o.resolve = resolve
, o.reject = reject
));
console.log(o) // <- {promise: Promise, resolve: ƒ, reject: ƒ}
return o;
}({});
console.log(q); // <- {promise: Promise, resolve: ƒ, reject: ƒ}
我有一种奇怪的感觉,好像我错过了一些非常基本的东西,但我不知道是什么。
在您显示的任何代码中都没有析构函数。
您正在将o
重新分配给一个简单的、具有两个属性的新对象:
o = {resolve,reject}
这只是ES2015引入的对象文字缩写语法,与相同
o = {resolve: resolve, reject: reject}
当然,之前的promise
属性会丢失,因为它位于一个不再被o
引用的对象上(如果内存中没有其他对它的引用,它最终会被垃圾收集(。
o.promise = new Promise((resolve,reject) => o = { ...o, resolve, reject });
...o
也没有破坏,它是对象扩展语法你也可以使用
o.promise = new Promise((resolve,reject) => o = Object.assign(o, { resolve, reject }));
请注意,Object.assign()
会触发setter(如果有的话(,而spread语法则不会。
问题
让我用这个方便的图表来解释发生了什么:
var p = function(o){
// 1 2 3
// v vvvvvvvvvvv vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
o.promise = new Promise((resolve,reject) => o = {resolve,reject});
// ^ ^ ^^^^^^^^^^^^^^^
// 6 5 4
console.log(o)
return o;
}({});
console.log(p);
- 开始分配给
o.promise
引擎将首先引用o
,然后评估分配给promise
属性的内容 - 赋值表达式是对具有
new
的Promise
构造函数的调用。引擎必须对整个事物进行评估并获得一个对象,然后才能将任何内容分配给promise
属性 Promise
构造函数接收一个executor函数。该函数会立即被调用。在执行人完成之前,承诺的构建无法完成- 不符合整个图片的顺序,但为了清晰起见,添加了一个具有两个属性
resolve
和reject
的对象 - 对象被指定给
o
。注意步骤1。是分配的开始。作业尚未完成。现在是中途代码在分配给o.promise
之前重新分配o
。但是,这种情况发生在引擎引用了分配给o
的初始对象之后现在有两个对象与o
相关:- 步骤1开始时分配给的。发生
- 分配给它的现在-在步骤5之后
- (为简洁起见,压缩内容(:executor函数已完成。
Promise
构造函数也完成了。对完整表达式CCD_ 24进行了评估并产生了一个值。现在可以继续分配给promise
属性。因为它使用了来自步骤1该代码分配给不再分配给o
的对象的promise值 - (也没有图示(在步骤1中分配给
o
的初始对象。没有更多对它的引用。它不符合垃圾回收的条件,将从内存中消失。(最初分配的(o
已死亡,(新分配的(长寿命o
整个情况可以简化并用以下代码表示:
//get two references to the same object
let o1 = {};
let o2 = o1;
o1.promise = (() => o1 = {resolve: "foo", reject: "bar"})();
console.log(o1);
console.log(o2);
由于Promise
构造函数只调用executor函数,因此它被IIFE所取代,IIFE在对其主体进行求值时表现相同。CCD_ 31和CCD_。由于IIFE重新分配了o1
,因此o2
变量会向我们显示初始对象发生了什么。它确实获得了一个添加到其中的promise
属性,但(没有其他引用(它随后丢失了。
考虑到这一点,我们可以看到,如果我们对作为o
:自变量的对象有另一个引用,那么在原始代码中也会发生类似的事情
//have a separate reference for the object passed below
const foo = {};
var p = function(o){
o.promise = new Promise((resolve,reject) => o = {resolve,reject});
console.log("o is", o)
return o;
}(foo);
console.log("p is", p);
//stack console shows `"promise": {}` but it's a promise object
//check the browser console if you want to see it
console.log("foo is", foo);
console.log("foo.promise is a Promise:", foo.promise instanceof Promise);
解决方案
connexo显示您可以在另一个答案中使用Object.assign()
(此处包含以供参考(:
var p = function(o){
o.promise = new Promise((resolve,reject) => o = Object.assign(o, { resolve, reject }));
console.log(o)
return o;
}({});
console.log(p);
//usage
p.promise
.then(result => console.log(`Completed with ${result}`));
p.resolve(42);
这是因为o
没有被重新分配,而是通过更多的属性进行了增强。这也是使用逗号运算符的版本有效的原因——它仍然没有重新分配o
,只是修改它:
o.promise = new Promise((resolve,reject) => ( o.resolve = resolve
, o.reject = reject
));
另一种选择是对对象属性(与变量相反(使用析构函数赋值:
o.promise = new Promise((resolve,reject) => [o.resolve, o.reject] = [resolve, reject]);
或者,如果你想让它更简洁:
o.promise = new Promise((...rs) => [o.resolve, o.reject] = rs);
var p = function(o){
o.promise = new Promise((...rs) => [o.resolve, o.reject] = rs);
console.log(o)
return o;
}({});
console.log(p);
//usage
p.promise
.then(result => console.log(`Completed with ${result}`));
p.resolve(42);