摘要:系列文章讀源碼之篇提供基本的屬性添加獲取移除等功能。判斷是否為等對(duì)象特性檢查閉包實(shí)現(xiàn)塊作用域,不污染全局變量。找這個(gè)屬性,若沒(méi)有則返回空對(duì)象執(zhí)行函數(shù),返回被修改的值。
系列文章:讀 arale 源碼之 class 篇
attributes 提供基本的屬性添加、獲取、移除等功能。它是與實(shí)例相關(guān)的狀態(tài)信息,可讀可寫,發(fā)生變化時(shí),會(huì)自動(dòng)觸發(fā)相關(guān)事件
先來(lái)了解一下 Attribute 模塊要實(shí)現(xiàn)的功能:
設(shè)置屬性值
{ attr1: "hello1", // 相當(dāng)于 attr1 的方式 attr2: { value: "hello2" } }
在屬性設(shè)置和獲取前,觸發(fā)一個(gè)函數(shù)來(lái)先處理屬性
{ attr3: { value: "hello3", setter: function(v) { return v + ""; }, getter: function(v) { return v * 6; } } }
只讀屬性
{ attr4: { value: 1, readOnly: true } }
屬性發(fā)生改變時(shí)自動(dòng)觸發(fā)相關(guān)事件
{ _onChangeAttr1: function(val) { console.log("attr1 changed by" + val); } }
設(shè)置屬性時(shí),多了兩個(gè)狀態(tài)
// {silent: true} 綁定的 _onChangeAttr1 不執(zhí)行 instance.set("attr1", 2, {silent: true}); // {override: true} 默認(rèn) false,對(duì)象混合,為 true 時(shí),直接覆蓋對(duì)象。 instance.set("attr2", {w: 12, h: 33}, {override: true});
接下來(lái)再去看看源碼怎么實(shí)現(xiàn)的!
initAttrs 初始化屬性initAttrs 將在實(shí)例化對(duì)象時(shí)調(diào)用。
exports.initAttrs = function(config) { // initAttrs 是在初始化時(shí)調(diào)用的,默認(rèn)情況下實(shí)例上肯定沒(méi)有 attrs,不存在覆蓋問(wèn)題 var attrs = this.attrs = {}; // 得到所有繼承的屬性 var specialProps = this.propsInAttrs || []; // 合并和克隆父類的 attrs 值到實(shí)例的 attrs 上 mergeInheritedAttrs(attrs, this, specialProps); // 從 config 中合并屬性 if (config) { mergeInheritedAttrs(atts, config); } // 對(duì)于有 setter 的屬性,要用初始值 set 一下,以保證關(guān)聯(lián)屬性也一同初始化 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); }來(lái)看看 initAttrs 中用到的幾個(gè)方法
function merge(receiver, supplier) { var key, value; for (key in supplier) { if (supplier.hasOwnPrototype(key)) { value = supplier[key]; // 只 clone 數(shù)組和 簡(jiǎn)單對(duì)象,其他保持不變 if (isArray(value)) { // 重新返回一個(gè)數(shù)組,不會(huì)占用同一個(gè)內(nèi)存地址 value = value.slice(); } else if (isPlainObject(value)) { // 接收者合并之前的值不是簡(jiǎn)單對(duì)象的話,將其設(shè)置為空對(duì)象,即覆蓋之前的值。 var prev = receiver[key]; isPlainObject(prev) || (prev = {}); // 如果是簡(jiǎn)單對(duì)象的話,混合他們的值 value = merge(prev, value); } receiver[key] = value; } } return receiver; }
// 什么是簡(jiǎn)單對(duì)象? 使用 {} 或者 new Object 創(chuàng)建。for-in 遍歷時(shí)只包含自身屬性的對(duì)象。 function isPlainObject(o) { // 首先必須是對(duì)象,然后要排除 DOM 對(duì)象和 Window 對(duì)象 if (!o || toString.call(o) != "[object Object]" || o.nodeType || isWindow(o)) { return false; } try { // Object 沒(méi)有屬于自己的 constructor if (o.constructor && !hasOwn.call(o, "constructor") && !hasOwn.call(o.constructor.prototype, "isPrototypeOf")) { return false; } } catch (e) { // IE8,9 會(huì)拋出異常 return false; } var key; // 如果是 IE9 以下. iteratesOwnLast 是特性檢查。 if (iteratesOwnLast) { // ie9 以下,遍歷時(shí)不會(huì)優(yōu)先遍歷自身屬性,如果第一個(gè)屬性是自身的,說(shuō)明所有屬性都是自身的。 for (key in o) { return hasOwn.call(o, key); } } // 自身屬性會(huì)優(yōu)先遍歷,然后才是原型鏈上的,如果最后一個(gè)屬性都是自身的,說(shuō)明所有屬性都是自身的。 for (key in o) {} // 除了 IE9 以下的瀏覽器,其它瀏覽器都不允許修改 undefined 關(guān)鍵字,所以這樣直接用也沒(méi)問(wèn)題。 return key === undefined || hasOwn.call(o, key); } // 判斷是否為 window top self 等對(duì)象 function isWindow(o) { return o != null && o == o.window; } // ie < 9 特性檢查, 閉包實(shí)現(xiàn)塊作用域,不污染全局變量。 (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"; })();
/* * supplier: 提供者; receiver: 接收者; specialProps: 指定提供者身上的屬性,相當(dāng)于白名單 */ 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]; } } }
// 遍歷實(shí)例的原型鏈,將 attrs 屬性合并 function mergeInheritedAttrs(attrs, instance, specialProps) { var inherited = []; var proto = instance.constructor.prototype; // 遍歷實(shí)例的原型鏈, 查找 attrs 屬性。并將其添加到 inherited 數(shù)組中。 while (proto) { // 原型鏈上若是沒(méi)有 attrs 屬性的話,將其設(shè)為空對(duì)象 if (!proto.hasOwnPrototype("attrs")) { proto.attrs = {}; } // 將 proto 上的特殊 properties 放到 proto.attrs 上,以便合并 copySpecialProps(specialProps, proto.attrs, proto); // 為空時(shí)不添加 if (!isEmptyObject(proto.attrs)) { // 將 proto.attrs 添加到 inherited 數(shù)組中,從頭部放。類似 stack(棧)的結(jié)構(gòu),后進(jìn)先出 inherited.unshift(proto.attrs); } // 繼續(xù)查找原型鏈,直至 Class.superclass 時(shí),為 undefined 值終止循環(huán) proto = proto.constructor.superclass; } // 合并和克隆繼承的值到實(shí)例上 for (var i = 0, len = inherited.length; i < len; i++) { merge(attrs, normalize(inherited[i])); } }
對(duì)于有 setter 的屬性,要用初始值 set 一下,以保證關(guān)聯(lián)屬性也一同初始化
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 (繼承的也可以),那么用初始值設(shè)置一下。 host.set(key, config[key], options); } } } delete host.__initializingAttrs; }
綁定 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 的話,調(diào)用 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 設(shè)置屬性的值
并且觸發(fā) change 綁定事件,除非 silent 為 true
exports.set = function(key, val, options) { var attrs = {}; // 像這樣調(diào)用 set("key", val, options) if (isString(key)) { attrs[key] = val; // 像這樣調(diào)用 set({key: val}, options) } else { attrs = key; options = val; } options || (options = {}); var silent = options.silent; var override = options.override; // 全局的 attrs 變量用局部變量 now 調(diào)用 var now = this.attrs; // 全局的 __changedAttrs 變量用局部變量 changed 來(lái)使用。 var changed = this.__changedAttrs || (this.__changedAttrs = {}); for (key in attrs) { if (!attrs.hasOwnPrototype(key)) continue; // 找這個(gè)屬性,若沒(méi)有則返回空對(duì)象 var attr = now[key] || (now[key] = {}); val = attrs[key]; if (attr.readOnly) { throw new Error("This attribute is readOnly:" + key); } // 執(zhí)行 setter 函數(shù),返回被修改的值。 if (attr.setter) { val = attr.setter.call(this, val, key); } // 獲取設(shè)置前的 prev 值 var prev = this.get(key); // 如果設(shè)置了 override 為 true,表示要強(qiáng)制覆蓋,就不去 merge 了 // 都為對(duì)象時(shí),做 merge 操作,以保留 prev 上沒(méi)有覆蓋的值 if (!override && isPlainObject(prev) && isPlainObject(val)) { val = merge(merge({}, prev), val); } // 執(zhí)行賦值 now[key].value = val; // 執(zhí)行 change 事件,初始化是調(diào)用 set 不觸發(fā)任何事件 if (!this.__intializingAttrs && !isEqual(prev, val)) { if (silent) { changed[key] = [val, prev]; } else { this.trigger("change:" + key, val, prev, key); } } } return this; }change
手動(dòng)觸發(fā)所有 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; }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/86012.html
摘要:擁有了和方法的三個(gè)變種屬性這三個(gè)屬性會(huì)做特殊處理繼承的方法,只支持單繼承建立原型鏈來(lái)實(shí)現(xiàn)繼承強(qiáng)制改變構(gòu)造函數(shù)提供語(yǔ)法糖,來(lái)調(diào)用父類屬性混入屬性,可以混入多個(gè)類的屬性將參數(shù)變成數(shù)組無(wú)論參數(shù)是類,還是對(duì)象,都混入。 更新:讀 arale 源碼之 attribute 篇 arale 是阿里、開(kāi)源社區(qū)明星人物--玉伯,開(kāi)發(fā)的一套組件,代碼相當(dāng)優(yōu)美,大贊玉伯的開(kāi)源精神,我是您的粉絲。 這里分享下...
摘要:本文同步自我的博客前言這個(gè)模塊實(shí)際上才是模塊系統(tǒng)中對(duì)外的模塊,它包含了之前介紹的類和類,以及自己內(nèi)部的模塊和模塊,因此模塊是真正的基礎(chǔ)類。這兩個(gè)方法的作用就是針對(duì)類上的某個(gè)方法,給這個(gè)方法綁定先于其執(zhí)行和后于其執(zhí)行的回調(diào)函數(shù)。 本文同步自我的GitHub博客 前言 Base這個(gè)模塊實(shí)際上才是Arale模塊系統(tǒng)中對(duì)外的模塊,它包含了之前介紹的Class類和Events類,以及自己內(nèi)部...
摘要:在方法執(zhí)行后,再執(zhí)行函數(shù)函數(shù)在執(zhí)行時(shí),接收的參數(shù)第一個(gè)是的返回值,之后的參數(shù)和傳給相同。的返回值源碼定義兩個(gè)出口定義一個(gè)可柯里化的函數(shù),柯里化成函數(shù)指向基于生成的類的實(shí)例,如上例的如果該函數(shù)是第一次切面化綁定,則包裝該函數(shù)。 系列文章:讀 arale 源碼之 class 篇 使用 Aspect,可以允許你在指定方法執(zhí)行的前后插入特定函數(shù) before object.before(me...
摘要:先來(lái)看源碼中,首先是做的是參數(shù)的處理工作,針對(duì)某些參數(shù)未傳的情況作了調(diào)整,最后達(dá)到的效果是的值為傳入的父類構(gòu)造函數(shù),如果沒(méi)有,設(shè)為。下一個(gè)語(yǔ)句其作用是處理父類構(gòu)造函數(shù)沒(méi)有修改的屬性值并且有方法的時(shí)候,在上調(diào)用方法。 本文同步自我的GitHub 概述 Arale是支付寶開(kāi)發(fā)的一套基礎(chǔ)類庫(kù),提供了一整套前端模塊架構(gòu),基于CMD規(guī)范,所有模塊均是以sea.js的標(biāo)準(zhǔn)進(jìn)行開(kāi)發(fā)。其開(kāi)發(fā)過(guò)程借...
摘要:今天我偷偷的去上喵了幾眼,發(fā)現(xiàn)我捉出來(lái)的,還沒(méi)被消滅掉,看來(lái)有必要發(fā)個(gè)郵件給高陽(yáng)了。還得到了高陽(yáng)的飯約,因?yàn)槲覀冏搅撕脦讉€(gè)的,高陽(yáng)你別忘啊下面就上幾張我們的的靚照吧的獲獎(jiǎng)自之后,我們還是很有自信能得個(gè)獎(jiǎng)的。 BugLife的誕生記 先自我介紹下我們團(tuán)隊(duì),Wellming、Bell orchid、Retamia,三枚程序員,主要技能點(diǎn)PHP,目前在一家棒棒的做開(kāi)源網(wǎng)校系統(tǒng)(EduSoho...
閱讀 2571·2021-11-24 09:38
閱讀 2601·2019-08-30 15:54
閱讀 915·2019-08-30 15:52
閱讀 1909·2019-08-30 15:44
閱讀 2713·2019-08-30 13:48
閱讀 768·2019-08-29 16:21
閱讀 996·2019-08-29 14:03
閱讀 2212·2019-08-28 18:15