【问题标题】:Is it possible to using Javascript prototypes without using the "new" keyword?是否可以在不使用“new”关键字的情况下使用 Javascript 原型?
【发布时间】:2015-02-17 22:41:26
【问题描述】:

Javascript 使用函数来创建对象的事实起初让我感到困惑。像这样的例子经常被用来强调原型在 Javascript 中是如何工作的:

function Car(){
  this.setModel=function(model){
    this.model=model;
  }
  this.getModel=function(){
    return this.model;
  }
}

function Bus(){}
Bus.prototype=new Car();

var obj=new Bus();
obj.setModel('A Bus');
alert(obj.getModel('A Bus');

是否可以不使用new FunctionName() 使用原型? IE。像这样:

var Car={
  setModel:function(model){
    this.model=model;
  },
  getModel:function(){
    return this.model
  }
}

var Bus={
  prototype:Car;
};

var obj=Bus;
obj.setModel('A Bus');
alert(obj.getModel());

这不使用函数和new 来创建对象。相反,它直接创建对象。

如果没有像 Object.__proto__ 这样的弃用功能或像 Object.setPrototypeOf() 这样的实验性功能,这是否可能?

【问题讨论】:

标签: javascript oop prototype


【解决方案1】:

Object.create 为您提供您正在寻找的行为,但您必须调用它而不是 new

// Using ES6 style methods here
// These translate directly to
// name: function name(params) { /* implementation here */ }
var Car = {
 setModel(model) {
    this.model = model;
  },
  getModel() {
    return this.model
  }
};

var Bus = Object.create(Car);

var obj = Object.create(Bus);
obj.setModel('A Bus');
alert(obj.getModel());

或者,您可以使用new ES 2015's __proto__ property 在声明时设置原型:

var Bus = {
  __proto__: Car
};

// You still need Object.create here since Bus is not a constructor
var obj = Object.create(Bus);
obj.setModel('A Bus');
alert(obj.getModel());

一些补充说明

您应该将方法添加到 Car.prototype 而不是在构造函数内部,除非您需要私有状态(这样只有一个 setModel 方法的实例,而不是一个类的每个实例的方法):

function Car() {}
Car.prototype.setModel = function(model) { this.model = model; };
Car.prototype.getModel = function(model) { return this.model; };

即使使用构造函数,您也可以使用 Object.create 绕过 new Car 的奇怪之处:

function Bus {}
Bus.prototype = Object.create(Car);

【讨论】:

  • “你应该将方法添加到 Car.prototype 而不是在构造函数内部”这是为什么?
  • 为了只有一个函数实例而不是每个实例一个。
【解决方案2】:

Crockford 在The Good Parts 中有一篇关于这个主题的精彩章节。

他在其中指出了在构造函数上使用new 的一大缺陷:

更糟糕的是,使用构造函数存在严重的危险。如果在调用构造函数的时候忘记使用new前缀,那么this就不会绑定到一个新的对象上……没有编译警告,也没有运行时警告。

(您可能已经意识到这一点,因此您的问题,但值得重申)

他提出的解决方案是推荐一个家庭滚动的Object.create(The Good Parts 比 ES5 早一点,但想法与其他答案中提到的原生版本相同)。

if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        var F = function () { };
        F.prototype = o;
        return new F();
    };
}

Object.create 将一个对象的实例用作原型并返回一个实例。

使用的示例与您的“类似这样”的示例非常相似(除了 Crocky 使用哺乳动物和猫而不是汽车和公共汽车):

var car = {
  setModel:function(model){
    this.model=model;
  },
  getModel:function(){
    return this.model
  }
};

var bus = Object.create(car);

bus.setModel('A Bus');
alert(bus.getModel());

【讨论】:

  • Crockford 从未能够产生一个正确工作的“经典继承”示例;创建一个 Parent 的实例用作 Child 的原型,甚至声称 Parent 构造函数不能被重用。忘记 new 与忘记 var 一样有效,并且没有任何理由不使用构造函数。
  • 您和 Crockford 给出的示例未初始化实例特定成员(请参阅我对名为乘客的成员的回答)。您可以将此模式与将接管构造函数的 init 函数一起使用,但基本上最终会使用稍微不同的语法来做同样的事情。
  • 当您的对象需要初始化时,没有理由不使用 new 关键字。 Object.create 也慢得多。
【解决方案3】:

可以使用Object.create()来实现。这是使用 this 的原型继承的粗略示例:

// Car constructor
var Car = function() {};
Car.prototype = {
    setModel: function(){},
    getModel: function(){}
};

// Bus constructor
var Bus = function() {
    Car.call(this); // call the parent ctor
};

Bus.prototype = Object.create(Car.prototype); // inherit from Car

var my_bus = new Bus(); // create a new instance of Bus
console.log(my_bus.getModel());

【讨论】:

    【解决方案4】:

    原型允许您从其他对象实例创建新的对象实例。新实例独立存在,可以随意更改。

    var bus = Object.clone(Car.prototype);
    bus.number = '3345';
    bus.route = 'Chicago West Loop';
    bus.setModel('A bus');
    

    【讨论】:

      【解决方案5】:

      为了继承而使用实例作为原型表明缺乏对原型是什么的理解。 Prototype 在 Child 的原型上共享成员和 Parent 的可变实例成员会给你带来非常意想不到的行为。将 Car.prototype 设置为 Object.create(Bus.prototype) 已经被其他人介绍过了,如果你想看看构造函数的作用和原型的作用,也许下面的答案会有所帮助:https://stackoverflow.com/a/16063711/1641941

      为了演示可能出错的地方,让我们引入一个名为 passengers 的特定于实例的可变成员,我们希望 Bus 对其进行初始化,因此我们不能使用 Jeff 的示例,因为它只处理原型部分。让我们使用构造函数,但为 Car.prototype 创建一个 Bus 实例。

      var Bus = function Bus(){
        this.passengers=[];
      }
      var Car = function Car(){};
      Car.prototype = new Bus()
      var car1=new Car();
      var car2=new Car();
      car1.passengers.push('Jerry');
      console.log(car2.passengers);//=['Jerry']
        //Yes, Jerry is a passenger of every car you created 
        //and are going to create
      

      可以通过重新使用 Parent 构造函数(Bus.call(this... 稍后提供)隐藏实例属性来解决此错误,但 Car.prototype 仍然有一个没有业务的乘客成员。

      如果您想防止人们忘记“新”,您可以执行以下操作:

      function Car(){
        if(this.constructor!==Car){
          return new Car();
        }
        //re use parent constructor
        Bus.call(this);
      }
      //Example of factory funcion
      Car.create=function(arg){
        //depending on arg return a Car
        return new Car();
      };
      Car.prototype = Object.create(Bus.prototype);
      Car.prototype.constructor = Car;
      
      var car1 = Car();//works
      var car2 = new Car();//works
      var car3 = Car.create();//works
      

      【讨论】:

        【解决方案6】:

        一种方法 - 创建 Car 并使用 Object.create 进行复制,然后根据需要扩展 Bus 功能

        (function () {
        
          "use strict";
          
           var setModel = function (model) {
            this.model = model;
          };
        
          var getModel = function () {
            return this.model;
          };
        
          var iAmBus = function () {
            alert("only a bus!");
          };
        
          var Car = {
              setModel: setModel,
              getModel: getModel
            };
        
          var Bus = Object.create(Car);
        
          // own bus property, not in the car
          Bus.iAmBus = iAmBus;
        
          var bus = Object.create(Bus);
          bus.setModel('A Bus');
          alert(bus.getModel());
        
          var car = Object.create(Car);
          car.setModel('A Car');
          alert(car.getModel());
        
          //inspect bus and car objects, proving the different object structure
          console.log(bus);
          console.log(car);
          console.log(Bus);
          console.log(Car);
        
        }());

        【讨论】:

          猜你喜欢
          • 2010-12-25
          • 1970-01-01
          • 1970-01-01
          • 2010-12-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多