Javascript: 如何在不复制但链接的情况下将其他对象 B、C 的方法混合到我的对象 A 中?
Javascript: How can I mix in methods of other Objects B, C to my Object A without copying but with linking?
如果我创建对象 A:
let A = {};
并且想要混合来自其他对象 B 和 C 的方法:
let B = {
foo() {
alert("Boo!");
}
};
let C = {
bar() {
alert("No!");
}
};
通常我会打电话给:
Object.assign(A, B, C);
然后我改变我的功能 foo:
Object.assign(B, {
foo() {
alert("Hooray!");
}
});
Objcect.assign(C, {
bar() {
alert("Yes!");
}
});
之后调用 foo 或 bar:
A.foo(); // Actual output: "Boo!", desired: "Hooray!"
A.bar(); // Actual output: "No!", desired: "Yes!"
到目前为止我发现,Object.assign
只复制目标中的方法,但不会 link 它们。
我已经上传了一个关于仅混合一个对象的问题,已解决:
关于继承和组合我在这里找到了一篇有用的博文:Understanding Prototypes, Delegation & Composition
我想在对象中混合方法,而不是复制方法,更确切地说,我想要对混合函数的定义进行赋值。
这怎么可能? (也许在 ES2015 中?)
对此有两个答案:
它们不是复制的,而是引用的
I want to mix methods in an Object, but not copy the methods...
您所做的不是复制方法(函数;JavaScript doesn't really have methods),而是重用您已有的方法。仅存在一个 foo
或 bar
函数; A
和 B
以及 A
和 C
上只有属性指的是同一个函数。
从 A.foo === B.foo
和 A.bar === C.bar
:
可以看出
let B = {
foo() {
alert("Boo!");
}
};
let C = {
bar() {
alert("No!");
}
};
let A = Object.assign({}, B, C);
console.log(A.foo === B.foo); // true
console.log(A.bar === C.bar); // true
您的代码在内存中生成的内容如下所示(省略了一些细节):
+−−−−−−−−−−+
B>−>| (object) |
+−−−−−−−−−−+ +−−−−−−−−−−−−−−−−+
| foo |>−+−>| (function) |
+−−−−−−−−−−+ | +−−−−−−−−−−−−−−−−+
| | (code for foo) |
+−−−−−−−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−+
|
| +−−−−−−−−−−+
| C>−>| (object) |
| +−−−−−−−−−−+ +−−−−−−−−−−−−−−−−+
| | bar |>−+−>| (function) |
| +−−−−−−−−−−+ | +−−−−−−−−−−−−−−−−+
| | | (code for bar) |
| | +−−−−−−−−−−−−−−−−+
| |
+−−−−−−−−−−+ | |
A>−>| (object) | | |
+−−−−−−−−−−+ | |
| foo |>−+ |
| bar |>−−−−−−−−−−−−−−−−−−−−−+
+−−−−−−−−−−+
但是如果你真的想要一个link
如果您希望能够 替换 B.foo
或 C.bar
函数,并让 A
看到该更改,您可以通过在 A
属性 访问器 :
上制作 foo
和 bar
来做到这一点
let A = {
get foo() { return B.foo; },
get bar() { return C.bar; }
};
现在 A
被 link 编辑为 B
和 C
:
let B = {
foo() {
console.log("foo1");
}
};
let C = {
bar() {
console.log("bar1");
}
};
let A = {
get foo() { return B.foo; },
get bar() { return C.bar; }
};
A.foo(); // foo1
A.bar(); // bar1
B.foo = function() {
console.log("foo2");
};
A.foo(); // foo2
C.bar = function() {
console.log("bar2");
};
A.bar(); // bar2
您可以使用 Object.defineProperty
:
为属性的动态组合做到这一点
function linkMethods(to, ...from) {
from.forEach(source => {
for (let key in source) { // We're intentionally not filtering out inherited
if (typeof source[key] === "function") {
Object.defineProperty(to, key, {
get: function() {
return source[key];
}
});
}
}
});
return to;
}
进行中:
function linkMethods(to, ...from) {
from.forEach(source => {
for (let key in source) { // We're intentionally not filtering out inherited
if (typeof source[key] === "function") {
Object.defineProperty(to, key, {
get: function() {
return source[key];
}
});
}
}
});
}
let B = {
foo() {
console.log("foo1");
}
};
let C = {
bar() {
console.log("bar1");
}
};
let A = {};
linkMethods(A, B);
linkMethods(A, C);
A.foo(); // foo1
A.bar(); // bar1
B.foo = function() {
console.log("foo2");
};
A.foo(); // foo2
C.bar = function() {
console.log("bar2");
};
A.bar(); // bar2
尽管我在上面使用了 ES2015 箭头函数,但如果将它们转换为 function
函数,以上所有功能都适用于 ES5(但不适用于没有 属性 的 ES3 或更早版本存取器)。
回复您的评论:
Could you please explain what the difference between copying, a reference and a link is?
一些关键的事情:
在JavaScript中,函数是对象。真正的、真实的对象,具有其他类型对象的所有特征,并且能够包含和 运行 代码。 (其他语言通常不会出现这种情况。)
变量和属性持有 值。
A value 要么是原始值(如 1 或“foo”),要么是 对象引用 。对象引用不是对象,它只是对存在于其他地方的对象的引用。
当您为变量或 属性 赋值时,您正在复制该值。如果该值是对象引用,则意味着您正在复制引用,而不是对象。
JavaScript 没有(在外部级别)变量引用 或 属性 引用。 (大多数语言不会,但有些会。)
我喜欢这样解释对象引用、变量和值:假设我们有这个人 Joe。乔真的很高兴,因为他今天 42 岁了,而且他是 银河系漫游指南 的粉丝。所以他在一张纸上写下了数字 42。 42 是一个值,在本例中是一个数字。这张纸是变量还是属性.
Joe 决定举办派对,所以他在他工作的休息室布告栏上张贴了派对公告,说他正在举办派对,并在上面写下了他的家庭住址。公告也是一个变量或属性。乔的房子是一个对象。公告上写的地址是一个对象引用,告诉我们乔的房子在哪里。
Joe 运行 在休息室遇见了 Mohammed,知道他是 HHG 的粉丝,向他展示了他的论文,上面有 42,并指出了派对公告。 Mohammed 拿出一张纸,将 42 复制到上面,以记住 Joe 的年龄(也许是为了购买合适的卡片)。也就是说,他 将 42(值)复制 到他的纸上(variable/property)。然后,因为他以前没有去过乔的家,所以他又拿了一张纸,从乔的聚会公告上抄了房子的地址。 house 没有被复制,只是地址。 对象没有被复制,只是引用。
后来,乔意识到聚会对他的房子来说太大了,于是决定搬到城里的一家酒吧。他划掉公告上的地址,写下酒吧的地址。但他忘了告诉穆罕默德。所以到了聚会时间,穆罕默德去了乔的家而不是酒吧。
回到JavaScript和函数,它们是对象:
如果你有:
let B = {
foo() {
console.log("I'm foo");
}
};
B.foo
不 包含 foo
函数,它包含 引用 到 foo
函数。然后当你这样做时:
let A = {};
A.foo = B.foo;
...您 将值(对象引用)从 B.foo
复制 到 A.foo
。 A.foo
和 B.foo
之间没有任何联系,没有任何 link ,只是它们碰巧包含相同的值,就像乔的派对公告和穆罕默德的那张纸之间没有联系一样,除此之外,他们碰巧都有乔的地址(开头)。
稍后,如果您执行 B.foo = function() { /*...*/ };
,您会将 B.foo
中的值替换为引用不同函数的 new 值。这对 A.foo
没有影响,因为 A.foo
和 B.foo
之间没有 link。就像乔在聚会公告上划掉地址,写上酒吧的地址一样,对穆罕默德的那张纸没有任何影响。
我上面的 属性 访问器机制起作用的原因是 属性 访问器是一个 函数 每次你阅读它都会得到 运行 属性 的值。因此,当您获得 A.foo
的值时,它实际上 运行 是一个函数 returns B.foo
的值,因为它是 然后 ,不像以前那样。类比是穆罕默德在他的纸上写下“看公告”而不是写下乔的地址,然后当准备去参加聚会时,看着他的纸以记住去哪里,看到“看看公告,”回到休息室,看到更新后的地址——然后去酒吧。
您可以使用 Proxies 创建 mixin:
let A = {
aa: "AA",
bb: 'BB'
};
let B = {
foo() {
console.log(this.aa);
}
};
let C = {
bar() {
console.log(this.bb);
}
};
mixin = function(target, ...others) {
return new Proxy(target, {
others,
get(target, prop) {
for (let p of this.others)
if (prop in p)
return p[prop];
return target[prop];
}
});
}
let mA = mixin(A, B, C);
mA.foo();
mA.bar();
这可以通过索引属性来优化以避免每次查找。
如果我创建对象 A:
let A = {};
并且想要混合来自其他对象 B 和 C 的方法:
let B = {
foo() {
alert("Boo!");
}
};
let C = {
bar() {
alert("No!");
}
};
通常我会打电话给:
Object.assign(A, B, C); 然后我改变我的功能 foo:
Object.assign(B, {
foo() {
alert("Hooray!");
}
});
Objcect.assign(C, {
bar() {
alert("Yes!");
}
});
之后调用 foo 或 bar:
A.foo(); // Actual output: "Boo!", desired: "Hooray!"
A.bar(); // Actual output: "No!", desired: "Yes!"
到目前为止我发现,Object.assign
只复制目标中的方法,但不会 link 它们。
我已经上传了一个关于仅混合一个对象的问题,已解决:
关于继承和组合我在这里找到了一篇有用的博文:Understanding Prototypes, Delegation & Composition
我想在对象中混合方法,而不是复制方法,更确切地说,我想要对混合函数的定义进行赋值。
这怎么可能? (也许在 ES2015 中?)
对此有两个答案:
它们不是复制的,而是引用的
I want to mix methods in an Object, but not copy the methods...
您所做的不是复制方法(函数;JavaScript doesn't really have methods),而是重用您已有的方法。仅存在一个 foo
或 bar
函数; A
和 B
以及 A
和 C
上只有属性指的是同一个函数。
从 A.foo === B.foo
和 A.bar === C.bar
:
let B = {
foo() {
alert("Boo!");
}
};
let C = {
bar() {
alert("No!");
}
};
let A = Object.assign({}, B, C);
console.log(A.foo === B.foo); // true
console.log(A.bar === C.bar); // true
您的代码在内存中生成的内容如下所示(省略了一些细节):
+−−−−−−−−−−+ B>−>| (object) | +−−−−−−−−−−+ +−−−−−−−−−−−−−−−−+ | foo |>−+−>| (function) | +−−−−−−−−−−+ | +−−−−−−−−−−−−−−−−+ | | (code for foo) | +−−−−−−−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−+ | | +−−−−−−−−−−+ | C>−>| (object) | | +−−−−−−−−−−+ +−−−−−−−−−−−−−−−−+ | | bar |>−+−>| (function) | | +−−−−−−−−−−+ | +−−−−−−−−−−−−−−−−+ | | | (code for bar) | | | +−−−−−−−−−−−−−−−−+ | | +−−−−−−−−−−+ | | A>−>| (object) | | | +−−−−−−−−−−+ | | | foo |>−+ | | bar |>−−−−−−−−−−−−−−−−−−−−−+ +−−−−−−−−−−+
但是如果你真的想要一个link
如果您希望能够 替换 B.foo
或 C.bar
函数,并让 A
看到该更改,您可以通过在 A
属性 访问器 :
foo
和 bar
来做到这一点
let A = {
get foo() { return B.foo; },
get bar() { return C.bar; }
};
现在 A
被 link 编辑为 B
和 C
:
let B = {
foo() {
console.log("foo1");
}
};
let C = {
bar() {
console.log("bar1");
}
};
let A = {
get foo() { return B.foo; },
get bar() { return C.bar; }
};
A.foo(); // foo1
A.bar(); // bar1
B.foo = function() {
console.log("foo2");
};
A.foo(); // foo2
C.bar = function() {
console.log("bar2");
};
A.bar(); // bar2
您可以使用 Object.defineProperty
:
function linkMethods(to, ...from) {
from.forEach(source => {
for (let key in source) { // We're intentionally not filtering out inherited
if (typeof source[key] === "function") {
Object.defineProperty(to, key, {
get: function() {
return source[key];
}
});
}
}
});
return to;
}
进行中:
function linkMethods(to, ...from) {
from.forEach(source => {
for (let key in source) { // We're intentionally not filtering out inherited
if (typeof source[key] === "function") {
Object.defineProperty(to, key, {
get: function() {
return source[key];
}
});
}
}
});
}
let B = {
foo() {
console.log("foo1");
}
};
let C = {
bar() {
console.log("bar1");
}
};
let A = {};
linkMethods(A, B);
linkMethods(A, C);
A.foo(); // foo1
A.bar(); // bar1
B.foo = function() {
console.log("foo2");
};
A.foo(); // foo2
C.bar = function() {
console.log("bar2");
};
A.bar(); // bar2
尽管我在上面使用了 ES2015 箭头函数,但如果将它们转换为 function
函数,以上所有功能都适用于 ES5(但不适用于没有 属性 的 ES3 或更早版本存取器)。
回复您的评论:
Could you please explain what the difference between copying, a reference and a link is?
一些关键的事情:
在JavaScript中,函数是对象。真正的、真实的对象,具有其他类型对象的所有特征,并且能够包含和 运行 代码。 (其他语言通常不会出现这种情况。)
变量和属性持有 值。
A value 要么是原始值(如 1 或“foo”),要么是 对象引用 。对象引用不是对象,它只是对存在于其他地方的对象的引用。
当您为变量或 属性 赋值时,您正在复制该值。如果该值是对象引用,则意味着您正在复制引用,而不是对象。
JavaScript 没有(在外部级别)变量引用 或 属性 引用。 (大多数语言不会,但有些会。)
我喜欢这样解释对象引用、变量和值:假设我们有这个人 Joe。乔真的很高兴,因为他今天 42 岁了,而且他是 银河系漫游指南 的粉丝。所以他在一张纸上写下了数字 42。 42 是一个值,在本例中是一个数字。这张纸是变量还是属性.
Joe 决定举办派对,所以他在他工作的休息室布告栏上张贴了派对公告,说他正在举办派对,并在上面写下了他的家庭住址。公告也是一个变量或属性。乔的房子是一个对象。公告上写的地址是一个对象引用,告诉我们乔的房子在哪里。
Joe 运行 在休息室遇见了 Mohammed,知道他是 HHG 的粉丝,向他展示了他的论文,上面有 42,并指出了派对公告。 Mohammed 拿出一张纸,将 42 复制到上面,以记住 Joe 的年龄(也许是为了购买合适的卡片)。也就是说,他 将 42(值)复制 到他的纸上(variable/property)。然后,因为他以前没有去过乔的家,所以他又拿了一张纸,从乔的聚会公告上抄了房子的地址。 house 没有被复制,只是地址。 对象没有被复制,只是引用。
后来,乔意识到聚会对他的房子来说太大了,于是决定搬到城里的一家酒吧。他划掉公告上的地址,写下酒吧的地址。但他忘了告诉穆罕默德。所以到了聚会时间,穆罕默德去了乔的家而不是酒吧。
回到JavaScript和函数,它们是对象:
如果你有:
let B = {
foo() {
console.log("I'm foo");
}
};
B.foo
不 包含 foo
函数,它包含 引用 到 foo
函数。然后当你这样做时:
let A = {};
A.foo = B.foo;
...您 将值(对象引用)从 B.foo
复制 到 A.foo
。 A.foo
和 B.foo
之间没有任何联系,没有任何 link ,只是它们碰巧包含相同的值,就像乔的派对公告和穆罕默德的那张纸之间没有联系一样,除此之外,他们碰巧都有乔的地址(开头)。
稍后,如果您执行 B.foo = function() { /*...*/ };
,您会将 B.foo
中的值替换为引用不同函数的 new 值。这对 A.foo
没有影响,因为 A.foo
和 B.foo
之间没有 link。就像乔在聚会公告上划掉地址,写上酒吧的地址一样,对穆罕默德的那张纸没有任何影响。
我上面的 属性 访问器机制起作用的原因是 属性 访问器是一个 函数 每次你阅读它都会得到 运行 属性 的值。因此,当您获得 A.foo
的值时,它实际上 运行 是一个函数 returns B.foo
的值,因为它是 然后 ,不像以前那样。类比是穆罕默德在他的纸上写下“看公告”而不是写下乔的地址,然后当准备去参加聚会时,看着他的纸以记住去哪里,看到“看看公告,”回到休息室,看到更新后的地址——然后去酒吧。
您可以使用 Proxies 创建 mixin:
let A = {
aa: "AA",
bb: 'BB'
};
let B = {
foo() {
console.log(this.aa);
}
};
let C = {
bar() {
console.log(this.bb);
}
};
mixin = function(target, ...others) {
return new Proxy(target, {
others,
get(target, prop) {
for (let p of this.others)
if (prop in p)
return p[prop];
return target[prop];
}
});
}
let mA = mixin(A, B, C);
mA.foo();
mA.bar();
这可以通过索引属性来优化以避免每次查找。