What techniques can be used to define a class in JavaScript, and what are their trade-offs?
我更喜欢在大型项目中使用OOP,比如我现在正在研究的项目。我需要用JavaScript创建几个类,但如果我没有弄错的话,至少有两种方法可以做到这一点。语法是什么?为什么要这样做?
我想避免使用第三方库——至少在一开始是这样。在寻找其他答案时,我发现了一篇关于用Javascript进行面向对象编程的文章,第一部分:继承-DocJavascript,它讨论了用Javascript进行面向对象编程。有更好的继承方式吗?
以下是不使用任何外部库的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // Define a class like this function Person(name, gender){ // Add object properties like this this.name = name; this.gender = gender; } // Add methods like this. All Person objects will be able to invoke this Person.prototype.speak = function(){ alert("Howdy, my name is" + this.name); }; // Instantiate new objects with 'new' var person = new Person("Bob","M"); // Invoke methods like this person.speak(); // alerts"Howdy, my name is Bob" |
现在真正的答案要比这复杂得多。例如,JavaScript中没有类。javascript使用基于
此外,还有许多流行的JavaScript库,它们都有自己的风格来近似JavaScript中类功能。您至少要检查一下原型和jquery。
决定哪一个是"最好的",是对堆栈溢出发起神圣战争的一个好方法。如果你正在着手一个更大的JavaScript重的项目,那绝对值得学习一个流行的库并按他们的方式来做。我是一个原型,但堆栈溢出似乎倾向于jquery。
因为只有"一种方法",不依赖于外部库,所以我写的方法差不多就是它。
在JavaScript中定义类的最佳方法是不定义类。
说真的。
对象方向有几种不同的风格,其中一些是:
- 基于类的OO(首先由Smalltalk介绍)
- 基于原型的OO(首先由Self介绍)
- 基于多方法的OO(我认为首先由CommonLoops引入)
- 基于谓词的OO(不知道)
可能还有其他我不认识的人。
JavaScript实现了基于原型的OO。在基于原型的OO中,通过复制其他对象(而不是从类模板实例化)来创建新对象,方法直接存在于对象中而不是类中。继承是通过委托完成的:如果一个对象没有方法或属性,它将在其原型(即从中克隆的对象)上进行查找,然后在原型的原型上进行查找,依此类推。
换句话说:没有课程。
实际上,javascript对该模型有一个很好的调整:构造器。可以说,您不仅可以通过复制现有对象来创建对象,还可以"凭空"构建它们。如果使用
现在,javascript是一种非常强大的语言,所以如果您愿意的话,很容易在javascript中实现一个基于类的OO系统。但是,如果你真的需要它,而不仅仅是因为Java是这样做的,你就应该这样做。
es2015类
在你可以使用AISI规范es2015,which is just the class的句法糖over the prototype system。P></
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Person { constructor(name) { this.name = name; } toString() { return `My name is ${ this.name }.`; } } class Employee extends Person { constructor(name, hours) { super(name); this.hours = hours; } toString() { return `${ super.toString() } I work ${ this.hours } hours.`; } } |
利益
is that the main和静态分析工具easier this to find /目标表。它也easier is for其他对象类的语言,从语言的使用在polyglot as to the。P></caveats
好wary of its current的局限性。一个是私人性质的,使用前必须weakmaps度假村的符号。在未来的新闻稿中,将包括扩大Most likely to be missing,这些特点。P></支持
浏览器支持不好的时刻(at the甚近每一个人除了负载模式你可以使用IE浏览器),但现在,这些特点与类transpiler巴贝尔。P></资源
- 在ECMAScript中6(最终的语义)
- 什么?等待。真的吗?哦,不!(后es6 about类和隐私)
- –兼容性表类
- 巴贝尔–类
我更喜欢用丹尼尔·X·摩尔的《江户记》1(2)。这是一个提供诸如真实实例变量、基于特征的继承、类层次结构和配置选项等好处的规程。下面的例子说明了使用真正的实例变量,我认为这是最大的优势。如果您不需要实例变量,只使用公共或私有变量,那么可能会有更简单的系统。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | function Person(I) { I = I || {}; Object.reverseMerge(I, { name:"McLovin", age: 25, homeState:"Hawaii" }); return { introduce: function() { return"Hi I'm" + I.name +" and I'm" + I.age; } }; } var fogel = Person({ age:"old enough" }); fogel.introduce(); //"Hi I'm McLovin and I'm old enough" |
哇,这本身并不是很有用,但是看看添加子类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | function Ninja(I) { I = I || {}; Object.reverseMerge(I, { belt:"black" }); // Ninja is a subclass of person return Object.extend(Person(I), { greetChallenger: function() { return"In all my" + I.age +" years as a ninja, I've never met a challenger as worthy as you..."; } }); } var resig = Ninja({name:"John Resig"}); resig.introduce(); //"Hi I'm John Resig and I'm 25" |
另一个优势是拥有模块和基于特征的继承的能力。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // The Bindable module function Bindable() { var eventCallbacks = {}; return { bind: function(event, callback) { eventCallbacks[event] = eventCallbacks[event] || []; eventCallbacks[event].push(callback); }, trigger: function(event) { var callbacks = eventCallbacks[event]; if(callbacks && callbacks.length) { var self = this; callbacks.forEach(function(callback) { callback(self); }); } }, }; } |
包含Person类的示例包括可绑定模块。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | function Person(I) { I = I || {}; Object.reverseMerge(I, { name:"McLovin", age: 25, homeState:"Hawaii" }); var self = { introduce: function() { return"Hi I'm" + I.name +" and I'm" + I.age; } }; // Including the Bindable module Object.extend(self, Bindable()); return self; } var person = Person(); person.bind("eat", function() { alert(person.introduce() +" and I'm eating!"); }); person.trigger("eat"); // Blasts the alert! |
披露:我是丹尼尔·X·摩尔,这是我的埃多克斯。这是用JavaScript定义类的最佳方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | var Animal = function(options) { var name = options.name; var animal = {}; animal.getName = function() { return name; }; var somePrivateMethod = function() { }; return animal; }; // usage var cat = Animal({name: 'tiger'}); |
下面是在javascript中创建对象的方法,到目前为止我已经使用了这些方法
例1:
1 2 3 4 5 | obj = new Object(); obj.name = 'test'; obj.sayHello = function() { console.log('Hello '+ this.name); } |
例2:
1 2 3 4 5 6 | obj = {}; obj.name = 'test'; obj.sayHello = function() { console.log('Hello '+ this.name); } obj.sayHello(); |
例3:
1 2 3 4 5 6 | var obj = function(nameParam) { this.name = nameParam; } obj.prototype.sayHello = function() { console.log('Hello '+ this.name); } |
示例4:object.create()的实际好处。请参阅[此链接]
1 2 3 4 5 6 7 8 9 10 11 12 | var Obj = { init: function(nameParam) { this.name = nameParam; }, sayHello: function() { console.log('Hello '+ this.name); } }; var usrObj = Object.create(Obj); // <== one level of inheritance usrObj.init('Bob'); usrObj.sayHello(); |
示例5(自定义crockford的object.create):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | Object.build = function(o) { var initArgs = Array.prototype.slice.call(arguments,1) function F() { if((typeof o.init === 'function') && initArgs.length) { o.init.apply(this,initArgs) } } F.prototype = o return new F() } MY_GLOBAL = {i: 1, nextId: function(){return this.i++}} // For example var userB = { init: function(nameParam) { this.id = MY_GLOBAL.nextId(); this.name = nameParam; }, sayHello: function() { console.log('Hello '+ this.name); } }; var bob = Object.build(userB, 'Bob'); // Different from your code bob.sayHello(); |
用ES6/ES2015更新答案
类的定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Person { constructor(strName, numAge) { this.name = strName; this.age = numAge; } toString() { return '((Class::Person) named ' + this.name + ' & of age ' + this.age + ')'; } } let objPerson = new Person("Bob",33); console.log(objPerson.toString()); |
我认为您应该阅读DouglasCrockford在javascript中的原型继承和在javascript中的经典继承。
本页示例:
1 2 3 4 | Function.prototype.method = function (name, func) { this.prototype[name] = func; return this; }; |
效果?它将允许您以更优雅的方式添加方法:
1 2 3 4 5 6 7 8 | function Parenizor(value) { this.setValue(value); } Parenizor.method('setValue', function (value) { this.value = value; return this; }); |
我还推荐他的视频:高级JavaScript。
你可以在他的网页上找到更多视频:http://javascript.crockford.com/在JohnReisig的书中,你可以从道格拉斯·克罗克福的网站上找到很多例子。
因为我不接受Yui/Crockford工厂计划,而且因为我喜欢保持事物的独立性和可扩展性,这是我的变体:
1 2 3 4 5 6 7 8 9 10 11 12 | function Person(params) { this.name = params.name || defaultnamevalue; this.role = params.role || defaultrolevalue; if(typeof(this.speak)=='undefined') //guarantees one time prototyping { Person.prototype.speak = function() {/* do whatever */}; } } var Robert = new Person({name:'Bob'}); |
在理想情况下,测试类型是基于类似于第一个原型方法的
如果你想简单点,你可以完全避免"new"关键字,只使用工厂方法。有时我更喜欢这样,因为我喜欢使用JSON创建对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function getSomeObj(var1, var2){ var obj = { instancevar1: var1, instancevar2: var2, someMethod: function(param) { //stuff; } }; return obj; } var myobj = getSomeObj("var1","var2"); myobj.someMethod("bla"); |
不过,我不确定大型对象的性能影响是什么。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var Student = (function () { function Student(firstname, lastname) { this.firstname = firstname; this.lastname = lastname; this.fullname = firstname +"" + lastname; } Student.prototype.sayMyName = function () { return this.fullname; }; return Student; }()); var user = new Student("Jane","User"); var user_fullname = user.sayMyName(); |
那类方式typescript compiles with to JavaScript构造函数。P></
简单的方法是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function Foo(a) { var that=this; function privateMethod() { .. } // public methods that.add = function(b) { return a + b; }; that.avg = function(b) { return that.add(b) / 2; // calling another public method }; } var x = new Foo(10); alert(x.add(2)); // 12 alert(x.avg(20)); // 15 |
编辑:这绝对不是最好的方法,只是一个简单的方法。我也在等待好的答案!
您可能希望使用折叠模式创建类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | // Here is the constructor section. var myType = function () { var N = {}, // Enclosed (private) members are here. X = this; // Exposed (public) members are here. (function ENCLOSED_FIELDS() { N.toggle = false; N.text = ''; }()); (function EXPOSED_FIELDS() { X.count = 0; X.numbers = [1, 2, 3]; }()); // The properties below have access to the enclosed fields. // Careful with functions exposed within the closure of the // constructor, each new instance will have it's own copy. (function EXPOSED_PROPERTIES_WITHIN_CONSTRUCTOR() { Object.defineProperty(X, 'toggle', { get: function () { var before = N.toggle; N.toggle = !N.toggle; return before; } }); Object.defineProperty(X, 'text', { get: function () { return N.text; }, set: function (value) { N.text = value; } }); }()); }; // Here is the prototype section. (function PROTOTYPE() { var P = myType.prototype; (function EXPOSED_PROPERTIES_WITHIN_PROTOTYPE() { Object.defineProperty(P, 'numberLength', { get: function () { return this.numbers.length; } }); }()); (function EXPOSED_METHODS() { P.incrementNumbersByCount = function () { var i; for (i = 0; i < this.numbers.length; i++) { this.numbers[i] += this.count; } }; P.tweak = function () { if (this.toggle) { this.count++; } this.text = 'tweaked'; }; }()); }()); |
该代码将为您提供一个名为MyType的类型。它将有称为切换和文本的内部私有字段。它还具有这些公开的成员:字段计数和数字;属性切换、文本和数字长度;方法incrementNumbersByCount和tweak。
折叠图案在这里详细说明:javascript折叠模式
S for @高尔夫liammclennan'回答队列。P></
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var Animal = function (args) { return { name: args.name, getName: function () { return this.name; // member access }, callGetName: function () { return this.getName(); // method call } }; }; var cat = Animal({ name: 'tiger' }); console.log(cat.callGetName()); |
P></
基于继承的类与对象P></
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | var baseObject = { // Replication / Constructor function new : function(){ return Object.create(this); }, aProperty : null, aMethod : function(param){ alert("Heres your" + param +"!"); }, } newObject = baseObject.new(); newObject.aProperty ="Hello"; anotherObject = Object.create(baseObject); anotherObject.aProperty ="There"; console.log(newObject.aProperty) //"Hello" console.log(anotherObject.aProperty) //"There" console.log(baseObject.aProperty) // null |
单,二和甜蜜,是做得到的。P></
moootools(我的面向对象工具)以类的概念为中心。甚至可以使用继承来扩展和实现。
当掌握后,它会产生荒谬的可重用、强大的javascript。
基于P></
1 2 3 | function Base(kind) { this.kind = kind; } |
在类P></
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // Shared var var _greeting; (function _init() { Class.prototype = new Base(); Class.prototype.constructor = Class; Class.prototype.log = function() { _log.apply(this, arguments); } _greeting ="Good afternoon!"; })(); function Class(name, kind) { Base.call(this, kind); this.name = name; } // Shared function function _log() { console.log(_greeting +" Me name is" + this.name +" and I'm a" + this.kind); } |
actionP></
1 2 | var c = new Class("Joe","Object"); c.log(); //"Good afternoon! Me name is Joe and I'm a Object" |
基于triptych the example of this might:嗯,甚至简单的内部P></
1 2 3 4 5 6 7 8 9 10 11 | // Define a class and instantiate it var ThePerson = new function Person(name, gender) { // Add class data members this.name = name; this.gender = gender; // Add class methods this.hello = function () { alert('Hello, this is ' + this.name); } }("Bob","M"); // this instantiates the 'new' object // Use the object ThePerson.hello(); // alerts"Hello, this is Bob" |
这只单曲创源到对象实例,但仍然是有用的,如果你想帮encapsulate of names for a class中的变量和方法。我normally not be there"鲍勃,M"Arguments for example to the构造函数方法,if the摇篮电话系统与自己的数据或网络数据库,such as a。P></
我还看到过纽约和JS使用this does not the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | //new way using this and new function Persons(name) { this.name = name; this.greeting = function() { alert('Hi! I\'m ' + this.name + '.'); }; } var gee=new Persons("gee"); gee.greeting(); var gray=new Persons("gray"); gray.greeting(); //old way function createPerson(name){ var obj={}; obj.name=name; obj.greeting = function(){ console.log("hello I am"+obj.name); }; return obj; } var gita=createPerson('Gita'); gita.greeting(); |
P></
JavaScript是面向对象的,但它与其他的OOP语言(如Java、C语言或C++)截然不同。不要那样理解它。抛开旧知识,重新开始。JavaScript需要不同的思考。
我建议你找一本好的手册或是有关这个问题的东西。我自己发现extjs教程对我来说是最好的,尽管我在阅读之前或之后都没有使用过这个框架。但它确实很好地解释了JavaScript世界中的内容。抱歉,内容似乎已被删除。这里有一个到archive.org copy的链接。今天工作。P