【问题标题】:extend or inherit an object prototypes when calling a native prototype method调用原生原型方法时扩展或继承对象原型
【发布时间】:2016-03-03 08:29:26
【问题描述】:

我一直在阅读 Inheritance and the prototype chain 和某处它说:

不良做法:扩展原生原型

一个经常使用的错误特性是扩展 Object.prototype 或 其他内置原型之一。

这种技术称为猴子补丁并破坏封装。 虽然被 Prototype.js 等流行框架使用,但仍有 没有充分的理由将内置类型与额外的 非标准功能。

扩展内置原型的唯一充分理由是向后移植 较新的 JavaScript 引擎的特性;例如 Array.forEach, 等等

在一个真实的例子中,我试图做的是一个角度服务来处理 http 请求,它返回一个带有少量属性的简单对象实例和两个方法 findAll()findOne(id),我将使用其中一个(并且只有一次)在我的 ui-router 的解析方法中:

resolve: {
    Resource: 'Resource',
    allImages: function(Resource) {
      var images = new Resource('images');
      return images.findAll();
    },
    singleImage: function(Resource) {
      var image = new Resource('images');
      return image.findOne(4);
    }
},

根据我将调用的两种方法中的哪一种,我尝试将整个实例 Resource 扩展(甚至替换)为另一个预定义实例 ItemCollection,其中每个实例都有自己的结构和方法,所以在我的控制器中我可以做类似的事情:

if (allImages.existNext()) allImages.nextPage();

var currentPage = allImages.meta.currentPage,
    collection = allImages.data;

singleImage.url = 'abc';
singleImage.save(); 

现在,到目前为止,我发现的唯一可行的解​​决方案(由一个与 http 请求无关的最小示例表示)是这样的:

var myApp = angular.module('myApp',[]);

myApp.factory('Animal', Animal);

function Animal() {

    var Cat = function() {
        this.prefferedFood = 'fish';
    };

    Cat.prototype.sayMiau = function() {
        console.log('Miau!')
    };

    var Dog = function(name) {
        this.dogName = name;
    };

    Dog.prototype.sayGrr = function() {
        console.log('Grrr!')
    };

    function Animal(age) {
        this.age = age;
    }

    Animal.prototype = {
        makeItCat: function() {},
        makeItDog: function(name) {
           Dog.call(this, name);
           this.__proto__ = Dog.prototype;
        },
    };

    return Animal;
}

所以在 Controller 里面我可以做:

var Dan = new Animal(7);
Dan.makeItDog('Dan');
Console.log(Dan.age); // outputs "7"
Console.log(Dan.dogName); // outputs "Dan"
Dan.sayGrr(); // outputs "Grrr!"

正如您在jsfiddle 中看到的那样,它的工作原理。

问题是:

正确吗?我没有破坏 Object.prototype 应该如何工作或失去它应该提供的性能增益?有更好的方法吗?比如可能使用 angular.extend 或者可能根本不扩展(或替换)原型,而是使用类似的东西:

var Dog = function(name) {
    this.dogName = name;
    this.sayGrr = function(string) {
      console.log('Grrr!')
    }
};
...
Animal.prototype = {
    makeItCat: function() {},
    makeItDog: function(name) {
      Dog.call(this, name);
    },
};

【问题讨论】:

    标签: javascript angularjs prototype


    【解决方案1】:

    我认为你这样做的方式可能会奏效。我不确定您所做的工厂实施。我曾经做过类似的事情可能会有所帮助:

    查看工作中的 jsFiddle here

    这是您的代码稍作修改:

    var myApp = angular.module('myApp', []);
    
    myApp.factory('AnimalFactory', AnimalFactory);
    
    function AnimalFactory() {
    
      // This is the main class
      var Animal = function(age, fullName) {
    
        this.age = age;
        this.fullName = fullName;
    
        this.eat = function() {
          console.log('I am eating');
        };
    
      };
    
      // Dog should inherit from Animal 
      var Dog = function(age, fullName) {
    
        // Calling the animal constructor
        Animal.call(this, age, fullName);
    
        // Augmenting object adding a new method
        this.bark = function() {
          console.log('I am ' + this.fullName + ' and I bark Woof woof');
        };
      };
    
      // Setting the Dog prototype to Animal
      Dog.prototype = Object.create(Animal);
    
      var Cat = function(age, fullName) {
    
        // Calling the animal constructor
        Animal.call(this, age, fullName);
    
        // Augmenting object adding a new method
        this.meow = function() {
          console.log('I am ' + this.fullName + ' and I meow');
        };
      };
    
      // Setting the Cat prototype to Animal
      Cat.prototype = Object.create(Animal);
    
      function createDog(age, fullName) {
        return new Dog(age, fullName);
      }
    
      function createCat(age, fullName) {
        return new Cat(age, fullName);
      }
    
      // Interface returned to use factory
      return {
        createDog: createDog,
        createCat: createCat
      };
    }
    
    function MyCtrl($scope, AnimalFactory) {
    
      var dan = AnimalFactory.createDog(7, 'Dan');
      dan.bark();
    
      console.log(dan);
    
      $scope.dan = dan;
    }
    

    我认为上面的代码在你的类之间有一个更清晰的原型继承实现。让我知道您的想法,以便我们改进它。

    【讨论】:

    • 不错的答案。我还没有在我的真实示例中尝试过,但是在阅读了this 之后,我很确定直接设置__proto__ 不是一个好主意。在第一次调用服务时设计继承布局和链接原型,然后在需要时使用new 关键字返回一个实例应该比我的解决方案更干净。感谢分享!
    • 没错。我认为您的示例可能有效,但此解决方案提供了一种更清洁的方法。我很高兴能帮助你@salemOuerdani。问候!
    猜你喜欢
    • 1970-01-01
    • 2016-07-29
    • 1970-01-01
    • 2014-05-08
    • 2022-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-07
    相关资源
    最近更新 更多