简体   繁体   English

较新的 JavaScript (ES6) 和 TypeScript 中原型/基于原型的继承的最佳实践?

[英]Best practices for prototypal / prototype-based inheritance in newer JavaScript (ES6) & TypeScript?

Here are a couple of older question that discuss the Javascript prototypal inheritance & delegation, eg:这里有几个较旧的问题,讨论 Javascript 原型继承和委托,例如:

I am wondering what the current (2018) recommendation is to use prototypes / prototypal inheritance in Javascript.我想知道当前(2018 年)的建议是在 Javascript 中使用原型/原型继承。

As far as I understand, newer version of JavaScript (ES6) and TypeScript they both head more towards traditional class-based inheritance.据我所知,较新版本的 JavaScript (ES6) 和 TypeScript 都更倾向于传统的基于类的继承。 (Myself I didn't use ES6 oder TS in practice yet.) Is this observation true? (我自己在实践中还没有使用 ES6 或 TS。)这个观察是真的吗?

In fact, this class-based code is really simple and easy to understand:其实这段基于类的代码真的很简单易懂:

class A { a: "a" }
class B extends A { b: "b" }
let a = new A(), b = new B();

EDIT 2 : In TypeScript it would be:编辑 2 :在 TypeScript 中,它将是:

class A { a = "a" }
class B extends A { b = "b" }
let a = new A(), b = new B();

EDIT : In fact, the ES6 syntax is more complex:编辑:事实上,ES6 语法更复杂:

class A { constructor() { this.a = "a"; } }
class B extends A { constructor() { super(); b = "b"; } }
let a = new A(), b = new B();

For using prototypes , there are more options, and actually I didn't find one yet that is equally simple and "nice".对于使用prototypes ,有更多的选择,实际上我还没有找到一个同样简单和“好”的。

EDIT : What I want to achieve is that I create b as instance of B with prototype A in a way that when I change A's property dynamically, b also is affected by the change:编辑我想要实现的是,我使用原型 A 创建 b 作为 B 的实例,这样当我动态更改 A 的属性时, b 也会受到更改的影响:

A simple approach is:一个简单的方法是:

 var A = { a: "a" } var B = Object.create(A, {b: {value: "b"}}); var a = Object.create(A), // direct instance of A b = Object.create(B); // indirect instance of A console.log(ba); // "a" Aa = "a++"; // change the base prototype (will affect B, a, and b) console.log(ba); // "a++"

Which would be a lot nicer if the 2nd arg could also be a simple object with key-value pairs, not the property descriptors (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create )如果第二个 arg 也可以是带有键值对的简单对象,而不是属性描述符,那会好得多(请参阅https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ Global_Objects/对象/创建)

Most of the time, the constructor function are used, eg in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain大多数情况下,使用构造函数,例如在https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

 function A() { this.a = "a"; } function B() { this.b = "b"; } B.prototype = new A(); var a = new A(), b = new B(); console.log(ba); // "a" Aa = "a++"; console.log(ba); // return "a" instead of "a++" as ba is overwritten in constructor

Also, not so nice, as here you cannot change Aa in a way that ba is also changed which is IMO a key point in prototypal inheritance.另外,不太好,因为在这里你不能以 ba 也改变的方式改变 Aa,这是 IMO 原型继承中的一个关键点。 So maybe this?所以也许这个?

function A() {}
A.prototype.a = "a";
function B() {}
B.prototype = Object.create(A.prototype);
B.prototype.b = "b";
var a = new A(), b = new B();
function A() { this.a = "a"; }
function B() { this.b = "b"; }
B.prototype = new A();
var a = new A(), b = new B();
console.log(b.a); // "a"
A.a = "a++";
console.log(b.a); // still "a" instead of "a++"

Does not give the expected result.没有给出预期的结果。 And, well, you don't want to write this, right?而且,好吧,你不想写这个,对吧?

Of course, you can put the creation in a constructor function as described by https://stackoverflow.com/a/16872315/1480587 but I think this is still not equally nice and simple to the class syntax.当然,您可以按照https://stackoverflow.com/a/16872315/1480587 的描述将创建放在构造函数中,但我认为这对于类语法来说仍然不够好和简单。 Actually, I'm looking for something like this (similar to Kotlin's object declaration ):实际上,我正在寻找这样的东西(类似于Kotlin 的对象声明):

object A { a: "a" }
object B extends A { b: "b" }
let a = new A(), b = new B();

So, what would you recommend?那么,你会推荐什么? Is there anything that comes close?有什么可以接近的吗?

Especially, if you want to use some encapsulation and have private object members not visible to cloned objects?特别是,如果您想使用一些封装并且让私有对象成员对克隆对象不可见?

Does TypeScript provide a nice solution here? TypeScript 在这里提供了一个很好的解决方案吗?

Go for Kotlin?去 Kotlin 吗?

Or should one generally move back to class-based inheritance as this is what everyone else is using and understands?还是应该回到基于类的继承,因为这是其他人都在使用和理解的?

Maybe I play around with a simple helper function like this:也许我会玩一个像这样的简单辅助函数:

/**
 * Create a new object with `prototype` as its prototype.
 * The (optional) properties will be copied to the newly created object
 * This is similar to Object.create(), however the properties passed is a plain
 * object, not a object with property descriptors,
 * see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
 *
 * @param prototype (object)
 * @param properties (object)
 * @return {prototype}
 */
function clonePrototype(prototype, properties) {
    var pDef = {};
    for(var key in (properties||{})) {
        if (properties.hasOwnProperty(key)) { pDef[key] = {value: properties[key]}; }
    }
    return Object.create(prototype, pDef);
}

This way, I can do what I wanted:这样,我可以做我想做的事:

var a = { a: "a" };
var b = clonePrototype(a, { b: "b" });
console.log(b.a); // "a"
a.a = "a++";
console.log(b.a); // "a++"

.... comments and suggestions are welcome. .... 欢迎提出意见和建议。

A simple approach is Object.create一个简单的方法是Object.create

Then use that.然后用那个。 It seems to be enough for what you want to do - two objects, the one inheriting from each the other.这似乎足以满足您想要做的事情 - 两个对象,一个相互继承。 You don't need any constructors to do initialisation, and therefore no class syntax either.您不需要任何构造函数来进行初始化,因此也不需要class语法。


Btw, to simplify I would not use the second argument to Object.create when you don't need custom property descriptors.顺便说一句,为了简化,当您不需要自定义属性描述符时,我不会使用Object.create的第二个参数。 Just do做就是了

var B = Object.create(A);
B.b = "b";

or, in one expression,或者,用一种表达方式,

var B = Object.assign(Object.create(A), {
  b: "b",
});

Alternatively, underscore and lodash provide _.create(), which help with creating a new object with initialized prototype:或者,underscore 和 lodash 提供 _.create(),这有助于创建一个带有初始化原型的新对象:

var a = { a: "a" };
var b = _.create(a, { b: "b" });
console.log(b.a); // "a"
a.a = "a++";
console.log(b.a); // "a++"

I just found what I was looking for: In ES6 "object literals are extended to support setting the prototype at construction."我刚刚找到了我要找的东西:在 ES6 中, “对象文字被扩展为支持在构造时设置原型。” This is really simple & convenient!!真的很简单方便!!

var obj = {
    // Sets the prototype. "__proto__" or '__proto__' would also work.
    __proto__: theProtoObj,
    // Computed property name does not set prototype or trigger early error for
    // duplicate __proto__ properties.
    ['__proto__']: somethingElse,
    // ...
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM