Attribute的源码解析:

--注:这部分的源码阅读起来比较费劲,可能解析的还不太到位,后续会多读几遍增添新的解释,后面随时更新。

// 负责 attributes 的初始化
// attributes 是与实例相关的状态信息,可读可写,发生变化时,会自动触发相关事件
exports.initAttrs = function(config) {// initAttrs 是在初始化时调用的,默认情况下实例上肯定没有 attrs,不存在覆盖问题// 在Base的initialize方法里进行调用,因此不存在覆盖的问题var attrs = this.attrs = {};// Get all inherited attributes.// 获取所有需要继承的属性,specialProps是一个数组,里面存的是属性名称var specialProps = this.propsInAttrs || [];// 该方法的注释见下面,用于将所有的特殊指定的属性格式化固定格式后扩展到attrs里面mergeInheritedAttrs(attrs, this, specialProps);// Merge user-specific attributes from config.if (config) {mergeUserValue(attrs, config);}// 对于有 setter 的属性,要用初始值 set 一下,以保证关联属性也一同初始化setSetterAttrs(this, attrs, config);// Convert `on/before/afterXxx` config to event handler.// 转化`on/before/afterXxx`的配置转化为事件处理parseEventsFromAttrs(this, attrs);// 将 this.attrs 上的 special properties 放回 this 上copySpecialProps(specialProps, this, attrs, true);
};
// Get the value of an attribute.
// 从Base实例属性attrs里取对应的key值,都是:
// {
//  value:,
//  getter:,
//  setter:
//}格式,有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 a hash of model attributes on the object, firing `"change"` unless you choose to silence it.
// 在对象上set模型属性hash值的时候默认会触发“change”事件,你也可以选择不触发
exports.set = function(key, val, options) {var attrs = {};// set("key", val, options)格式if (isString(key)) {attrs[key] = val;}// set({ "key": val, "key2": val2 }, options)格式(此时只有两个参数或一个,options可选)else {attrs = key;options = val;}// 给options赋默认值防报错options || (options = {});// 设置了silent则不会触发change事件var silent = options.silent;var override = options.override;var now = this.attrs;// 纪录被修改过的属性var changed = this.__changedAttrs || (this.__changedAttrs = {});for (key in attrs) {// key必须是attrs的实例属性if (!attrs.hasOwnProperty(key)) continue;// 纪录被修改前的attr值var attr = now[key] || (now[key] = {});val = attrs[key]; // 对应要设置的val值if (attr.readOnly) {// 只读的属性不允许被setthrow new Error('This attribute is readOnly: ' + key);}// invoke setter 调用setterif (attr.setter) {val = attr.setter.call(this, val, key);}// 获取设置前的 prev 值var prev = this.get(key);// 获取需要设置的 val 值// 如果设置了 override 为 true,表示要强制覆盖,就不去 merge 了// 都为对象时,做 merge 操作,以保留 prev 上没有覆盖的值if (!override && isPlainObject(prev) && isPlainObject(val)) {val = merge(merge({},prev), val);}// set finallynow[key].value = val;// invoke change event// 初始化时对 set 的调用,不触发任何事件// 初始化的时候会调用setSetterAttrs方法,在该方法内会调用set方法,此时this.__initializingAttrs为trueif (!this.__initializingAttrs && !isEqual(prev, val)) {if (silent) {changed[key] = [val, prev];} else {this.trigger('change:' + key, val, prev, key);}}}return this;
};
// Call this method to manually fire a `"change"` event for triggering
// a `"change:attribute"` event for each changed attribute.
// 为所有改变过的属性触发对应的事件
exports.change = function() {var changed = this.__changedAttrs;if (changed) {for (var key in changed) {if (changed.hasOwnProperty(key)) {var args = changed[key];this.trigger('change:' + key, args[0], args[1], key);}}// 触发完后删除该属性以免重复触发delete this.__changedAttrs;}return this;
};
// for test
exports._isPlainObject = isPlainObject;
// Helpers
// -------
var toString = Object.prototype.toString;
var hasOwn = Object.prototype.hasOwnProperty;
/**
* Detect the JScript [[DontEnum]] bug:
* In IE < 9 an objects own properties, shadowing non-enumerable ones, are
* made non-enumerable as well.
* https://github.com/bestiejs/lodash/blob/7520066fc916e205ef84cb97fbfe630d7c154158/lodash.js#L134-L144
*/
/** Detect if own properties are iterated after inherited properties (IE < 9) */
//  检测如果自持有的属性是否在被继承属性后被迭代
var iteratesOwnLast;
(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';
} ());
var isArray = Array.isArray ||
function(val) {return toString.call(val) === '[object Array]';
};
function isString(val) {return toString.call(val) === '[object String]';
}
function isFunction(val) {return toString.call(val) === '[object Function]';
}
function isWindow(o) {return o != null && o == o.window;
}
function isPlainObject(o) {// Must be an Object.// Because of IE, we also have to check the presence of the constructor// property. Make sure that DOM nodes and window objects don't pass through, as well// o必须是一个对象。因为IE,我们不得不检查constructor属性的存在性。我们也要保证DOM节点对象以及window对象也不能通过检测。if (!o || toString.call(o) !== "[object Object]" || o.nodeType || isWindow(o)) {return false;}try {// Not own constructor property must be Object// 没有直接持有constructor属性的必定是对象if (o.constructor && !hasOwn.call(o, "constructor") && !hasOwn.call(o.constructor.prototype, "isPrototypeOf")) {return false;}} catch(e) {// IE8,9 Will throw exceptions on certain host objects #9897return false;}var key;// Support: IE<9// Handle iteration over inherited properties before own properties.// http://bugs.jquery.com/ticket/12199// IE<9的情况下处理继承属性在自己拥有的属性前被迭代if (iteratesOwnLast) {for (key in o) {// 继承的属性会被先迭代,所以有继承的属性的返回false,没有则返回truereturn hasOwn.call(o, key);}}// Own properties are enumerated firstly, so to speed up,// if last one is own, then all properties are own.// o为{}空对象的时候key为undefined;因为现代浏览器枚举对象属性的顺序是按照先枚举自身的属性后枚举继承的属性,那么如果最后一个被迭代的属性不是继承属性,那么该对象符合条件for (key in o) {}return key === undefined || hasOwn.call(o, key);
}
// 空对象:{}
function isEmptyObject(o) {if (!o || toString.call(o) !== "[object Object]" || o.nodeType || isWindow(o) || !o.hasOwnProperty) {return false;}for (var p in o) {if (o.hasOwnProperty(p)) return false;}return true;
}
function merge(receiver, supplier) {var key, value;for (key in supplier) {if (supplier.hasOwnProperty(key)) {// 会对数组和plain 对象做复制,其他保持不变receiver[key] = cloneValue(supplier[key], receiver[key]);}}return receiver;
}
// 只 clone 数组和 plain object,其他的保持不变
function cloneValue(value, prev) {if (isArray(value)) {value = value.slice();} else if (isPlainObject(value)) {isPlainObject(prev) || (prev = {});value = merge(prev, value);}return value;
}var keys = Object.keys;
// 扩展Object的keys方法
if (!keys) {keys = function(o) {var result = [];for (var name in o) {if (o.hasOwnProperty(name)) {result.push(name);}}return result;};
}
// attrs:base实例的attrs属性;instance:Base实例;specialProps:存放特殊属性名的数组
function mergeInheritedAttrs(attrs, instance, specialProps) {// 用于存放非空的proto.attrsvar inherited = [];// 获取实例构造函数的原型var proto = instance.constructor.prototype;while (proto) {// 不要拿到 prototype 上的if (!proto.hasOwnProperty('attrs')) {// 如果没有attrs属性则将attrs赋值给protoproto.attrs = {};}// 将 proto 上的特殊 properties 放到 proto.attrs 上,以便合并// 简单的将proto的特殊属性复制到proto.attrscopySpecialProps(specialProps, proto.attrs, proto);// 为空时不添加if (!isEmptyObject(proto.attrs)) {inherited.unshift(proto.attrs);}// 向上回溯一级proto = proto.constructor.superclass;}// Merge and clone default values to instance.for (var i = 0,len = inherited.length; i < len; i++) {// normalize将对象置为固定格式// {// value: 'xx',// getter: fn,// setter: fn,// readOnly: boolean// }// 将所有继承过来的特殊属性merge给attrsmergeAttrs(attrs, normalize(inherited[i]));}
}
function mergeUserValue(attrs, config) {mergeAttrs(attrs, normalize(config, true), true);
}
function copySpecialProps(specialProps, receiver, supplier, isAttr2Prop) {for (var i = 0,len = specialProps.length; i < len; i++) {var key = specialProps[i];if (supplier.hasOwnProperty(key)) {receiver[key] = isAttr2Prop ? receiver.get(key) : supplier[key];}}
}
var EVENT_PATTERN = /^(on|before|after)([A-Z].*)$/;
var EVENT_NAME_PATTERN = /^(Change)?([A-Z])(.*)/;
// 从attrs里解析事件,由onChangeTitle|befoerChangeTitle|afterChangeTitle转化为监听事件或切面编程
function parseEventsFromAttrs(host, attrs) {for (var key in attrs) {if (attrs.hasOwnProperty(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];}}}
}
// Converts `Show` to `show` and `ChangeTitle` to `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;
}
function setSetterAttrs(host, attrs, config) {var options = {silent: true};host.__initializingAttrs = true;for (var key in config) {if (config.hasOwnProperty(key)) {if (attrs[key].setter) {host.set(key, config[key], options);}}}delete host.__initializingAttrs;
}
var ATTR_SPECIAL_KEYS = ['value', 'getter', 'setter', 'readOnly'];
// normalize `attrs` to
//
// {
// value: 'xx',
// getter: fn,
// setter: fn,
// readOnly: boolean
// }
//
function normalize(attrs, isUserValue) {var newAttrs = {};for (var key in attrs) {var attr = attrs[key];if (!isUserValue && isPlainObject(attr) && hasOwnProperties(attr, ATTR_SPECIAL_KEYS)) {newAttrs[key] = attr;continue;}// 用户数据只保存value值newAttrs[key] = {value: attr};}return newAttrs;
}
var ATTR_OPTIONS = ['setter', 'getter', 'readOnly'三个属性值赋给attr];
// 专用于 attrs 的 merge 方法
function mergeAttrs(attrs, inheritedAttrs, isUserValue) {var key, value;var attr;for (key in inheritedAttrs) {if (inheritedAttrs.hasOwnProperty(key)) {value = inheritedAttrs[key];attr = attrs[key];if (!attr) {// 如果attrs里没有对应的key值,则将其赋为一个空对象attr = attrs[key] = {};}// 从严谨上来说,遍历 ATTR_SPECIAL_KEYS 更好// 从性能来说,直接 人肉赋值 更快// 这里还是选择 性能优先// 只有 value 要复制原值,其他的直接覆盖即可(value['value'] !== undefined) && (attr['value'] = cloneValue(value['value'], attr['value']));// 如果是用户赋值,只要考虑valueif (isUserValue) continue;for (var i in ATTR_OPTIONS) {var option = ATTR_OPTIONS[i];if (value[option] !== undefined) {// 'setter', 'getter', 'readOnly'三个属性值赋给attrattr[option] = value[option];}}}}return attrs;
}
// 判断object对象是否有properties数组中的实例属性
function hasOwnProperties(object, properties) {for (var i = 0,len = properties.length; i < len; i++) {if (object.hasOwnProperty(properties[i])) {return true;}}return false;
}
// 对于 attrs 的 value 来说,以下值都认为是空值: null, undefined, '', [], {}
function isEmptyAttrValue(o) {return o == null || // null, undefined(isString(o) || isArray(o)) && o.length === 0 || // '', []isEmptyObject(o); // {}
}
// 判断属性值 a 和 b 是否相等,注意仅适用于属性值的判断,非普适的 === 或 == 判断。
// 也可参考underscore的isEqual()方法
function isEqual(a, b) {if (a === b) return true;if (isEmptyAttrValue(a) && isEmptyAttrValue(b)) return true;// Compare `[[Class]]` names.var className = toString.call(a);if (className != toString.call(b)) return false;switch (className) {// Strings, numbers, dates, and booleans are compared by value.case '[object String]':// Primitives and their corresponding object wrappers are// equivalent; thus, `"5"` is equivalent to `new String("5")`.return a == String(b);case '[object Number]':// `NaN`s are equivalent, but non-reflexive. An `equal`// comparison is performed for other numeric values.return a != +a ? b != +b: (a == 0 ? 1 / a == 1 / b: a == +b);case '[object Date]':case '[object Boolean]':// Coerce dates and booleans to numeric primitive values.// Dates are compared by their millisecond representations.// Note that invalid dates with millisecond representations// of `NaN` are not equivalent.return + a == +b;// RegExps are compared by their source patterns and flags.case '[object RegExp]':return a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase;// 简单判断数组包含的 primitive 值是否相等case '[object Array]':var aString = a.toString();var bString = b.toString();// 只要包含非 primitive 值,为了稳妥起见,都返回 falsereturn aString.indexOf('[object') === -1 && bString.indexOf('[object') === -1 && aString === bString;}if (typeof a != 'object' || typeof b != 'object') return false;// 简单判断两个对象是否相等,只判断第一层if (isPlainObject(a) && isPlainObject(b)) {// 键值不相等,立刻返回 falseif (!isEqual(keys(a), keys(b))) {return false;}// 键相同,但有值不等,立刻返回 falsefor (var p in a) {if (a[p] !== b[p]) return false;}return true;}// 其他情况返回 false, 以避免误判导致 change 事件没发生return false;
}

有了基础以后,Base的源代码则变的简单了许多:

var Class = require('arale-class');
var Events = require('arale-events');
var Aspect = require('./aspect');
var Attribute = require('./attribute');
module.exports = Class.create({Implements: [Events, Aspect, Attribute],initialize: function(config) {// 初始化,具体见Attribute的initAttrs方法this.initAttrs(config);// Automatically register `this._onChangeAttr` method as// a `change:attr` event handler.parseEventsFromInstance(this, this.attrs);},destroy: function() {// 解绑所有事件this.off();for (var p in this) {if (this.hasOwnProperty(p)) {// 删除所有的属性delete this[p];}}// Destroy should be called only once, generate a fake destroy after called// https://github.com/aralejs/widget/issues/50// 在第一次调用 destroy 后,生成一个空的 destroy,这样不会报错也不会多执行this.destroy = function() {};}
});
function parseEventsFromInstance(host, attrs) {for (var attr in attrs) {if (attrs.hasOwnProperty(attr)) {// 有_onChangeTitle格式的属性则添加对应的事件var m = '_onChange' + ucfirst(attr);if (host[m]) { // 对应的事件处理handlerhost.on('change:' + attr, host[m]);}}}
}
// 首字母大写
function ucfirst(str) {return str.charAt(0).toUpperCase() + str.substring(1);
}

Arale Base源码分析(含Attribute)相关推荐

  1. Spring_AOP架构介绍与源码分析(含事务深度分析)

    请见链接:http://edu.51cto.com/course/16573.html?source=so 第1章课程介绍6分钟1节 1-1课程介绍[免费试看]06:11 第2章AOP深入分析52分钟 ...

  2. 视频教程-经典Vue从入门到案例到源码分析教程(含资料)-Vue

    经典Vue从入门到案例到源码分析教程(含资料) 张长志技术全才.擅长领域:区块链.大数据.Java等.10余年软件研发及企业培训经验,曾为多家大型企业提供企业内训如中石化,中国联通,中国移动等知名企业 ...

  3. 集合之ArrayList(含JDK1.8源码分析)

    一.ArrayList的数据结构 ArrayList底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据.我们对ArrayList类的实例的所有的操作(增删改查等),其底层都 ...

  4. djangorestframework源码分析2:serializer序列化数据的执行流程

    djangorestframework源码分析 本文环境python3.5.2,djangorestframework (3.5.1)系列 djangorestframework源码分析-serial ...

  5. djangorestframework源码分析1:generics中的view执行流程

    djangorestframework源码分析 本文环境python3.5.2,djangorestframework (3.5.1)系列 djangorestframework源码分析-generi ...

  6. Django源码分析9:model.py表结构的初始化概述

    django源码分析 本文环境python3.5.2,django1.10.x系列 django源码分析-model概述 Django项目中提供了内置的orm框架,只需要在models.py文件中添加 ...

  7. celery源码分析-Task的初始化与发送任务

    celery源码分析 本文环境python3.5.2,celery4.0.2,django1.10.x系列 celery的任务发送 在Django项目中使用了装饰器来包装待执行任务, from cel ...

  8. celery源码分析-wroker初始化分析(上)

    celery源码分析 本文环境python3.5.2,celery4.0.2,django1.10.x系列 celery与Django的配合使用 首先,在安装有django的环境中创建一个django ...

  9. tornado源码分析

    tornado源码分析 本源码为tornado1.0版本 源码附带例子helloworld import tornado.httpserver import tornado.ioloop import ...

最新文章

  1. 何为优秀的机器学习特征 zz
  2. SSM整合项目中使用百度Ueditor遇到的问题。
  3. js文件中声明的一个json对象, 在另一个js文件中可以直接使用。
  4. 车流检测之halcon光流法算法实现
  5. python做数据和大数据区别_不懂Python,不懂大数据的人,和咸鱼有什么区别?
  6. 6种字符串数组的java排序 (String array sort)
  7. 三维旋转四元数系列(3.四元数定义与基本性质)
  8. win10自动切换日夜模式
  9. python c语言接口_C/C++ 提供 Python 接口
  10. Java内部类相关问题的总结与体会
  11. 3项目里面全局用less变量 cli vue_Vue.js构建工具比较
  12. 兴义智力象机器人_中科院科普讲师专家赴黔西南州做科普报告巡讲
  13. 自相关法基音提取的matlab程序,自相关函数法基音检测matlab程序
  14. 【蓝桥云课】最大公约数与最小公倍数
  15. 顶点缓冲区与着色器 (The Cherno + LeranOpenGL)笔记
  16. 保利威荣获「年度最佳直播服务商」!引领视频直播行业创新发展
  17. Ambari User Guide
  18. Copying DNA 复制DNA
  19. 变配电站综合监控平台
  20. svn取消文件夹图标_如何去掉svn文件夹上面的符号

热门文章

  1. 某博数据挖掘:使用Scrapy构建自定义数据采集提取洞察信息
  2. c语言 inc文件夹,汇编 inc 和 dec 指令
  3. css3 的filer 功能
  4. 蓝奏云分享文件无法下载地址失效的解决办法
  5. 用计算机弹起风了歌词,买辣椒也用券
  6. 【前端三剑客三】 JavaScript
  7. Git 学会git,探索GitHub,掌握新知识 (二)
  8. 训练模型时候显存爆炸的一种可能性以及解决办法
  9. 人脸识别特征脸提取PCA算法
  10. Elasticsearch深度探秘搜索技术如何手动控制全文检索结果的精准度