在 Javascript 中扩展原型 - 好方法吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28487909/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Extending prototypes in Javascript - good way?
提问by Jem
I want to validate that the approach I'm using is correct when it comes to extend a prototype - supposing "extend" is the right word.
我想验证我使用的方法在扩展原型时是否正确 - 假设“扩展”是正确的词。
This topic gets a lot of clones. I'm still trying to properly understand this topic...
这个话题得到了很多克隆。我仍在努力正确理解这个话题......
The purpose is: - to write clean and good code. - to avoid using frameworks, if possible plain Javascript. - get advice on the clean frameworks that don't twist JS to obtain class-enabled behaviors.
目的是: - 编写干净和良好的代码。- 避免使用框架,如果可能的话,使用纯 Javascript。- 获取有关不会扭曲 JS 以获得启用类的行为的干净框架的建议。
Here is the Parent prototype of my sandbox:
这是我的沙箱的父原型:
function Parent(){
}
Parent.prototype = {
"init":function(){
this.name = "anon";
},
"initWithParameters":function(parameters){
this.name = parameters.name ? parameters.name : "anon";
},
"talk": function(){
console.log('Parent is: ' + this.name);
}
}
Now the Child prototype - it adds a "position" property and redefines the behaviors:
现在是 Child 原型——它添加了一个“位置”属性并重新定义了行为:
function Child(){
Parent.call(this);
}
Child.prototype = new Parent;
Child.prototype.constructor = Child;
Child.prototype.init = function(){
Parent.prototype.call(this);
this.setPosition(0, 0);
}
Child.prototype.initWithParameters = function(parameters){
Parent.prototype.initWithParameters.call(this, parameters);
if(!this.position){
this.position = {x:0, y:0};
}
this.setPosition(parameters.pos.x, parameters.pos.y);
}
Child.prototype.setPosition = function(x, y){
this.position.x = x;
this.position.y = y;
}
Child.prototype.talk = function(){
console.log('Child is: ' + this.name + ' and location is: ' + this.position.x + ', ' + this.position.y);
}
Is this a good practice? Is there no shorthand to avoid writing "Child.prototype." when overriding a property (using a litteral maybe, like the Parent prototype is written).
这是一个好习惯吗?有没有避免写“Child.prototype”的简写。当覆盖一个属性时(可能使用文字,就像编写 Parent 原型一样)。
I know of J. Resig's Class/extend approach. But I'd rather use Javascript as the prototypical language it is, not make it work as a "class-like behaving class-less OO language".
我知道 J. Resig 的 Class/extend 方法。但我更愿意使用 Javascript 作为它的原型语言,而不是让它作为“类类行为无类 OO 语言”工作。
Thanks for your help :-)
谢谢你的帮助 :-)
回答by skay-
In general your approach will work but a better approach will be to replace:
一般来说,您的方法会奏效,但更好的方法是替换:
Child.prototype = new Parent;
with:
和:
Child.prototype = Object.create(Parent.prototype);
This way you don't need to call new Parent
, which is somewhat an anti-pattern. You could also define new properties directly as follows:
这样你就不需要调用new Parent
,这有点反模式。您还可以直接定义新属性,如下所示:
Child.prototype = Object.create(Parent.prototype, {
setPosition: {
value: function() {
//... etc
},
writable: true,
enumerable: true,
configurable: true
}
});
Hope this helps.
希望这可以帮助。
回答by Celeb
Your approach it is a good pure JavaScript approach. The only get away from tipping "Child.prototype" every time is to put it in a reference variable.
你的方法是一种很好的纯 JavaScript 方法。避免每次都提示“Child.prototype”的唯一方法是将其放入引用变量中。
Like:
喜欢:
var children = Child.prototype;
children.init = function(){ /*/someoverridecode*/}
But you are still doing the Child.prototype behind this. You can also define a function that does this for you, see bind of underscore, maybe it suits your needs.
但是你仍然在做这背后的 Child.prototype。您还可以定义一个为您执行此操作的函数,请参阅下划线的绑定,也许它适合您的需求。
Cheers
干杯
回答by Diniden
I may get deep fried for this suggestion as there are several articles that can argue against some practices in my example, but this has worked for me and works well for clean looking code, stays consistent, minifies well, operates in strict mode, and stays compatible with IE8.
我可能会因为这个建议而深陷其中,因为有几篇文章可以反对我的示例中的某些做法,但这对我有用,并且适用于干净的代码,保持一致,缩小良好,在严格模式下运行,并保持兼容IE8。
I also like utilizing the prototype methodology (rather than all of the 'extend' or 'apply' styles you see everywhere).
我也喜欢使用原型方法(而不是您随处可见的所有“扩展”或“应用”样式)。
I write out my classes like this. Yes, it looks a lot like an OOP language which you didn't want, but it still adheres to the prototypical model while holding similarities to other familiar languages which makes projects easier to navigate.
我像这样写出我的课程。是的,它看起来很像您不想要的 OOP 语言,但它仍然遵循原型模型,同时与其他熟悉的语言保持相似,这使得项目更易于导航。
This is my style I prefer :) I'm not saying it's the best, but it's so easy to read.
这是我喜欢的风格 :) 我不是说它是最好的,但它很容易阅读。
(function(ns) {
var Class = ns.ClassName = function() {
};
Class.prototype = new baseClass();
Class.constructor = Class;
var _public = Class.prototype;
var _private = _public._ = {};
Class.aClassProperty = "aValue";
Class.aClassMethod = function(params) {
}
_public.aMethod = function(params) {
_private.myMethod.call(this, "aParam");
Class.aClassMethod("aParam");
}
_private.myMethod = function(params) {
}
})({});
EDIT:
编辑:
I went ahead and converted your example this style just to show you what it would look like:
我继续将您的示例转换为这种样式,只是为了向您展示它的外观:
var namespace = {};
(function(ns) {
var Class = ns.Parent = function() {
};
var _public = Class.prototype;
var _private = _public._ = {};
_public.init = function() {
this.name = "anon";
}
_public.initWithParameters = function(parameters) {
this.name = parameters.name ? parameters.name : "anon";
}
_public.talk = function() {
console.log('Parent is: ' + this.name);
}
})(namespace);
(function(ns) {
var Class = ns.Child = function() {
this.position = {x:0, y:0};
};
Class.prototype = new ns.Parent();
Class.constructor = Class;
var _public = Class.prototype;
var _private = _public._ = {};
_public.init = function() {
_public.init.call(this);
this.setPosition(0, 0);
}
_public.initWithParameters = function(parameters) {
_public.initWithParameters.call(this, parameters);
this.setPosition(parameters.pos.x, parameters.pos.y);
}
_public.setPosition = function(x, y) {
this.position.x = x;
this.position.y = y;
}
_public.talk = function() {
console.log('Child is: ' + this.name + ' and location is: ' + this.position.x + ', ' + this.position.y);
}
})(namespace);
回答by Keni
Coming from google in 2019.
2019 年来自谷歌。
From the latest MDN documentation, the way to extend prototype is :
从最新的 MDN 文档来看,扩展原型的方法是:
function MyClass() {
SuperClass.call(this);
}
// inherit one class
MyClass.prototype = Object.create(SuperClass.prototype);
// mixin another
Object.assign(MyClass.prototype, {
//... you own prototype ...
});
// re-assign constructor
MyClass.prototype.constructor = MyClass;
回答by A-312
My example shows several things : Private variable, same parsing argument for the parant and child constructor, rewrite .toString() function try : ""+this
. :)
我的示例显示了几件事:私有变量,父子构造函数的相同解析参数,重写 .toString() 函数 try : ""+this
。:)
full example with doc :
完整的文档示例:
Output :
输出 :
Constructor arguments
构造函数参数
MM = new Parent("val of arg1", "val of arg2");
Child1 = new childInterface("1", "2");
Child2 = new child2Interface("a", "b");
console.log(MM + "args:", MM.arg1, MM.arg2);
// Parentargs: val of arg1 val of arg2
console.log(Child1 + "args:", Child1.arg1, Child1.arg2);
// childInterfaceargs: 1 2
console.log(Child2 + "args:", Child2.arg1, Child2.arg2);
// child2Interfaceargs: a b
Extend function in child class
在子类中扩展功能
MM.init();
// Parent: default ouput
Child1.init();
// childInterface: new output
Child2.init();
// child2Interface: default ouput
Increment variable
增量变量
MM.increment();
// Parent: increment 1
Child1.increment();
// childInterface: increment 1
Child2.increment();
// child2Interface: increment 1
Child2.increment();
// child2Interface: increment 2
MM.increment();
// Parent: increment 2
console.log("p", "c1", "c2");
// p c1 c2
console.log(MM.value, " " + Child1.value, " " + Child2.value);
// 2 1 2
Private variable
私有变量
MM.getHidden();
// Parent: hidden (private var) is true
MM.setHidden(false);
// Parent: hidden (private var) set to false
Child2.getHidden();
// child2Interface: hidden (private var) is true
MM.setHidden(true);
// Parent: hidden (private var) set to true
Child2.setHidden(false);
// child2Interface: hidden (private var) set to false
MM.getHidden();
// Parent: hidden (private var) is true
Child1.getHidden();
// childInterface: hidden (private var) is true
Child2.getHidden();
// child2Interface: hidden (private var) is false
Protected variable
受保护的变量
function Parent() {
//...
Object.defineProperty(this, "_id", { value: 312 });
};
console.log(MM._id); // 312
MM._id = "lol";
console.log(MM._id); // 312
/**
* Class interface for Parent
*
* @class
*/
function Parent() {
this.parseArguments(...arguments);
/**
* hidden variable
*
* @type {Boolean}
* @private
*/
var hidden = true;
/**
* Get hidden
*/
this.getHidden = () => {
console.log(this + ": hidden (private var) is", hidden);
}
/**
* Set hidden
*
* @param {Boolean} state New value of hidden
*/
this.setHidden = (state) => {
console.log(this + ": hidden (private var) set to", !!state);
hidden = state;
}
Object.defineProperty(this, "_id", { value: "312" });
}
Object.defineProperty(Parent.prototype, "nameString", { value: "Parent" });
/**
* Parse arguments
*/
Parent.prototype.parseArguments = function(arg1, arg2) {
this.arg1 = arg1;
this.arg2 = arg2;
};
/**
* Get className with `class.toString()`
*/
Parent.prototype.toString = function() {
return this.nameString;
};
/**
* Initialize middleware
*/
Parent.prototype.init = function() {
console.log(this + ": default ouput");
};
/**
* Increment value
*/
Parent.prototype.increment = function() {
this.value = (this.value) ? this.value + 1 : 1;
console.log(this + ": increment", this.value);
};
/**
* Class interface for Child
*
* @class
*/
function childInterface() {
this.parseArguments(...arguments);
}
// extend
childInterface.prototype = new Parent();
Object.defineProperty(childInterface.prototype, "nameString", { value: "childInterface" });
/**
* Initialize middleware (rewrite default)
*/
childInterface.prototype.init = function(chatClient) {
console.log(this + ": new output");
};
/**
* Class interface for Child2
*
* @class
*/
function child2Interface() {
this.parseArguments(...arguments);
}
// extend
child2Interface.prototype = new Parent();
Object.defineProperty(child2Interface.prototype, "nameString", { value: "child2Interface" });
//---------------------------------------------------------
//---------------------------------------------------------
MM = new Parent("val of arg1", "val of arg2");
Child1 = new childInterface("1", "2");
Child2 = new child2Interface("a", "b");
console.log(MM + " args:", MM.arg1, MM.arg2);
console.log(Child1 + " args:", Child1.arg1, Child1.arg2);
console.log(Child2 + " args:", Child2.arg1, Child2.arg2);
console.log(" ");
MM.init();
Child1.init();
Child2.init();
console.log(" ");
MM.increment();
Child1.increment();
Child2.increment();
Child2.increment();
MM.increment();
console.log("p", "c1", "c2");
console.log(MM.value, " " + Child1.value, " " + Child2.value);
console.log(" ");
MM.getHidden();
MM.setHidden(false);
Child2.getHidden();
MM.setHidden(true);
console.log(" ");
Child2.setHidden(false);
MM.getHidden();
Child1.getHidden();
Child2.getHidden();
console.log(MM._id);
MM._id = "lol";
console.log(MM._id);
回答by A-312
I normally do it like this. I use the class
operator now, but there's still a good way to do it in ES3.
我通常是这样做的。我class
现在使用操作符,但在 ES3 中仍然有一个很好的方法来做到这一点。
Using Node.js utilities
使用 Node.js 实用程序
Node.js includes a utility functionfor this very thing.
Node.js 包含一个实用程序函数来处理这个事情。
const { inherits } = require('util');
function SuperClass () {
this.fromSuperClass = 1;
}
function ExtendingClass () {
this.fromExtendingClass = 1;
}
inherits(ExtendingClass, SuperClass);
const extending = new ExtendingClass();
this.fromSuperClass; // -> 1
this.fromExtendingClass; // -> 1
The above has it's fair share of problems. It doesn't establish the prototypical chain, so it's considered semantically incompatiblewith the class
operator.
以上有很多问题。它不建立原型链,因此被认为在语义上与class
运算符不兼容。
Using the Object.create
API
使用Object.create
API
Otherwise, you can use Object.create
for this.
否则,您可以Object.create
为此使用。
function Person () {
this.person = true;
}
function CoolPerson () {
Person.call(this);
this.cool = true;
}
CoolPerson.prototype = Object.create(Person);
CoolPerson.prototype.constructor = CoolPerson;
Using a "helper function"
使用“辅助功能”
Please note that in the above example that uses the Object.create
API, if you do not call Person
(the "super class") in the CoolPerson
(the extending class), instance properties (and optionally initialization) will not be applied when instantiating Person
.
请注意,在上面使用Object.create
API 的示例中,如果您没有Person
在CoolPerson
(扩展类)中调用(“超类”),则实例化时将不会应用实例属性(以及可选的初始化)Person
。
If you want to be more "elegant", you could even create a helper function for this, which might be easier for you.
如果你想更“优雅”,你甚至可以为此创建一个辅助函数,这对你来说可能更容易。
function extend(BaseClassFactory, SuperClass) {
const BaseClass = BaseClassFactory(SuperClass.prototype, SuperClass);
BaseClass.prototype = Object.assign(BaseClass.prototype, Object.create(SuperClass));
BaseClass.prototype.constructor = BaseClass;
return BaseClass;
}
function SuperClass() {
this.superClass = true;
}
SuperClass.prototype.method = function() {
return 'one';
}
const ExtendingClass = extend((Super, SuperCtor) => {
function ExtendingClass () {
SuperCtor.call(this);
this.extending = true;
}
// Example of calling a super method:
ExtendingClass.prototype.method = function () {
return Super.method.call(this) + ' two'; // one two
}
return ExtendingClass;
}, SuperClass);
const extending = new ExtendingClass();
extending.method(); // => one two
Using ES6's class
operator
使用 ES6 的class
操作符
There's a new class
operator in JavaScript that's been released in JavaScript that may make this whole experience more expressive.
class
JavaScript 中有一个新的运算符,它已在 JavaScript 中发布,可能会使整个体验更具表现力。
class SuperClass {
constructor() {
this.superClass = true;
}
method() {
return 'one';
}
}
class ExtendingClass extends SuperClass {
constructor() {
super();
this.extending = true;
}
method() {
// In this context, `super.method` refers to a bound version of `SuperClass.method`, which can be called like a normal method.
return `${super.method()} two`;
}
}
const extending = new ExtendingClass();
extending.method(); // => one two
Hope this helps.
希望这可以帮助。
回答by Rootical V.
These are the ways I usually do it:
这些是我通常的做法:
Using a helper function:
使用辅助函数:
/**
* A clone of the Node.js util.inherits() function. This will require
* browser support for the ES5 Object.create() method.
*
* @param {Function} ctor
* The child constructor.
* @param {Function} superCtor
* The parent constructor.
*/
function inherits (ctor, superCtor) {
ctor.super_ = superCtor;
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false
}
});
};
Then you might simply do:
那么你可以简单地做:
function ChildClass() {
inherits(this, ParentClass);
// If you want to call parent's constructor:
this.super_.apply(this, arguments);
}
Extending prototype using Lodash
使用 Lodash 扩展原型
_.assign(ChildClass.prototype, {
value: key
});
Or just give ES6 a chance!
或者只是给 ES6 一个机会!
class ParentClass {
constructor() {
var date = new Date();
var hours = date.getHours();
var minutes = date.getMinutes();
var seconds = date.getSeconds();
this.initializeTime = hours + ':' + minutes + ':' + seconds;
}
}
class ChildClass extends ParentsClass {
constructor() {
super();
console.log(this.initializeTime);
}
}