可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
The fact that Javascript uses functions to create objects was confusing to me at first. An example like this is often used to highlight how prototypes work in 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');
Is it possible to use prototypes without using new FunctionName()
? I.e. something like this:
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());
This does not use functions and new
to create objects. Rather, it creates objects directly.
Is this possible without deprecated features like Object.__proto__
or experimental functions like Object.setPrototypeOf()
?
回答1:
Object.create
gets you the behavior that you are looking for, but you have to call it instead of 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());
Alternatively, you can use the new ES 2015's __proto__
property to set the prototype at declaration time:
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());
Some additional notes
You should add the methods to Car.prototype
not inside of the constructor unless you need the private state (this way there is only one instance of the setModel
method, rather than one instance of the method per instance of the class):
function Car() {}
Car.prototype.setModel = function(model) { this.model = model; };
Car.prototype.getModel = function(model) { return this.model; };
You can get around the new Car
oddness with Object.create
even with constructor functions:
function Bus {}
Bus.prototype = Object.create(Car);
回答2:
Crockford has quite a good chapter on this subject in The Good Parts.
In it he points out one big flaw with using new
on constructor functions:
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.
(You may already be aware of this, hence your question, but it's worth reiterating)
His proposed solution is to recommend a home rolled Object.create
(The Good Parts predates ES5 by a bit, but the idea is the same as the native version mentioned in the other answers).
if (typeof Object.create !== 'function') {
Object.create = function (o) {
var F = function () { };
F.prototype = o;
return new F();
};
}
Object.create
takes an instance of an object to use as the prototype and returns an instance.
The example used is very similar to your "something like this" example (except Crocky uses mammals and cats instead of cars and buses):
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());
回答3:
You can use Object.create()
to achieve. Here is a crude example of prototypal inheritance using 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:
Prototypes allow you to make new object instances from other object instances. The new instance stands on its own and can be changed however you like.
var bus = Object.clone(Car.prototype);
bus.number = '3345';
bus.route = 'Chicago West Loop';
bus.setModel('A bus');
回答5:
To use an instance as prototype in order to inherit shows a lack of understanding what prototype is. Prototype shares members and a mutable instance member of Parent on the prototype of Child gets you very unexpected behavior. Setting Car.prototype to Object.create(Bus.prototype) has already covered by others, if you want to see the role of the constructor and the role of the prototype maybe the following answer can help: https://stackoverflow.com/a/16063711/1641941
To demonstrate what could go wrong let's introduce an instance specific mutable member called passengers
we would like Bus to initialize this so we can't use Jeff's example as that only takes care of the prototype part. Let's use constructor functions but create an instance of Bus for Car.prototype.
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
This error could be resolved by shadowing instance properties by re using Parent constructor (Bus.call(this... as provided later) but Car.prototype still has a passengers member that has no business being there.
If you want to protect agains people forgetting "new" you can do the following:
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:
One approach - create a Car and use Object.create to make a copy and then extend Bus functionality as needed
(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);
}());