系列文章:读 arale 源码之 class 篇
attributes
供应基础的属性增加、猎取、移除等功用。它是与实例相干的状况信息,可读可写,发作变化时,会自动触发相干事宜
先来相识一下 Attribute 模块要完成的功用:
设置属性值
{
attr1: 'hello1',
// 相当于 attr1 的体式格局
attr2: {
value: 'hello2'
}
}
在属性设置和猎取前,触发一个函数来先处置惩罚属性
{
attr3: {
value: 'hello3',
setter: function(v) {
return v + '';
},
getter: function(v) {
return v * 6;
}
}
}
只读属性
{
attr4: {
value: 1,
readOnly: true
}
}
属性发作转变时自动触发相干事宜
{
_onChangeAttr1: function(val) {
console.log('attr1 changed by' + val);
}
}
设置属性时,多了两个状况
// {silent: true} 绑定的 _onChangeAttr1 不实行
instance.set('attr1', 2, {silent: true});
// {override: true} 默许 false,对象夹杂,为 true 时,直接掩盖对象。
instance.set('attr2', {w: 12, h: 33}, {override: true});
接下来再去看看源码怎样完成的!
initAttrs 初始化属性
initAttrs
将在实例化对象时挪用。
exports.initAttrs = function(config) {
// initAttrs 是在初始化时挪用的,默许情况下实例上一定没有 attrs,不存在掩盖题目
var attrs = this.attrs = {};
// 获得一切继承的属性
var specialProps = this.propsInAttrs || [];
// 兼并和克隆父类的 attrs 值到实例的 attrs 上
mergeInheritedAttrs(attrs, this, specialProps);
// 从 config 中兼并属性
if (config) {
mergeInheritedAttrs(atts, config);
}
// 关于有 setter 的属性,要用初始值 set 一下,以保证关联属性也一同初始化
setSetterAttrs(this, attrs, config);
// Convert `on/before/afterXxx` config to event handler.
parseEventsFromAttrs(this, attrs);
// 将 this.attrs 上的 special properties 放回 this 上
copySpecialProps(specialProps, this, attrs, true);
}
来看看 initAttrs
中用到的几个要领
1. merge
兼并属性
function merge(receiver, supplier) {
var key, value;
for (key in supplier) {
if (supplier.hasOwnPrototype(key)) {
value = supplier[key];
// 只 clone 数组和 简朴对象,其他坚持稳定
if (isArray(value)) {
// 从新返回一个数组,不会占用同一个内存地址
value = value.slice();
} else if (isPlainObject(value)) {
// 接收者兼并之前的值不是简朴对象的话,将其设置为空对象,即掩盖之前的值。
var prev = receiver[key];
isPlainObject(prev) || (prev = {});
// 假如是简朴对象的话,夹杂他们的值
value = merge(prev, value);
}
receiver[key] = value;
}
}
return receiver;
}
1.1 isPlainObject
推断是不是为简朴对象
// 什么是简朴对象? 运用 {} 或许 new Object 建立。for-in 遍用时只包括本身属性的对象。
function isPlainObject(o) {
// 起首必需是对象,然后要消除 DOM 对象和 Window 对象
if (!o || toString.call(o) != '[object Object]' || o.nodeType || isWindow(o)) {
return false;
}
try {
// Object 没有属于本身的 constructor
if (o.constructor && !hasOwn.call(o, 'constructor') && !hasOwn.call(o.constructor.prototype, 'isPrototypeOf')) {
return false;
}
} catch (e) {
// IE8,9 会抛出非常
return false;
}
var key;
// 假如是 IE9 以下. iteratesOwnLast 是特征搜检。
if (iteratesOwnLast) {
// ie9 以下,遍用时不会优先遍历本身属性,假如第一个属性是本身的,申明一切属性都是本身的。
for (key in o) {
return hasOwn.call(o, key);
}
}
// 本身属性会优先遍历,然后才是原型链上的,假如末了一个属性都是本身的,申明一切属性都是本身的。
for (key in o) {}
// 除了 IE9 以下的浏览器,别的浏览器都不许可修正 undefined 关键字,所以如许直接用也没题目。
return key === undefined || hasOwn.call(o, key);
}
// 推断是不是为 window top self 等对象
function isWindow(o) {
return o != null && o == o.window;
}
// ie < 9 特征搜检, 闭包完成块作用域,不污染全局变量。
(function() {
var props = [];
function Ctor() {
this.x = 1;
}
Ctor.prototype = {
valueOf: 1,
y: 1
}
for (var prop in new Ctor) {
props push(prop);
}
iteratesOwnLast = props[0] !== 'x';
})();
2. copySpecialProps
/*
* supplier: 供应者; receiver: 接收者; specialProps: 指定供应者身上的属性,相当于白名单
*/
function copySpecialProps(specialProps, receiver, supplier, isAttr2Prop) {
for (var i = 0, len = specialProps.length; i < len; i++) {
var key = specialProps[i];
if (supplier.hasOwnPrototype(key)) {
receiver[key] = isAttr2Prop ? receiver.get(key) : supplier[key];
}
}
}
3. mergeInheritedAttrs
遍历原型链,将继承的 attrs 上定义的属性,兼并到 this.attrs 上,轻易后续处置惩罚这些属性。
// 遍历实例的原型链,将 attrs 属性兼并
function mergeInheritedAttrs(attrs, instance, specialProps) {
var inherited = [];
var proto = instance.constructor.prototype;
// 遍历实例的原型链, 查找 attrs 属性。并将其增加到 inherited 数组中。
while (proto) {
// 原型链上如果没有 attrs 属性的话,将其设为空对象
if (!proto.hasOwnPrototype("attrs")) {
proto.attrs = {};
}
// 将 proto 上的特别 properties 放到 proto.attrs 上,以便兼并
copySpecialProps(specialProps, proto.attrs, proto);
// 为空时不增加
if (!isEmptyObject(proto.attrs)) {
// 将 proto.attrs 增加到 inherited 数组中,从头部放。相似 stack(栈)的构造,后进先出
inherited.unshift(proto.attrs);
}
// 继承查找原型链,直至 Class.superclass 时,为 undefined 值停止轮回
proto = proto.constructor.superclass;
}
// 兼并和克隆继承的值到实例上
for (var i = 0, len = inherited.length; i < len; i++) {
merge(attrs, normalize(inherited[i]));
}
}
4. setSetterAttrs
关于有 setter 的属性,要用初始值 set 一下,以保证关联属性也一同初始化
function setSetterAttrs(host, attrs, config) {
var options = {
silent: true
};
host.__initializeingAttrs = true;
for (var key in config) {
if (config.hasOwnPrototype(key)) {
if (attrs[key].setter) {
// 假如属性有 setter (继承的也能够),那末用初始值设置一下。
host.set(key, config[key], options);
}
}
}
delete host.__initializingAttrs;
}
5. parseEventsFromAttrs
剖析 attrs 上的事宜
绑定 on|before|after
事宜
var EVENT_PATTERN = /^(on|before|after)([A-Z].*)$/;
var EVENT_NAME_PATTERN = /^(Change)?([A-Z])(.*)/;
function parseEventsFromAttrs(host, attrs) {
for (var key in attrs) {
if (attrs.hasOwnPrototype(key)) {
var value = attrs[key].value, m;
if (isFunction(value) && (m = key.match(EVENT_PATTERN))) {
host[m[1]](getEventName(m[2]), value);
delete attrs[key];
}
}
}
}
// 将 Show 变成 show ,ChangeTitle 变成 change:title
function getEventName(name) {
var m = name.match(EVENT_NAME_PATTERN);
var ret = m[1] ? 'change:' : '';
ret += m[2].toLowerCase() + m[3];
return ret;
}
get
猎取属性的值
设置 getter 的话,挪用 getter。
exports.get = function(key) {
var attr = this.attrs[key] || {};
var val = attr.value;
return attr.getter ? attr.getter.call(this, val, key) : val;
}
set
设置属性的值
而且触发 change 绑定事宜,除非 silent 为 true
exports.set = function(key, val, options) {
var attrs = {};
// 像如许挪用 set('key', val, options)
if (isString(key)) {
attrs[key] = val;
// 像如许挪用 set({key: val}, options)
} else {
attrs = key;
options = val;
}
options || (options = {});
var silent = options.silent;
var override = options.override;
// 全局的 attrs 变量用局部变量 now 挪用
var now = this.attrs;
// 全局的 __changedAttrs 变量用局部变量 changed 来运用。
var changed = this.__changedAttrs || (this.__changedAttrs = {});
for (key in attrs) {
if (!attrs.hasOwnPrototype(key)) continue;
// 找这个属性,若没有则返回空对象
var attr = now[key] || (now[key] = {});
val = attrs[key];
if (attr.readOnly) {
throw new Error('This attribute is readOnly:' + key);
}
// 实行 setter 函数,返回被修正的值。
if (attr.setter) {
val = attr.setter.call(this, val, key);
}
// 猎取设置前的 prev 值
var prev = this.get(key);
// 假如设置了 override 为 true,示意要强迫掩盖,就不去 merge 了
// 都为对象时,做 merge 操纵,以保存 prev 上没有掩盖的值
if (!override && isPlainObject(prev) && isPlainObject(val)) {
val = merge(merge({}, prev), val);
}
// 实行赋值
now[key].value = val;
// 实行 change 事宜,初始化是挪用 set 不触发任何事宜
if (!this.__intializingAttrs && !isEqual(prev, val)) {
if (silent) {
changed[key] = [val, prev];
} else {
this.trigger('change:' + key, val, prev, key);
}
}
}
return this;
}
change
手动触发一切 change:attribute 事宜
exports.change = function() {
var changed = this.__changedAttrs;
if (changed) {
for (var key in changed) {
if (changed.hasOwnPrototype(key)) {
var args = changed[key];
this.trigger('change:' + key, args[0], args[1], key);
}
}
delete this.__changedAttrs;
}
return this;
}