是否可以在不使用 "new" 关键字的情况下使用 Javascript 原型?

Is it possible to using Javascript prototypes without using the "new" keyword?

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() 这样的实验功能,这是否可能?

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 方法,而不是每个 class 实例一个方法实例):

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);

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

// 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());

原型允许您从其他对象实例创建新的对象实例。新实例独立存在,可以根据需要进行更改。

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

Crockford 在 The Good Parts.

中有一个关于这个主题的很好的章节

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

Even worse, there is a serious hazard with the use of constructor functions. If you forget to use the new prefix when calling a constructor function, then this will not be bound to a new object... There is no compile warning, and there is no runtime warning.

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

他提出的解决方案是推荐一个home rolled 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 使用一个对象实例作为原型,returns 一个实例。

使用的示例与您的 "something like this" 示例非常相似(除了 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());

一种方法 - 创建一个 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);

}());

为了继承而使用实例作为原型,说明对原型是什么缺乏理解。 Prototype 共享成员,并且 Parent 的可变实例成员在 Child 的原型上会让您出现非常意外的行为。将 Car.prototype 设置为 Object.create(Bus.prototype) 已经被其他人覆盖,如果你想看到构造函数的作用和原型的作用,也许下面的答案可以帮助:

为了演示可能出错的地方,让我们引入一个名为 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(这...稍后提供)但是 Car.prototype 仍然有一个乘客成员没有业务那里。

如果你想防止人们再次忘记 "new",你可以执行以下操作:

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