JS中OO的模拟有很多的实现与尝试(相关文章),Arale则在以下原则的指导下进行构建:
1.如无必要,勿增实体 —— Simple
2. 一目了然,容易学习 —— Stupid
Class源码的学习可以通过了解如何调用Class来实现类的继承与扩展的例子进行学习。下面是对Class类的源码的注释(简单的辅助方法不再进行注释),有问题可指出,欢迎交流。
// The base Class implementation.
// 基本的Class类的构造函数
// --直接调用该构造函数,传入的类o将被包裹,拥有extend和implement方法。
function Class(o) {
// Convert existed function to Class.
if (!(this instanceof Class) && isFunction(o)) {
return classify(o)
}
}
// commonJS规范
module.exports = Class
// Create a new Class.
//
// var SuperPig = Class.create({
// Extends: Animal,
// Implements: Flyable,
// initialize: function() {
// SuperPig.superclass.initialize.apply(this, arguments)
// },
// Statics: {
// COLOR: 'red'
// }
// })
// 核心方法,返回一个新创建的子类
// --如果传入了父类parent以及传递给子类原型的properties对象,则分别继承父类的实例方法以及静态属性
// --如果只传入了properties对象,则默认将parent置为该对象的Extends属性值,没有该属性则默认置为Class
Class.create = function(parent, properties) {
// 强制parent为函数类型,properties(最好)为对象
if (!isFunction(parent)) {
properties = parent
parent = null
}
properties || (properties = {}) // 防报错,默认置为空对象
parent || (parent = properties.Extends || Class)// Subclass需要继承的父类
properties.Extends = parent //将父类置为properties的Extends属性值(可参考上面调用Class.create的例子)
// The created class constructor
// 创建类的构造函数
function SubClass() {
// 调用父类的构造函数,继承其中的实例方法
parent.apply(this, arguments)
// 在构造函数为Subclass自身且具有initialize实例方法的时候执行初始化方法
if (this.constructor === SubClass && this.initialize) {
this.initialize.apply(this, arguments)
}
}
// Inherit class (static) properties from parent.
// 将父类的静态方法mix给Subclass(可在父类StaticsWhiteList属性中指定要继承的属性)
if (parent !== Class) {
mix(SubClass, parent, parent.StaticsWhiteList)
}
// Add instance properties to the subclass.
// 该方法特别重要。遍历properties的所有属性,如果该属性名存在于Class.Mutators中(Extends,Implements,Statics),
// 则执行Class.Mutators对应的方法,否则将该属性对应的值赋给Subclass的原型链作为其实例属性
// 因此,Class.Mutators的所有方法中的this指向Subclass,这一点需要注意
implement.call(SubClass, properties)
// Make subclass extendable.类化Subclass,使其可扩展可继承其他类
return classify(SubClass)
}
function implement(properties) {
var key, value
for (key in properties) {
value = properties[key]
if (Class.Mutators.hasOwnProperty(key)) {
Class.Mutators[key].call(this, value)
} else {
this.prototype[key] = value
}
}
}
// Create a sub Class based on `Class`.
// 基于Class创建子类
Class.extend = function(properties) {
properties || (properties = {})
properties.Extends = this
return Class.create(properties)
}
// 类化构造函数cls,使其具有extend以及implement方法,从而使其能够轻松进行扩展
function classify(cls) {
cls.extend = Class.extend
cls.implement = implement
return cls
}
// Mutators define special properties.
// Class.Mutators定义了三个特定的属性,从而在调用Class.create的时候如果用户传入了对应的属性则进行对应方法的调用
// 注意:三个函数中的this都指向新创建的Subclass
Class.Mutators = {
'Extends': function(parent) {
// 新创建类的原型
var existed = this.prototype
// 创建以父类原型为原型对象的对象(该对象继承自parent.prototype)
var proto = createProto(parent.prototype)
// Keep existed properties.将Subclass的静态属性mix给proto
mix(proto, existed)
// Enforce the constructor to be what we expect.
// 将proto的构造函数指向Subclass
proto.constructor = this
// Set the prototype chain to inherit from `parent`.
// Subclass的原型指向proto,proto的原型指向parent.prototype,从而形成原型链继承
this.prototype = proto
// Set a convenience property in case the parent's prototype is needed later.
// 方便对父类的原型的方法和属性进行调用
this.superclass = parent.prototype
},
// implements为JS保留关键字,故开头大写
'Implements': function(items) {
// 保证参数为数组,方便后面调用
isArray(items) || (items = [items])
var proto = this.prototype, item
while (item = items.shift()) {
// 直接将被继承对象的原型(不是类的则将其本身)的属性赋值给Subclass原型
mix(proto, item.prototype || item)
}
},
// 静态属性
'Statics': function(staticProperties) {
mix(this, staticProperties)
}
}
// Shared empty constructor function to aid in prototype-chain creation.
function Ctor() {}
// See: http://jsperf.com/object-create-vs-new-ctor
var createProto = Object.__proto__ ?
function(proto) {
return { __proto__: proto }
} :
function(proto) {
Ctor.prototype = proto
return new Ctor()
}
// Helpers
// ------------
function mix(r, s, wl) {
// Copy "all" properties including inherited ones.
for (var p in s) {
if (s.hasOwnProperty(p)) {
if (wl && indexOf(wl, p) === -1) continue
// 在 iPhone 1 代等设备的 Safari 中,prototype 也会被枚举出来,需排除
if (p !== 'prototype') {
r[p] = s[p]
}
}
}
}
var toString = Object.prototype.toString
var isArray = Array.isArray || function(val) {
return toString.call(val) === '[object Array]'
}
var isFunction = function(val) {
return toString.call(val) === '[object Function]'
}
var indexOf = Array.prototype.indexOf ?
function(arr, item) {
return arr.indexOf(item)
} :
function(arr, item) {
for (var i = 0, len = arr.length; i < len; i++) {
if (arr[i] === item) {
return i
}
}
return -1
}