ES6提供了完整的class语法,因此,著作权归作者所有,禁止商业用途转载。未经授权,禁止复制转载。可以非常方便的使用extends关键字对【版权所有】唐霜 www.tangshuang.net【本文受版权保护】类进行扩展(继承)。为了实现类的一些基础著作权归作者所有,禁止商业用途转载。【作者:唐霜】功能,我撰写了下面这个类,用以被其他类基本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。础,拥有这个基础类的基础功能:
【作者:唐霜】【未经授权禁止转载】本文版权归作者所有,未经授权不得转载。【访问 www.tangshuang.net 获取更多精彩内容】var events = {}
var data = {}
var copyProperty = function(Target, Source) {
for(let key of Reflect.ownKeys(Source)) {
if(key !== 'constructor' && key !== 'prototype' && key !== 'name') {
let descriptor = Object.getOwnPropertyDescriptor(Source, key)
Object.defineProperty(Target, key, descriptor)
}
}
}
export default class ClassBase {
constructor(...args) {
events[this] = {}
data[this] = {}
this.call(this.initialize, ...args)
return this
}
/**
* @desc initialize class method, be called every time class is initialized
* Notice: never use constructor when extends by a sub class
*/
initialize() {}
/**
* @desc get data from data manager
* @param string key: the key of data, you can use '.' to get tree info. e.g. .get('root.sub.ClassBaseMix') => .get('root').sub.ClassBaseMix
*/
get(key) {
let target = data[this]
if(key.indexOf('.') === -1) return target[key]
let nodes = key.split('.').filter(item => item && item !== '')
if(nodes.length === 0) return
for(let node of nodes) {
if(typeof target !== 'object' || !target[node]) return
target = target[node]
}
return target
}
/**
* @desc save data to data manager
* @param string key: the key of data, use '.' to set tree structure. e.g. .set('root.sub.ClassBaseMix', 'value') => .get('root').sub.ClassBaseMix = 'value'
* @param mix value: the value to save
* @param boolean notify: whether to trigger data change event
*/
set(key, value, notify = true) {
if(!data[this]) data[this] = {}
let target = data[this]
if(key.indexOf('.') === -1) {
target[key] = value
if(notify) {
this.trigger('change:' + key, value)
}
return this
}
let nodes = key.split('.').filter(item => item && item !== '')
if(nodes.length === 0) return
let lastKey = nodes.pop()
for(let node of nodes) {
if(typeof target !== 'object') return
if(!target[node]) {
target[node] = {}
}
target = target[node]
}
target[lastKey] = value
if(notify) {
nodes.push(lastKey)
let event = nodes.shift()
this.trigger('change:' + event, value)
while (nodes.length > 0) {
event += '.' + nodes.shift()
this.trigger('change:' + event, value)
}
}
return this
}
/**
* @desc call some function out of this class bind with this
* @param function factory: the function to call
* @param args: arguments to pass to function be called
*/
call(factory, ...args) {
factory.apply(this, args)
return this
}
/**
* @desc bind events on Instantiate objects
* @param string evts: events want to bind, use ' ' to split different events, e.g. .on('change:data change:name', ...)
* @param function handler: function to call back when event triggered
* @param number order: the order to call function. functions are listed one by one with using order.
*/
on(evts, handler, order = 10) {
if(!events[this]) events[this] = {}
evts = evts.split(' ')
let target = events[this]
evts.forEach(evt => {
if(!target[evt]) {
target[evt] = {}
}
let node = target[evt]
if(!node[order]) node[order] = []
let hdles = node[order]
if(hdles.indexOf(handler) === -1) hdles.push(handler) // make sure only once in one order
})
return this
}
/**
* @desc remove event handlers
* @param string event: event name, only one event supported
* @param function handler: the function wanted to remove, notice: if you passed it twice, all of them will be removed. If you do not pass handler, all handlers of this event will be removed.
*/
off(event, handler) {
if(!handler) {
events[this][event] = {}
return
}
let node = events[this][event]
if(!node) return
let orders = Object.keys(node)
if(!orders || orders.length === 0) return
if(orders.length > 1) orders = orders.sort((a, b) => a - b)
orders.forEach(order => {
let hdles = node[order]
let index = hdles.indexOf(handler)
if(index > -1) hdles.splice(index, 1) // delete it/them
if(hdles.length === 0) delete node[order]
})
return this
}
/**
* @desc trigger events handlers
* @param string event: which event to trigger
* @param args: arguments to pass to handler function
*/
trigger(event, ...args) {
let node = events[this][event]
if(!node) return
let orders = Object.keys(node)
if(!orders || orders.length === 0) return
if(orders.length > 1) orders = orders.sort((a, b) => a - b)
let handlers = []
orders.forEach(order => {
let hdles = node[order]
handlers = [...handlers, ...hdles]
})
handlers.forEach(handler => {
if(typeof handler === 'function') {
// this.call(handler, ...args) // 会绑定this
handler(...args) // 不会绑定this,其实可以在on的时候用bind去绑定
}
})
return this
}
/**
* @desc mix this class with other classes, this class property will never be overwrite, the final output class contains certain property and all of this class's property
* @param Classes: the classes passed to mix, previous class will NOT be overwrite by the behind ones.
*/
static mixin(...Classes) {
class ClassBaseMix {}
Classes.reverse()
Classes.push(this)
for(let Mixin of Classes) {
copyProperty(ClassBaseMix, Mixin)
copyProperty(ClassBaseMix.prototype, Mixin.prototype)
}
return ClassBaseMix
}
/**
* @desc mix other classes into this class, property may be overwrite by passed class, behind class will cover previous class
*/
static mixto(...Classes) {
class ClassBaseMix {}
Classes.unshift(this)
for(let Mixin of Classes) {
copyProperty(ClassBaseMix, Mixin)
copyProperty(ClassBaseMix.prototype, Mixin.prototype)
}
return ClassBaseMix
}
toString() {
return this.constructor.name
}
}
你可以在【访问 www.tangshuang.net 获取更多精彩内容】这里【本文受版权保护】阅读每一个方法的说明,这里简单的说明一下【原创内容,转载请注明出处】本文作者:唐霜,转载请注明出处。它们的各自用途。
本文版权归作者所有,未经授权不得转载。本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。initialize方法本文版权归作者所有,未经授权不得转载。
【作者:唐霜】本文版权归作者所有,未经授权不得转载。【原创内容,转载请注明出处】用来替代constructor作为实例化【作者:唐霜】【版权所有,侵权必究】方法,虽然在class中使用constr本文版权归作者所有,未经授权不得转载。转载请注明出处:www.tangshuang.netuctor并没有什么问题,但是大家似乎约【版权所有】唐霜 www.tangshuang.net【原创内容,转载请注明出处】定熟成的使用initialize方法替换【关注微信公众号:wwwtangshuangnet】【版权所有,侵权必究】它。所以constructor方法作为一【未经授权禁止转载】本文作者:唐霜,转载请注明出处。个最起始的方法,不应该在子类中出现被覆盖【版权所有】唐霜 www.tangshuang.net【转载请注明来源】,因为这里会用它来调用initializ本文作者:唐霜,转载请注明出处。【原创不易,请尊重版权】e方法,一旦被覆盖,子类中就不能自动调用原创内容,盗版必究。【转载请注明来源】initialize方法了。
【关注微信公众号:wwwtangshuangnet】本文作者:唐霜,转载请注明出处。未经授权,禁止复制转载。转载请注明出处:www.tangshuang.netsetter和getter转载请注明出处:www.tangshuang.net
原创内容,盗版必究。【转载请注明来源】转载请注明出处:www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】【关注微信公众号:wwwtangshuangnet】用以获取和设置私有属性,当然也可以保存其本文版权归作者所有,未经授权不得转载。转载请注明出处:www.tangshuang.net他任何数据类型。这些数据都会被保存在at【转载请注明来源】本文作者:唐霜,转载请注明出处。tributions这个变量里面,但是它【未经授权禁止转载】【未经授权禁止转载】仅在这个文档中可见,所以不会被外部访问,转载请注明出处:www.tangshuang.net原创内容,盗版必究。只有通过get和set方法才能访问。
【本文受版权保护】本文作者:唐霜,转载请注明出处。著作权归作者所有,禁止商业用途转载。【原创内容,转载请注明出处】而且功能有所提升,传入的变量支持用点隔开【本文首发于唐霜的博客】著作权归作者所有,禁止商业用途转载。来表示父子关系。比如set('book.name', 'News'),这样可以直接设置book对象的name【原创内容,转载请注明出处】【版权所有】唐霜 www.tangshuang.net属性,用get('book').name = 'News'也可以达到这个效果(性能更高),但形式上本文作者:唐霜,转载请注明出处。本文版权归作者所有,未经授权不得转载。没有前者好看,使用get('book.name')这种写法也更优雅。【本文受版权保护】
on、off和trigger原创内容,盗版必究。
本文作者:唐霜,转载请注明出处。原创内容,盗版必究。【访问 www.tangshuang.net 获取更多精彩内容】和大多数事件绑定和触发一样,这三个方法也【原创不易,请尊重版权】【原创内容,转载请注明出处】是实现这个功能的。on绑定事件,传入一个【原创不易,请尊重版权】本文版权归作者所有,未经授权不得转载。回调函数,但是这里还给on加了一个功能,【作者:唐霜】本文作者:唐霜,转载请注明出处。就是第三个参数规定回调函数执行的顺序。比原创内容,盗版必究。【未经授权禁止转载】如当你给同一个事件传入了多个回调函数,怎【本文首发于唐霜的博客】【版权所有,侵权必究】么来规定它们之间的顺序呢?通过传入第三个著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.net参数即可,数字越小的,越靠前执行。
著作权归作者所有,禁止商业用途转载。【本文受版权保护】【转载请注明来源】【版权所有,侵权必究】【版权所有,侵权必究】off在解绑事件的时候,也有一个比较好的【本文受版权保护】【原创内容,转载请注明出处】功能,可以只解绑某一个回调函数,但前提是【版权所有】唐霜 www.tangshuang.net【未经授权禁止转载】,你在on的时候,传入的是变量名函数,解著作权归作者所有,禁止商业用途转载。【本文首发于唐霜的博客】绑的时候也是这个指向函数的变量。
著作权归作者所有,禁止商业用途转载。转载请注明出处:www.tangshuang.net【未经授权禁止转载】【作者:唐霜】setter中的事件【版权所有】唐霜 www.tangshuang.net
转载请注明出处:www.tangshuang.net著作权归作者所有,禁止商业用途转载。本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。当你使用set方法设置一个新值的时候,这【访问 www.tangshuang.net 获取更多精彩内容】【作者:唐霜】个类会自动调用trigger方法去触发一【本文受版权保护】【关注微信公众号:wwwtangshuangnet】个change事件,例如set(R转载请注明出处:www.tangshuang.net原创内容,盗版必究。16;name’, R【本文首发于唐霜的博客】著作权归作者所有,禁止商业用途转载。16;new name’)的【版权所有】唐霜 www.tangshuang.net著作权归作者所有,禁止商业用途转载。时候trigger(‘cha【原创不易,请尊重版权】【版权所有】唐霜 www.tangshuang.netnge:name’, 【访问 www.tangshuang.net 获取更多精彩内容】【访问 www.tangshuang.net 获取更多精彩内容】216;new name’)【本文受版权保护】【关注微信公众号:wwwtangshuangnet】会被自动触发。这和backbone的规则【未经授权禁止转载】著作权归作者所有,禁止商业用途转载。非常像。
【作者:唐霜】原创内容,盗版必究。【访问 www.tangshuang.net 获取更多精彩内容】【访问 www.tangshuang.net 获取更多精彩内容】【版权所有】唐霜 www.tangshuang.net同时,像getter,setter里面的本文版权归作者所有,未经授权不得转载。【版权所有】唐霜 www.tangshuang.net层级关系也被支持了,比如set('book.name', 'Book'),这个时候其实触发了两个事件,一个是【本文受版权保护】change:book,一个是未经授权,禁止复制转载。change:book.name,它们都会被触发,而且是两个独立的事件,【访问 www.tangshuang.net 获取更多精彩内容】【作者:唐霜】绑在change:book上的回调函数和【原创不易,请尊重版权】【版权所有,侵权必究】绑在change:book.name上的【未经授权禁止转载】原创内容,盗版必究。回调函数是完全分开的,没有任何关系,ch【本文首发于唐霜的博客】【原创不易,请尊重版权】ange:book事件的回调函数会被先执著作权归作者所有,禁止商业用途转载。【转载请注明来源】行。如果你不想使用这个功能,可以把set【关注微信公众号:wwwtangshuangnet】未经授权,禁止复制转载。方法的第三个参数设置为false,这样就【原创内容,转载请注明出处】【原创内容,转载请注明出处】不会触发trigger。
call私有方法【版权所有】唐霜 www.tangshuang.net
本文作者:唐霜,转载请注明出处。本文版权归作者所有,未经授权不得转载。原创内容,盗版必究。【转载请注明来源】ES还不支持private关键字,所以不【关注微信公众号:wwwtangshuangnet】原创内容,盗版必究。能直接定义私有方法。这个类拥有一个.ca原创内容,盗版必究。未经授权,禁止复制转载。ll方法,可以用来调用私有方法。私有方法【版权所有,侵权必究】【访问 www.tangshuang.net 获取更多精彩内容】写在class外面,跟attributi【原创不易,请尊重版权】【作者:唐霜】ons、events这两个变量差不多。但【本文受版权保护】【原创不易,请尊重版权】是在私有方法里面可以使用this,以及t【本文首发于唐霜的博客】原创内容,盗版必究。his携带的任何东西,在class里面c本文作者:唐霜,转载请注明出处。【原创不易,请尊重版权】all它的时候,this都是有效的。
本文作者:唐霜,转载请注明出处。原创内容,盗版必究。【版权所有】唐霜 www.tangshuang.net转载请注明出处:www.tangshuang.netmix一次性继承多个类【作者:唐霜】
【版权所有,侵权必究】【本文首发于唐霜的博客】本文版权归作者所有,未经授权不得转载。原创内容,盗版必究。【原创不易,请尊重版权】在backbone或其他一些框架中,每个转载请注明出处:www.tangshuang.net本文版权归作者所有,未经授权不得转载。类有一个extends方法来创建一个子类【版权所有】唐霜 www.tangshuang.net著作权归作者所有,禁止商业用途转载。,在extends参数中写方法来覆盖父类【关注微信公众号:wwwtangshuangnet】【原创不易,请尊重版权】的方法。但是这种操作只能继承于一个类,而本文作者:唐霜,转载请注明出处。转载请注明出处:www.tangshuang.net如果想一次性继承几个类的某些方法,还需要原创内容,盗版必究。著作权归作者所有,禁止商业用途转载。自己写个扩展方法来实现。再说了,ES6本【版权所有,侵权必究】【本文受版权保护】身就提供了extends关键字来继承类,【转载请注明来源】本文作者:唐霜,转载请注明出处。所以单纯的extends方法不应该再继续本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。使用了,只需要写一个mix方法来混入这些【关注微信公众号:wwwtangshuangnet】本文作者:唐霜,转载请注明出处。想要继承的类就可以了。
【转载请注明来源】未经授权,禁止复制转载。著作权归作者所有,禁止商业用途转载。我写的这个类提供了两个方法,mixin和【原创不易,请尊重版权】【未经授权禁止转载】mixto,混入的方式不同。mixin是【版权所有,侵权必究】【本文受版权保护】把参数里面的类的方法或属性一个一个往自己【原创不易,请尊重版权】【原创内容,转载请注明出处】里面塞,而mixto是把自己的方法或属性本文作者:唐霜,转载请注明出处。转载请注明出处:www.tangshuang.net往参数里面的类塞,方向上正好相反。由于塞本文作者:唐霜,转载请注明出处。【本文受版权保护】的方向不同,最终如果方法有重名的话,被塞本文作者:唐霜,转载请注明出处。【本文首发于唐霜的博客】的一方的方法就会被保留下来,作为最终产生本文作者:唐霜,转载请注明出处。【关注微信公众号:wwwtangshuangnet】的混类的主体。写一个继承就非常简单:
【作者:唐霜】原创内容,盗版必究。本文作者:唐霜,转载请注明出处。【未经授权禁止转载】class SubClass extends ClassBase.mixin(A, B, C) {}
mixin和mixto都是静态属性,所以原创内容,盗版必究。【未经授权禁止转载】可以直接用类名来调用。
【原创不易,请尊重版权】转载请注明出处:www.tangshuang.net本文版权归作者所有,未经授权不得转载。【未经授权禁止转载】【关注微信公众号:wwwtangshuangnet】toString或取类名著作权归作者所有,禁止商业用途转载。
本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。【访问 www.tangshuang.net 获取更多精彩内容】未经授权,禁止复制转载。【本文首发于唐霜的博客】有的时候你想看下当前的实例化对象到底是从【作者:唐霜】【版权所有,侵权必究】哪个类实例化出来的,那么直接用toStr转载请注明出处:www.tangshuang.net【原创不易,请尊重版权】ing方法来获取类的名称。原本我想返回'本文作者:唐霜,转载请注明出处。未经授权,禁止复制转载。[class BaseClass]【作者:唐霜】【本文首发于唐霜的博客】217;这种类型的字符串,但是绝对没什么未经授权,禁止复制转载。转载请注明出处:www.tangshuang.net意义,还不如直接返回类名,还可以用来做比【作者:唐霜】【版权所有】唐霜 www.tangshuang.net较。
【原创内容,转载请注明出处】【转载请注明来源】【访问 www.tangshuang.net 获取更多精彩内容】未经授权,禁止复制转载。


[…] 本文发布在我的博客求个兼职,如果您有web开发方面的需要,可以联系我,生活不容易,且行且珍惜。请在我的个人博客 http://www.tangshuang.net 留言,我会联系你。 […]