【问题标题】:Best practices for prototypal / prototype-based inheritance in newer JavaScript (ES6) & TypeScript?在较新的 JavaScript (ES6) 和 TypeScript 中原型/基于原型的继承的最佳实践?
【发布时间】:2018-03-01 12:03:04
【问题描述】:

这里有几个讨论 Javascript 原型继承和委托的老问题,例如:

我想知道当前(2018 年)的建议是在 Javascript 中使用原型/原型继承。

据我了解,较新版本的 JavaScript (ES6) 和 TypeScript 都更倾向于传统的基于类的继承。 (我自己还没有在实践中使用 ES6 oder TS。)这个观察结果是真的吗?

其实这个基于类的代码真的很简单易懂:

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

编辑 2:在 TypeScript 中是:

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

编辑:其实 ES6 的语法更复杂:

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

对于使用原型,有更多的选择,实际上我还没有找到一个同样简单和“好”的。

编辑我想要实现的是,我使用原型 A 创建 b 作为 B 的实例,当我动态更改 A 的属性时,b 也会受到影响通过更改:

一个简单的方法是:

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(b.a); // "a"
A.a = "a++"; // change the base prototype (will affect B, a, and b)
console.log(b.a); // "a++"

如果第二个参数也可以是具有键值对的简单对象,而不是属性描述符,那就更好了(请参阅https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create

大多数时候,使用构造函数,例如在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(b.a); // "a"
A.a = "a++";
console.log(b.a); // return "a" instead of "a++" as b.a is overwritten in constructor

另外,不是很好,因为在这里你不能改变 A.a 的方式,而 b.a 也改变了,这是 IMO 原型继承的一个关键点。所以也许是这个?

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++"

没有给出预期的结果。而且,好吧,你不想写这个,对吧?

当然,您可以将创建放在 https://stackoverflow.com/a/16872315/1480587 所描述的构造函数中,但我认为这对于类语法来说仍然不那么好和简单。其实我在找这样的东西(类似于Kotlin's object declaration):

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

那么,你会推荐什么?有什么可以接近的吗?

特别是,如果您想使用一些封装并让私有对象成员对克隆对象不可见?

TypeScript 在这里提供了一个很好的解决方案吗?

选择 Kotlin?

还是应该回到基于类的继承,因为这是其他人都在使用和理解的?

【问题讨论】:

  • 实际上,在 ES6 中class 只是原型的语法糖,试试class X{}console.log(typeof X)
  • TypeScript 在这里提供了很好的解决方案吗? 是的! ES6 类。欢迎来到现代 JS。如果没有课程,您将很难让 TS 类型为您工作。
  • 我刚刚编辑了我的问题以明确我在寻找什么。从您的 cmets 看来,似乎没有使用真正的基于原型的继承。这实际上很可悲,因为我认为这是 JS 中非常好的事情之一。好吧,如果我们有一个很好的语法就好了。 @georg:IMO JS 中的构造函数对正确的原型继承没有帮助,所以我理解 ES6 类在这种情况下没有帮助。
  • 在前三个 sn-ps 中,第一个似乎不相关 — 它似乎在任何地方都不是有效的语法。第二个 sn-p 应该是关于 TypeScript 的,尽管我不确定这是实际的 TypeScript 语法还是过去曾有过。第三个 sn-p 缺少 b = "b"; 中的 this.。这个问题的重点是什么? ES6 还是 TypeScript?还是两者之间的互动?第一个 sn-p 可能应该被删除,而另一个 sn-ps 应该更新。

标签: javascript typescript ecmascript-6 prototype prototypal-inheritance


【解决方案1】:

一个简单的方法是Object.create

然后使用它。对于您想要做的事情,这似乎就足够了 - 两个对象,一个相互继承。您不需要任何构造函数来进行初始化,因此也不需要 class 语法。


顺便说一句,为了简化,当您不需要自定义属性描述符时,我不会使用 Object.create 的第二个参数。做吧

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

或者,在一个表达式中,

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

【讨论】:

    【解决方案2】:

    也许我可以使用这样一个简单的辅助函数:

    /**
     * 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);
    }
    

    这样,我就可以为所欲为:

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

    ....欢迎CMets和建议。

    【讨论】:

      【解决方案3】:

      或者,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++"
      

      【讨论】:

        【解决方案4】:

        我刚刚找到了我要找的东西:在 ES6 中 "object literals are extended to support setting the prototype at construction."真的简单方便!!

        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,
            // ...
        };
        

        【讨论】:

        • __proto__ 已弃用。
        • 哦,对了,babel 页面有(现在)对此的评论。
        猜你喜欢
        • 1970-01-01
        • 2021-01-27
        • 1970-01-01
        • 1970-01-01
        • 2012-08-25
        • 1970-01-01
        • 2018-05-25
        • 2014-09-21
        • 1970-01-01
        相关资源
        最近更新 更多