理解为什么真正的原型继承优于经典/伪原型继承以及为什么我不应该使用“新”原型继承。 [英] Understanding why true prototypal inheritance is better than classical/pseudo prototypal inheritance and why i shouldn't use "new"

查看:91
本文介绍了理解为什么真正的原型继承优于经典/伪原型继承以及为什么我不应该使用“新”原型继承。的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

阅读 Aadit M Shah 中的一些文章,例如为什么原型继承很重要
停止在JavaScript中使用构造函数来自Eric Elliott我认为我理解他们所有的论点,在理论上。但实际上我没有看到这种模式的真正优势。

Reading some articles from Aadit M Shah like Why Prototypal Inheritance Matters or Stop Using Constructor Functions in JavaScript from Eric Elliott i think i understand all of their arguments, in theoric. But in practice i don't see the real advantages of this pattern.

让我们看一下两个片段中的两个实现来进行继承。

Let's take a look two implementations from two snippets to make inheritance.


  1. 第一个是使用 augment.js 这是来自Aadit M Shah的剧本

  2. 在这个例子中,我们将使用此脚本。也是由Aadit M Shah制作的。

  1. First one is using augment.js it's a script from Aadit M Shah
  2. On this example we are going to use this script. Is made it by Aadit M Shah as well.

实施1

    var AugmentPerson = Object.augment(function() {
      this.constructor = function(name) {
          this.name = name;
      };
      this.setAddress = function(country, city, street) {
          this.country = country;
          this.city = city;
          this.street = street;
      };
    });
    var AugmentFrenchGuy = AugmentPerson.augment(function(base) {
      this.constructor = function(name) {
          base.constructor.call(this,name);
      };
      this.setAddress = function(city, street) {
          base.setAddress.call(this, "France", city, street);
      };
    });
    var AugmentParisLover = AugmentFrenchGuy.augment(function(base) {

      this.constructor = function(name) {
          base.constructor.call(this, name);
      };

      this.setAddress = function(street) {
          base.setAddress.call(this, "Paris", street);
      };
    });
    var t = new AugmentParisLover("Mary");
    t.setAddress("CH");
    console.log(t.name, t.country, t.city, t.street); //Mary France Paris CH

在这个例子中,我们使用函数构造函数而不是直接从对象继承。

In this example we are using function constructors instead of inherit directly from a object.

实施2

    var CreatePerson = {
        create: function (name) {
            this.name = name;
            return this.extend();
        },
        setAddress: function(country, city, street) {
             this.country = country;
             this.city = city;
             this.street = street;
        }
    };
    var CreateFrenchGuy  = CreatePerson.extend({
        create: function (name) {
            return CreatePerson.create.call(this,name);
        },
        setAddress: function(city, street) {
            CreatePerson.setAddress('France', city, street);
        }
    });
    var CreateParisLover  = CreateFrenchGuy.extend({
        create: function (name) {
            return CreateFrenchGuy.create.call(this,name);
        },
        setAddress: function(street) {
            CreateFrenchGuy.setAddress('Paris', street);
        }
    });

    var t = CreateParisLover.create("Mary");
    t.setAddress("CH");
    console.log(t.name, t.country, t.city, t.street); //Mary France Paris CH

老实说,我正试图看到第二个好处实现。但我不能。我看到的唯一一点是更灵活,因为我们可以使用apply创建实例:

To be honest, i'm trying to see the benefits of the second implementation. But i am not able. The only point i see is more flexible is because we can create the instance using apply:

var t = CreateParisLover.create.apply(CreateParisLover, [Mary]);

这给了我们更大的灵活性,这是真的。但是我们也可以使用这个来做同样的事情:

This give us more flexibility, it's true. But we can do the same with this:

  Function.prototype.new = function () {
     function functor() { return constructor.apply(this, args); }
     var args = Array.prototype.slice.call(arguments);
     functor.prototype = this.prototype;
     var constructor = this;
     return new functor;
  };

然后我们可以:

var t = AugmentParisLover.new.apply(AugmentParisLover, ["Mary"]);

灵活性,可重用性,难度方面的真正好处是什么...
因为如果你检查两种情况的表现。 Object.create()比新的慢得多: http://jsperf.com/ inheritance-using-create-vs-new
我很困惑。

What is the real benefits in terms of flexibility, re-usability, difficulty... Because if you check the performance of both cases. Object.create() is pretty much slower than new: http://jsperf.com/inheritance-using-create-vs-new I'm confusing.

推荐答案

类似的问题有之前被多次询问和回答。请参阅:

Similar questions have been asked and answered many times before. See:

构造函数功能与工厂功能
古典与原型继承

更多学习:
https://medium.com/javascript-scene/3-different-kinds-of- prototypal-inheritance-es6-edition-32d777fa16c9#.s0r3i5w6t
http://vimeo.com/ 69255635

tl; dr


  • 构造商打破开放/封闭原则

  • 构造函数将对象创建与对象初始化混淆 - sometim阻碍代码的可重用性

  • 构造函数看起来有点像类,这令人困惑。 JavaScript不需要类(我建议避免使用ES6中的class关键字)。 JavaScript比类更好。

  • 原型委托和动态对象扩展(连接继承)的组合比经典继承更强大,更灵活。

  • 在JavaScript中,Constructor.prototype和实例之间的连接是脆弱的和不可靠的。使用构造函数可以提供工作实例的错觉,当它在执行上下文中不起作用时可能会造成混淆,或者如果构造函数原型被换出则不起作用。

  • 交换构造函数更难实现原型。您可能希望这样做以启用多态对象构造。对于工厂,热插拔原型很容易,可以使用.call()和.apply()来完成。

  • Constructors break the open / closed principle
  • Constructors conflate object creation with object initialization - sometimes hampering the reusability of the code
  • Constructors look a bit like classes, which is confusing. JavaScript doesn't need classes (I recommend avoiding the class keyword coming in ES6). JavaScript has something better than classes.
  • The combination of prototype delegation and dynamic object extension (concatenative inheritance) is much more powerful and flexible than classical inheritance.
  • The connections between the Constructor.prototype and instances are frail and untrustworthy in JavaScript. Using constructors can provide the illusion of a working instanceof, which could be confusing when it doesn't work across execution contexts, or doesn't work if the constructor prototype gets swapped out.
  • Swapping out the prototype is harder with constructors. You may want to do that to enable polymorphic object construction. With factories, hot swappable prototypes are easy, and can be done using .call() and .apply().

编辑 - 回应OP发布的答案:

关于Object.create的最好的事情是它是一个专用的低级工具,允许您创建新对象并在不使用构造函数的情况下为其分配任何原型。有很多理由可以避免构造函数,这里有深入介绍:构造函数vs工厂功能

The best thing about Object.create is that it's a dedicated, low-level tool that lets you create a new object and assign any prototype you want to it without using a constructor function. There are lots of reasons to avoid constructors, covered in-depth here: Constructor function vs Factory functions


  1. 用于演示代码较少的代码并未真正证明其差异在经典和原型继承之间。更典型的示例可能如下所示:

经典

var Animal = function Animal(name) {
  this.name = name;
};

Animal.prototype.walk = function walk() {
  console.log(this.name + ' goes for a walk.');
};

var Rabbit = function Rabbit(/* name */) {
  // Because construction and instantiation are conflated, you must call super().
  Animal.prototype.constructor.apply(this, arguments);
};

// Classical inheritance is really built on top of prototypal inheritance:
Rabbit.prototype = Object.create(Animal.prototype);

// Fix the .constructor property:
Rabbit.prototype.constructor = Rabbit;

Rabbit.prototype.jump = function jump() {
  console.log(this.name + ' hops around a bit.');
};

var myRabbit = new Rabbit('Bunny George');

myRabbit.walk();
// Bunny George goes for a walk.

Prototypal

var animalMethods =  {
  walk: function walk() {
    console.log(this.name + ' goes for a walk.');
  }
};

var animal = function animal(name) {
  var instance = Object.create(animalMethods);
  instance.name = name;
  return instance;
};

var rabbitMethods = {
  jump: function jump() {
    console.log(this.name + ' hops around a bit.');
  }
};

var rabbit = function rabbit(name) {
  var proto = rabbitMethods;

  // This is more commonly done like mixin({}, animalMethods, rabbitMethods);
  // where mixin = $.extend, _.extend, mout.object.mixIn, etc... It just copies
  // source properties to the destination object (first arg), where properties from
  // the last argument override properties from previous source arguments.
  proto.walk = animalMethods.walk;
  var instance = Object.create(rabbitMethods);

  // This could just as easily be a functional mixin,
  // shared with both animal and rabbit.
  instance.name = name;
  return instance;
};

var rabbit2 = rabbit('Bunny Bob');

rabbit2.walk();
// Bunny Bob goes for a walk.

所需的代码量非常相似,但对我而言,原型是多么明显事情正在发生,它也更加灵活,并且没有第一个例子的经典继承关节炎行李。

The amount of code required is pretty similar, but to me, it's a LOT more clear what the prototypal stuff is doing, and it's also a lot more flexible, and has none of the classical inheritance arthritic baggage of the first example.

这篇关于理解为什么真正的原型继承优于经典/伪原型继承以及为什么我不应该使用“新”原型继承。的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆