0.0.2 • Published 11 years ago

oop.js v0.0.2

Weekly downloads
2
License
-
Repository
github
Last release
11 years ago

JS OOP 库概要设计

目标

实现功能强大、适应场景广的JavaScript OOP库。

去除js中冗余、晦涩的噪音代码。

  • 隐藏 prototype;ß
  • 隐藏不同引擎对js的不同处理;
  • 名称一次定义,方便改名
  • 不必写 var self = this;

特征

  • 单继承
  • 多mixin
  • property
  • 实例方法、类方法和静态方法
  • metaclass

修饰器

在此OOP库中,大量使用了修饰器模式。

修饰器是一个函数,用于包装另一个函数,这种包装的首要目的是透明的修改或增强被包装对象的行为。但在定义后,函数对象本身就被传递给修饰器函数,修饰器函数返回一个新函数替代原始的函数。

可以使用多个修饰器,装饰器将按照出现的先后顺序应用。

修饰器也可以接受参数,返回在调用时使用函数作为参数的函数。

在其他语言中,修饰器一般通过@进行调用。由于语法限制,在js中使用函数调用形式进行调用。

在Python中:

class A(object):
  @classmethod
  @mydecorator('a', 'b')
  def myMethod():
    pass

在js中:

var A = new Class(Object, function() {
  this.myMethod = classmethod(mydecorator('a', 'b')(function() {
  }));
});

Mootools的Function.prototype.overloadSetter就是一个典型的修饰器应用。

创建一个类

构造函数风格

通过构造函数扩充this实现:

var MyClass = new Class(function() {

	// 这里的 this 指向class的构造成员
	this.member1 = 1;
	this.member2 = function() { };
	
});

原型成员风格

通过key-value object实现:

var MyClass = new Class({
	member1 : 1,
	member2: function() { }
});

成员类型

有几种常用成员类型:

  • 普通属性
  • property属性
  • 构造方法
  • 实例方法
  • 类方法
  • 静态方法

普通属性

定义在类上的一个普通静态属性。

由于JS中区分传值引用与地址引用,普通的静态属性需要注意对象在类的创建过程中是地址引用的,也就是说没有自动帮你将这些对象成员自动拷贝一份到实例上。

如果需要为每个实例设置不同的初始化值,需要在构造函数中对已经产生的实例(self)进行赋值。或者使用property属性也可实现类似的功能。

var MyClass = new Class(function() {
	// 普通属性
	this.field = 1;
	this.field2 = {foo:1, bar:2};
});

var myClass = new MyClass();
var myClass2 = new MyClass();

// field2为地址引用,所有实例公用一个
myClass.field2.foo = 11;

console.log(myClass2.field2.foo); // ==> 11

property属性

可以设置getter和setter方法的属性,通过 obj.get('propertyName') 获取属性值,obj.set('propertyName', propertyValue) 设置属性值。

在获取和设置的时,设置的getter和setter分别会被执行。

var MyClass = new Class(function() {

	// property属性
	this.prop = property(function(self) {
		// 这个函数是getter
		self._set('prop', self.__prop); // 确保通过 self.prop 也能获取到最新的值
		return self.__prop;
	}, function(self, value) {
		// 这个函数是setter
		self._set('prop', value); // 确保通过 self.prop 也能获取到最新的值
		self.__prop = value;
	});

});

var myClass = new MyClass();

myClass.set('prop', 'test');
console.log(myClass.get('prop')); // ==> test
console.log(myClass.prop); // ==> test

property属性是为了实现ECMA5 Object.defineProperty类似的功能设计的,因此,为保持对支持Object.defineProperty的兼容性,请使用self._set('prop', value)方法对实例上的同名属性进行赋值,而不是直接使用self.prop = value的形式。

实例方法

默认的方法,声明函数第一个参数是一个实例的引用,一般通过self关键字进行调用。

在实例上调用时,第一个参数默认为调用实例,只需传入第二个参数开始的参数;

var MyClass = new Class(function() {

    // instancemethod,实例方法。默认方法类型,第一个参数为方法调用者对象
    this.myInstanceMethod = function(self, arg1, arg2) {
		console.log(self);
    };
	
});

var myClass = new MyClass();

// 在实例上调用
myClass.myInstanceMethod(arg1, arg2); // ==> myClass

可以通过类的get方法获取到类的实例方法的类函数,实现同定义函数相同的传参调用。这种调用在继承的时候可以使用。

	// 直接调用MyClass的实例方法,实例对象{}(self)通过第一个参数传递进去。
	MyClass.get('myInstanceMethod')({}, arg1, arg2);
	
	var MyClass2 = new Class(MyClass, function() {
		this.initialize = function(self, arg1, arg2) {
			MyClass.get('initialize')(self, arg1, arg2);
		};
	});

类方法

通过classmethod方法包装后的函数成员会被作为类的类方法存在。声明函数第一个参数是一个类的引用,一般通过cls关键字进行调用。

类方法在类上和类的实例上均可调用。

在实例上调用时,第一个参数默认为调用实例的构造类,只需传入第二个参数开始的参数;

在类上调用时,第一个参数默认为调用类,只需传入第二个参数开始的参数。

var MyClass = new Class(function() {

	// classmethod,类方法。第一个参数为方法调用者的类
	this.myClassMethod = classmethod(function(cls, arg1, arg2) {
		console.log(cls);
	});
	
});

// 在类上调用
MyClass.myClassMethod(arg1, arg2); // ==> MyClass

var myClass = new MyClass();

// 在实例上调用
myClass.myClassMethod(arg1, arg2); // ==> MyClass

静态方法

通过staticmethod方法包装后的函数成员会被作为类的静态方法存在。声明函数不设置任何默认参数。

静态在类上和类的实例上均可调用。

在实例和在类上调用时,所有参数一对一的传递,不会有任何默认参数的传递。

var MyClass = new Class(function() {

	// staticmethod,静态方法。不传递任何默认参数
	this.myStaticMethod = staticmethod(function(arg1, arg2) {
	});
	
});

// 在类上调用
MyClass.myStaticMethod(arg1, arg2);

var myClass = new MyClass();

// 在实例上调用
myClass.myStaticMethod(arg1, arg2);

构造方法

构造方法会在生成类实例(new)时被调用。

由于地址引用的原因,对每个实例独有的成员一般是在构造函数中进行创建。

var MyClass = new Class(function() {
	// 构造方法,在new的时候会执行
	this.initialize = function(self) {
		self.foo = 1;
		self.bar = {}; // 每个实例都会有一个不同的bar属性对象
		console.log('base class!');
	};
});

继承

单继承,通过new Class的第一个参数指定父类

在继承方法中不会自动调用父类同名方法,需要手工调用,调用方法有2种:

  • 直接调用父类上的方法
  • this.parent 调用父类同名方法
var MyClass2 = new Class(MyClass, function() {
	/**
	 * 覆盖了父类的同名方法
	 * @override
	 */
	this.initialize = function(self) {
		MyClass.get('initialize')(self); // 调用父类的同名方法
		// 或 this.parent(self); // this.parent指向父类同名方法

		console.log('inherit class!');
	}
});

var myClass2 = new MyClass2(); // ==> base class! inherit class!

Mixin

通过mixin,可以将另外一个类的成员mix到本类中,与继承机制不同,可以同时mix多个类。

var MyClass = new Class(function() {
	this.__mixins__ = [Events]; // mixin 了 Events 这个类
});

var myClass = new MyClass();

myClass.addEvent('click', function() {}); // addEvent是从Events类中mixin进来的

原型成员风格写法:

var MyClass = new Class({
	'__mixins__': [Events]
});

修改类

一个创建好的类也可以重新修改:

// ...接以上代码...
MyClass.set('addEvent', function(self) {
	alert('changed!');
});

myClass.addEvent(); // ==> changed!

也可以扩展出新的成员:

MyClass.set('myNewCustomMethod', function() {
	alert('new method!');
});

对于classmethod和staticmethod,动态修改类会导致遍历继承树,对于已经存在的比较庞大的类树,将会有比较大的性能消耗。

metaclass

使用metaclass提供了对类的创建过程的处理机制,metaclass是一个继成于Type的class,其中有两个特殊方法:

  • __new__ 类的创建过程
  • initialize 类的产生过程

创建一个metaclass

__new__方法和initialize方法都接收四个参数:

  1. 创建出来的类
  2. 类的名称(目前永远为null,为了兼容python的api)
  3. 此类的父类
  4. 类的成员

__new__用于修改类的定义,在非常早期的时候修改类,type.__new__方法用于初始化一个类; initialize用于对已经创建好的类进行修改。

	var MyMetaClass = new Class(Type, {
		__new__: function(cls, name, base, dict) {
			return Type.__new__(cls, name, base, dict);
		},
		initialize: function(cls, name, base, dict) {
			
		}
	});

###使用一个metaclass

var MyClass = new Class({
	this.__metaclass__ = MyMetaClass;	
});

__metaclass__成员也会继承,所有子类自动适用此metaclass

0.0.2

11 years ago

0.0.1

11 years ago

0.0.0

11 years ago