不使用“new”关键字,是否可以使用Javascript原型?

3

一开始,我对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() 的情况下使用原型?比如像这样:

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() 的情况下实现呢?


将原型设置为new对象是常见的吗?例如,请参阅有关此的MDN文档:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript - Dave Newton
6个回答

2
"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());

或者,您可以在声明时使用新的 ES 2015 的 __proto__ 属性来设置原型:
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而不是构造函数内部。为什么呢? - Leo Jiang
1
为了只有一个函数实例而不是每个实例一个。 - Sean Vieira

1

Crockford在JavaScript语言精粹一书中对这个主题有很好的章节。

他指出在构造函数上使用new存在一个大缺陷:

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

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

他提出的解决方案是推荐一个自制的Object.create(《JavaScript语言精粹》早于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一样有效,没有任何理由不使用构造函数。 - HMR
你和Crockford提供的示例没有初始化特定于实例的成员(请参见我的答案,其中包含名为passenger的成员)。您可以使用一个init函数来代替构造函数,但基本上会以稍微不同的语法执行相同的操作。 - HMR
当你的对象需要初始化时,没有理由不使用新关键字。Object.create也要慢得多。 - plalx

0

你可以使用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());

0

使用实例作为原型以进行继承显示了对原型的理解不足。在 Child 的原型上共享成员和可变实例成员 Parent 的行为会让你感到非常意外。如果你想了解构造函数和原型的作用,可以参考以下答案:https://dev59.com/J2Qo5IYBdhLWcg3wbe5K#16063711。已经有其他人将 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

这个错误可以通过重用父构造函数(如后面提供的Bus.call(this...))来遮蔽实例属性,但是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

0

原型允许您从其他对象实例创建新的对象实例。这个新实例是独立的,可以根据您的喜好进行更改。

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

0
一种方法是创建一个汽车,使用Object.create来制作副本,然后根据需要扩展公交车功能。

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

}());


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接