我更喜欢在大型项目中使用OOP,就像我现在正在研究的那样.我需要在JavaScript中创建几个类,但是,如果我没有弄错的话,至少有几种方法可以做到这一点.什么是语法,为什么会以这种方式完成?
我想避免使用第三方库 - 至少在开始时.
寻找其他答案,我找到了文章面向对象的JavaScript编程,第一部分:继承 - Doc JavaScript,讨论了JavaScript中的面向对象编程.是否有更好的继承方式?
这是在不使用任何外部库的情况下执行此操作的方法:
// 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使用prototype
基于继承的方案.
此外,还有许多流行的JavaScript库,它们在JavaScript中具有类似于类的功能.你至少要检查一下Prototype和jQuery.
决定哪一个是"最好的"是在Stack Overflow上开始圣战的好方法.如果你正在着手一个更大的JavaScript项目,那么绝对值得学习一个受欢迎的图书馆并按照自己的方式去做.我是一个原型人,但Stack Overflow似乎倾向于jQuery.
至于只有"一种方法来做它",没有任何外部库的依赖,我写的方式就是它.
在JavaScript中定义类的最佳方法是不定义类.
认真.
面向对象有几种不同的风格,其中一些是:
基于类的OO(由Smalltalk首次引入)
基于原型的OO(由Self首次推出)
基于多方法的OO(我认为首先由CommonLoops引入)
基于谓词的OO(不知道)
还有其他我不知道的.
JavaScript实现了基于原型的OO.在基于原型的OO中,通过复制其他对象(而不是从类模板实例化)来创建新对象,并且方法直接存在于对象中而不是类中.继承是通过委托来完成的:如果一个对象没有方法或属性,则查询它的原型(即从中克隆的对象),然后是原型的原型等等.
换句话说:没有课程.
JavaScript实际上对该模型进行了很好的调整:构造函数.您不仅可以通过复制现有对象来创建对象,还可以"凭空"构建对象,可以这么说.如果使用new
关键字调用函数,则该函数将成为构造函数,并且this
关键字不会指向当前对象,而是指向新创建的"空"对象.因此,您可以按照自己喜欢的方式配置对象.通过这种方式,JavaScript构造函数可以在传统的基于类的OO中承担类的一个角色:用作新对象的模板或蓝图.
现在,JavaScript是一种非常强大的语言,因此如果您愿意,可以很容易地在JavaScript中实现基于类的OO系统.但是,如果您确实需要它,那么您应该只这样做,而不仅仅是因为Java就是这样做的.
在ES2015规范中,您可以使用与原型系统相比仅仅是糖的类语法.
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.`;
}
}
主要好处是静态分析工具更容易定位此语法.来自基于类的语言的其他人也更容易将该语言用作多语言.
警惕其目前的局限性.要实现私有属性,必须使用Symbols或WeakMaps.在将来的版本中,类很可能会扩展为包含这些缺失的功能.
目前浏览器支持不是很好(除了IE之外几乎所有人都支持),但你现在可以使用像Babel这样的转换器来使用这些功能.
ECMAScript 6中的类(最终语义)
什么?等待.真?不好了!(关于ES6课程和隐私的帖子)
兼容性表 - 类
巴别塔 - 班级
我更喜欢使用Daniel X. Moore {SUPER: SYSTEM}
.这是一门提供诸如真实实例变量,基于特征的继承,类层次结构和配置选项等优点的学科.下面的例子说明了真实实例变量的使用,我认为这是最大的优势.如果您不需要实例变量并且只对公共或私有变量感到满意,那么可能会有更简单的系统.
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"
哇,这本身并不是非常有用,但看看添加一个子类:
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"
另一个优点是具有模块和基于特征的继承的能力.
// 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类的示例包括可绑定模块.
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!
披露:我是Daniel X. Moore,这是我的{SUPER: SYSTEM}
.这是在JavaScript中定义类的最佳方法.
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:
obj = new Object(); obj.name = 'test'; obj.sayHello = function() { console.log('Hello '+ this.name); }
例2:
obj = {}; obj.name = 'test'; obj.sayHello = function() { console.log('Hello '+ this.name); } obj.sayHello();
例3:
var obj = function(nameParam) { this.name = nameParam; } obj.prototype.sayHello = function() { console.log('Hello '+ this.name); }
示例4:Object.create()的实际好处.请参考[此链接]
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):
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();
类定义如下:
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());
我想你应该读道格拉斯Crockford的JavaScript中原型继承和在JavaScript经典传承.
他的页面中的示例:
Function.prototype.method = function (name, func) { this.prototype[name] = func; return this; };
影响?它将允许您以更优雅的方式添加方法:
function Parenizor(value) { this.setValue(value); } Parenizor.method('setValue', function (value) { this.value = value; return this; });
我还推荐他的视频: 高级JavaScript.
您可以在他的页面上找到更多视频:http: //javascript.crockford.com/在John Reisig的书中,您可以找到Douglas Crockfor网站上的许多示例.
因为我不承认YUI/Crockford的工厂计划,因为我喜欢保持自包含和可扩展的东西,这是我的变化:
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来创建对象.
function getSomeObj(var1, var2){ var obj = { instancevar1: var1, instancevar2: var2, someMethod: function(param) { //stuff; } }; return obj; } var myobj = getSomeObj("var1", "var2"); myobj.someMethod("bla");
不过,我不确定大型物体的性能是什么.
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使用构造函数将类编译为JavaScript的方式.
简单的方法是:
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
原因that
是,this
如果将方法作为事件处理程序,则可以绑定到其他内容,因此在实例化期间保存该值并稍后使用它.
编辑:这绝对不是最好的方式,只是一种简单的方式.我也在等待好的答案!
您可能希望使用折叠模式创建一个类型:
// 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的类型.它将具有称为切换和文本的内部私有字段.它也会有这些暴露的成员:字段数和数字 ; 属性toggle,text和numberLength ; 方法incrementNumbersByCount和tweak.
折叠模式在这里详细说明: Javascript折叠模式