gpt4 book ai didi

javascript - Javascript:如何在不复制但通过链接的情况下将其他对象B,C的方法混合到对象A中?

转载 作者:行者123 更新时间:2023-11-30 08:32:09 26 4
gpt4 key购买 nike

如果创建对象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仅复制目标中的方法,但未链接它们。
我已经上传了一个仅在一个对象中混合的问题,该问题已解决: Mix in one object, solution: prototypical inheritance

关于继承和组合,我在这里找到了有用的博客文章: Understanding Prototypes, Delegation & Composition

我想在一个对象中混合方法,而不是复制这些方法,更确切地说,我想对混合函数的定义进行赋值。

这怎么可能? (也许在ES2015中?)

最佳答案

对此有两个答案:

他们没有被复制,他们被引用


  我想在对象中混合方法,但不复制方法...


您正在执行的操作不是复制方法(函数; JavaScript doesn't really have methods),而是重用您已有的方法。仅存在一个foobar函数;在AB以及AC上只有属性,它们都引用相同的函数。

您可以从A.foo === B.fooA.bar === C.bar的事实中看到这一点:



let B = {
foo() {
alert("Boo!");
}
};
let C = {
bar() {
alert("No!");
}
};
let A = Object.assign({}, B, C);
snippet.log(A.foo === B.foo); // true
snippet.log(A.bar === C.bar); // true

<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>





您的代码在内存中产生的内容如下所示(省略了一些详细信息):

                        +----------+                      B>->| (object) |                          +----------+     +----------------+                        | foo      |>-+->|   (function)   |                        +----------+  |  +----------------+                                      |  | (code for foo) |                  +-------------------+  +----------------+                  |                  |     +----------+                        | C>->| (object) |                        |     +----------+     +----------------+                  |     | bar      |>-+->|   (function)   |                  |     +----------+  |  +----------------+                  |                   |  | (code for bar) |                  |                   |  +----------------+                  |                   |    +----------+  |                   |A>->| (object) |  |                   |    +----------+  |                   |    | foo      |>-+                   |    | bar      |>---------------------+    +----------+

But if you really want a link

If you want to be able to replace the B.foo or C.bar function with something else, and have A see that change, you can do that by making foo and bar on A property accessors:

let A = {
get foo() { return B.foo; },
get bar() { return C.bar; }
};


现在 A链接到 BC



let B = {
foo() {
snippet.log("foo1");
}
};
let C = {
bar() {
snippet.log("bar1");
}
};

let A = {
get foo() { return B.foo; },
get bar() { return C.bar; }
};

A.foo(); // foo1
A.bar(); // bar1

B.foo = function() {
snippet.log("foo2");
};

A.foo(); // foo2

C.bar = function() {
snippet.log("bar2");
};

A.bar(); // bar2

<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>





您可以使用 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() {
snippet.log("foo1");
}
};
let C = {
bar() {
snippet.log("bar1");
}
};

let A = {};
linkMethods(A, B);
linkMethods(A, C);

A.foo(); // foo1
A.bar(); // bar1

B.foo = function() {
snippet.log("foo2");
};

A.foo(); // foo2

C.bar = function() {
snippet.log("bar2");
};

A.bar(); // bar2

<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>





尽管我在上面使用了ES2015箭头功能,但如果将它们转换为 function函数,则以上所有功能都可以在ES5中使用(但不能在ES3或更早版本中使用,没有属性访问器)。



发表您的评论:


  您能否解释一下复制,引用和链接之间的区别是什么?


一些关键的事情:


在JavaScript中,函数是对象。真正的真实对象,具有其他种类的对象的所有功能,以及包含和运行代码的能力。 (其他语言通常不是这种情况。)
变量和属性保存值。
值可以是原始值(例如1或“ foo”),也可以是对象引用。对象引用不是对象,只是对对象的引用,该对象存在于其他地方。
当您将值分配给变量或属性时,就是在复制该值。如果该值是对象引用,则意味着您要复制的是引用,而不是对象。
JavaScript没有(在外部级别)变量引用或属性引用。 (大多数语言不,但有些语言。)


我喜欢这样解释对象引用,变量和值:假设我们有这个家伙Joe。乔真的很高兴,因为他今天已经42岁了,并且是《 The Hitchhiker's Guide to the Galaxy》的粉丝。因此他将数字42写在一张纸上。 42是一个值,在这种情况下为数字。纸是变量或属性。

乔决定参加一个聚会,因此他在休息室通知板上张贴了一个聚会通知,说他正在参加聚会,并在上面写上自己的家庭住址。公告也是变量或属性。乔的房子是一个对象。公告上写的地址是一个对象引用,告诉我们乔的房子在哪里。

乔在休息室进入穆罕默德(Mohammed),并且知道他是HHG的同伴,向他展示了他的论文,上面写着42,并指出了聚会的公告。穆罕默德(Mohammed)拿出一张纸,然后将42张纸复制到纸上,以记住乔的转折年龄(也许是买合适的卡)。也就是说,他将42(值)复制到他的纸上(变量/属性)。然后,因为他以前没有去过乔的家,所以他又拿了一张纸,并从乔的聚会公告中抄了房子的地址。房子不被复制,只是地址。该对象没有被复制,只是对其的引用。

后来,乔意识到派对的大小对于他的房子来说太大了,于是决定将其搬到城市的一家酒吧。他划掉公告上的地址,然后写在酒馆的地址上。但是他忘了告诉穆罕默德。所以聚会时间到了,穆罕默德去乔的家而不是酒吧。

返回JavaScript和函数,它们是对象:

如果你有:

let B = {
foo() {
console.log("I'm foo");
}
};


B.foo不包含 foo函数,它包含一个引用 foo函数的值(对象引用)。然后,当您这样做时:

let A = {};
A.foo = B.foo;


...您将值(对象引用)从 B.foo复制到 A.fooA.fooB.foo之间没有任何联系,也没有链接,只是它们恰好包含相同的值,就像乔的聚会声明和穆罕默德的纸之间没有联系一样,只是它们恰好都具有乔在他们身上的地址(开头)。

稍后,如果您执行 B.foo = function() { /*...*/ };,则将 B.foo中的值替换为引用另一个函数的新值。这对 A.foo无效,因为 A.fooB.foo之间也没有链接。就像乔对聚会公告上的地址划掉并在酒馆的地址写信一样,这对穆罕默德的纸没有任何影响。

我上面的属性访问器机制起作用的原因是,属性访问器是一个在您每次读取属性值时都会运行的函数。因此,当您获得 A.foo的值时,它实际上会运行一个函数,该函数返回当时 B.foo的值,而不是以前的值。比喻是,穆罕默德在纸上写下“看公告”,而不是写下乔的地址,然后在准备参加聚会时,看他的纸以记住去哪里,看到“查看公告,”返回休息室,然后查看更新的地址,然后转到酒吧。

关于javascript - Javascript:如何在不复制但通过链接的情况下将其他对象B,C的方法混合到对象A中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36080643/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com