摘要:先來看源碼中,首先是做的是參數(shù)的處理工作,針對(duì)某些參數(shù)未傳的情況作了調(diào)整,最后達(dá)到的效果是的值為傳入的父類構(gòu)造函數(shù),如果沒有,設(shè)為。下一個(gè)語句其作用是處理父類構(gòu)造函數(shù)沒有修改的屬性值并且有方法的時(shí)候,在上調(diào)用方法。
本文同步自我的GitHub
概述Arale是支付寶開發(fā)的一套基礎(chǔ)類庫,提供了一整套前端模塊架構(gòu),基于CMD規(guī)范,所有模塊均是以sea.js的標(biāo)準(zhǔn)進(jìn)行開發(fā)。其開發(fā)過程借鑒了優(yōu)秀的開源類庫如jQuery, underscore等的經(jīng)驗(yàn),并融合發(fā)展,最后建立了一套自己的開發(fā)機(jī)制。
結(jié)構(gòu)Arale |--基礎(chǔ)設(shè)施 | |-- Base | |-- Class | |-- Events | `-- Widget |--工具 | |-- Cookie | |-- Detector | |-- Dnd | |-- Easing | |-- Iframe-Shim | |-- Messenger | |-- Name-Storage | |-- Position | |-- Qrcode | |-- Sticky | |-- Templatable | `-- Upload `--UI組件 |-- Autocomplete |-- Calendar |-- Dialog |-- Overlay |-- Popup |-- Switchable |-- Select |-- Tip `-- Validator開篇明義
這是本系列的第一篇,對(duì)于Arale中每個(gè)模塊的分析文章將采取同樣的結(jié)構(gòu)。前半部分是帶注釋源碼,在模塊源碼中會(huì)添加盡可能詳細(xì)的注釋。后半部分則是分析,針對(duì)模塊的運(yùn)作方式進(jìn)行具體分析。
帶注釋源碼// The base Class implementation. // 基礎(chǔ)Class類的實(shí)現(xiàn),Class類作為返回的對(duì)象,本身也可接受對(duì)象參數(shù) function Class(o) { // Convert existed function to Class. // 將現(xiàn)有的函數(shù)轉(zhuǎn)換為Class類 if (!(this instanceof Class) && isFunction(o)) { return classify(o) } } 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" // } // }) // /** * 創(chuàng)建Class子類 * @param {Function} parent 要繼承的父類的構(gòu)造函數(shù) * @param {Object} properties 包含要混入屬性的對(duì)象 * @return {Function} 生成子類的構(gòu)造函數(shù) */ Class.create = function(parent, properties) { // 首先對(duì)第一個(gè)參數(shù)進(jìn)行類型驗(yàn)證,是否為函數(shù) if (!isFunction(parent)) { // 如不是函數(shù),將值賦給properties,再將parent設(shè)為null properties = parent parent = null } // 如properties是undefined或null等為false的值,將properties設(shè)為空對(duì)象 properties || (properties = {}) // 如parent為null,且properties有Extends屬性,則將Extends屬性的值賦給parent, // 如properties沒有Extends屬性,則將Class賦給parents,以Class為父類 parent || (parent = properties.Extends || Class) // 將parents賦給properties,如原來properties無Extends屬性,此時(shí)其Extends屬性將為父類構(gòu)造函數(shù)或Class properties.Extends = parent // The created class constructor // 用作生成子類的構(gòu)造函數(shù)雛形 function SubClass() { // Call the parent constructor. // 在this上調(diào)用父類構(gòu)造函數(shù) parent.apply(this, arguments) // Only call initialize in self constructor. // 當(dāng)this.constructor為SubClass本身(即Parent的構(gòu)造函數(shù)未修改constuctor屬性值), // 及父類構(gòu)造函數(shù)中有initialize方法時(shí),在this上調(diào)用自身的initialize方法 if (this.constructor === SubClass && this.initialize) { this.initialize.apply(this, arguments) } } // Inherit class (static) properties from parent. // 從parent繼承類的靜態(tài)屬性 // 判斷parent不是Class if (parent !== Class) { // 將parent的靜態(tài)屬性混入SubClass中,如果parent有StaticsWhiteList屬性,則復(fù)制其指定的屬性。 mix(SubClass, parent, parent.StaticsWhiteList) } // Add instance properties to the subclass. // 調(diào)用implement方法,具體操作見implement函數(shù)注釋 implement.call(SubClass, properties) // Make subclass extendable. // 最后,對(duì)SubClass構(gòu)造函數(shù)進(jìn)行classify操作,在SubClass上添加extend和implement這兩個(gè)Class類特有的方法,然后返回出去 return classify(SubClass) } /** * 使子類混入屬性或調(diào)用一些特殊的方法,這個(gè)方法只有在構(gòu)建SubClass時(shí)的時(shí)候才會(huì)有用,所以沒有掛載到Class上 * @param {Object} properties 包含某些屬性的對(duì)象 */ function implement(properties) { var key, value // 遍歷properties中的屬性 for (key in properties) { // 暫存properties中屬性對(duì)應(yīng)的屬性值 value = properties[key] // 如果Class類的工具方法中有同名方法,則在this上調(diào)用該方法,暫存的value值作為參數(shù) if (Class.Mutators.hasOwnProperty(key)) { Class.Mutators[key].call(this, value) } else { // 如沒有同名方法,則進(jìn)行簡單的賦值操作 this.prototype[key] = value } } } // Create a sub Class based on `Class`. // 以Class類或調(diào)用extend方法的類為父類,生成混入properties屬性的子類 Class.extend = function(properties) { // 如不存在properties,給properties賦空對(duì)象作為默認(rèn)值 properties || (properties = {}) // 將properties的Extends屬性設(shè)為this,表示以this為父類 properties.Extends = this // 調(diào)用create方法返回新的子類 return Class.create(properties) } // 給cls添加`Class.extend`和`implement`方法 function classify(cls) { cls.extend = Class.extend cls.implement = implement return cls } // Mutators define special properties. // Class類自有的一些方法,保存在Class的一些屬性上,子類不會(huì)繼承,只是作為構(gòu)建子類時(shí)的工具函數(shù)使用 Class.Mutators = { /** * SubClass調(diào)用此方法,在原型上添加父類原型上的方法 * @param {Function} parent 要生成子類的父類構(gòu)造函數(shù) */ "Extends": function(parent) { // 保存this的原型對(duì)象 var existed = this.prototype // 創(chuàng)建一個(gè)以parent.prototype為原型的空對(duì)象 var proto = createProto(parent.prototype) // Keep existed properties. // 在proto這個(gè)空對(duì)象上混入this的原型對(duì)象上的屬性 mix(proto, existed) // Enforce the constructor to be what we expect. // proto的constructor指向this,為了構(gòu)造正確的原型鏈 proto.constructor = this // Set the prototype chain to inherit from `parent`. // 將proto賦給this的prototype對(duì)象,這樣this的prototype上既有原有的屬性,又有Extend的類的原型對(duì)象上的屬性 this.prototype = proto // Set a convenience property in case the parent"s prototype is // needed later. // 將父類的prototye保存為this的superclass屬性,可以通過superclass快速訪問 this.superclass = parent.prototype }, /** * 從某些類中混入屬性 * @param {Array|Function} items 包含提供屬性的類的數(shù)組 */ "Implements": function(items) { // 檢測參數(shù)類型,單個(gè)構(gòu)造函數(shù)用數(shù)組包裹 isArray(items) || (items = [items]) // 保存子類的原型對(duì)象 var proto = this.prototype, item // 循環(huán)遍歷 while (item = items.shift()) { // 將item原型對(duì)象中的屬性混入子類原型對(duì)象中,如item沒有原型對(duì)象,則item是包含需混入的屬性的對(duì)象,直接mix即可 mix(proto, item.prototype || item) } }, // 將屬性作為靜態(tài)屬性加入子類,這些屬性不會(huì)被繼續(xù)繼承 "Statics": function(staticProperties) { mix(this, staticProperties) } } // Shared empty constructor function to aid in prototype-chain creation. // 無constructor的空函數(shù),用于原型鏈的構(gòu)造。 function Ctor() { } // See: http://jsperf.com/object-create-vs-new-ctor // 工具函數(shù),返回一個(gè)以proto為原型的空對(duì)象 var createProto = Object.__proto__ ? function(proto) { return { __proto__: proto } } : function(proto) { Ctor.prototype = proto return new Ctor() } // Helpers // 工具方法 // ------------ /** * 將s中的屬性混入r * @param {Object} r 接受復(fù)制對(duì)象 * @param {Object} s 被復(fù)制對(duì)象 * @param {Array} wl 白名單,用于特別指定要復(fù)制的屬性 */ function mix(r, s, wl) { // Copy "all" properties including inherited ones. // 將s對(duì)象的所有屬性,包括繼承的屬性,全部復(fù)制到新的r對(duì)象中 for (var p in s) { if (s.hasOwnProperty(p)) { if (wl && indexOf(wl, p) === -1) continue // 在 iPhone 1 代等設(shè)備的 Safari 中,prototype 也會(huì)被枚舉出來,需排除 if (p !== "prototype") { r[p] = s[p] } } } } // 對(duì)Object.prototype.toString方法的引用 var toString = Object.prototype.toString // 檢測是否為數(shù)組方法 var isArray = Array.isArray || function(val) { return toString.call(val) === "[object Array]" } // 檢測是否為函數(shù)方法 var isFunction = function(val) { return toString.call(val) === "[object Function]" } // 查詢?cè)卦跀?shù)組中的索引值,如不存在則返回-1 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 }分析
Class類是整個(gè)Arale類庫的基礎(chǔ),所有在Arale中使用到的類都是由Class構(gòu)建的,因?yàn)槠錁?gòu)建的所有類都包含特定的方法,有特殊性,是根據(jù)Arale的需要定制的。所有基于Arale的開發(fā)都要遵循Class類的規(guī)定,可以說這個(gè)類是Arale生態(tài)圈的基石。
既然有官方文檔,具體使用方法就不用多說了,下面分析一下具體實(shí)現(xiàn)。
首先介紹一下模塊中的工具函數(shù),分別是:
mix() // 用于混入屬性的方法 toString() // 轉(zhuǎn)換為字符串類型的方法 isArray(), isFunction() 類型檢測方法 indexOf() // 計(jì)算元素在數(shù)組中索引值的方法
具體實(shí)現(xiàn)見源碼及注釋即可。
首先是Class函數(shù),這個(gè)函數(shù)是對(duì)外暴露的,所有方法都可以在它上面調(diào)用??稍贑lass上調(diào)用的方法只有兩個(gè),分別是Class.create()和Class.extend()。先來看Class.create():
源碼中,首先是做的是參數(shù)的處理工作,針對(duì)某些參數(shù)未傳的情況作了調(diào)整,最后達(dá)到的效果是parent的值為傳入的父類構(gòu)造函數(shù),如果沒有,設(shè)為null。properties為需要混入屬性的對(duì)象,其中可能有些Arale規(guī)定的特殊的屬性會(huì)進(jìn)行特殊處理,這個(gè)后面會(huì)說。
下面一步,針對(duì)parent為null的情況,parent為null時(shí),如properties中有Entends屬性,則將該屬性值賦給parent,如果沒有Extends,則將Class賦給parent。意思就是,有Extends屬性時(shí),屬性值作為子類的父類,如果沒有,Class作為父類。然后將parent回頭賦給properties.Extends,這是針對(duì)parent為Class的情況。
再往后聲明了子類的構(gòu)造函數(shù)雛形——SubClass函數(shù),在函數(shù)內(nèi)首先在this上調(diào)用parent的構(gòu)造函數(shù)。下一個(gè)if語句:
if (this.constructor === SubClass && this.initialize) { this.initialize.apply(this, arguments) }
其作用是處理父類構(gòu)造函數(shù)沒有修改this的constructor屬性值并且有initialize方法的時(shí)候,在this上調(diào)用initialize方法。這個(gè)多數(shù)情況下不會(huì)執(zhí)行。下一步則是在parent不為Class時(shí)執(zhí)行,將parent的靜態(tài)屬性賦給SubClass,可以通過StaticWhiteList參數(shù)特別指定要復(fù)制的屬性。
接下來是關(guān)鍵一步,也是我認(rèn)為整個(gè)Class類中技巧最高的一步。在SubClass上調(diào)用implement方法,該方法中,對(duì)properties進(jìn)行遍歷,將properties中的每個(gè)屬性值和Class.Mutators中的屬性值進(jìn)行對(duì)比,Class.Mutators對(duì)象中保存的都是一些特殊的方法,這些方法可以以屬性的方式寫在properties參數(shù)中,當(dāng)遇到特定名稱的屬性時(shí),就會(huì)在SubClass上調(diào)用Class.Mutators中的同名方法,并且properties中對(duì)應(yīng)的屬性值會(huì)作為該方法的參數(shù)傳入。而不存在于Class.Mutators中的屬性,則會(huì)執(zhí)行一般的賦值操作賦給SubClass。這種方法巧妙地將預(yù)設(shè)的方法和需要混入的屬性通過同一種方式傳入,降低了API的復(fù)雜性,提高了方法的靈活度。同樣的技巧我在糖餅的artDialog源碼中也看到過,不知道是不是受了Arale的啟發(fā)。
最后返回“加工”后的SubClass,當(dāng)然最后執(zhí)行了一個(gè)classify()方法,作用就是在SubClass上加入extend和implement方法,讓子類也可以擁有這些方法。
Class.Mutators中的方法具體實(shí)現(xiàn)就不說了,看注釋即可,反正都是在SubClass上調(diào)用的。
至于Class.extend(每個(gè)子類都有的)方法,最后其實(shí)調(diào)用的還是Class.create,只是對(duì)properties做了一些處理,方便由子類直接調(diào)用再生成子類的一種簡化API,免得再寫一次類似Class.create(SubClass, properties)這么長的語句。
構(gòu)造過程中,對(duì)原型鏈的處理是比較重要的一個(gè)環(huán)節(jié),這是JavaScript的一大特色,注意一下就好。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/85357.html
摘要:本文同步自我的博客前言這個(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)部...
摘要:擁有了和方法的三個(gè)變種屬性這三個(gè)屬性會(huì)做特殊處理繼承的方法,只支持單繼承建立原型鏈來實(shí)現(xiàn)繼承強(qiáng)制改變構(gòu)造函數(shù)提供語法糖,來調(diào)用父類屬性混入屬性,可以混入多個(gè)類的屬性將參數(shù)變成數(shù)組無論參數(shù)是類,還是對(duì)象,都混入。 更新:讀 arale 源碼之 attribute 篇 arale 是阿里、開源社區(qū)明星人物--玉伯,開發(fā)的一套組件,代碼相當(dāng)優(yōu)美,大贊玉伯的開源精神,我是您的粉絲。 這里分享下...
摘要:帶注釋源碼用于分割事件名的正則,識(shí)別空格介紹使用方法,這個(gè)模塊可以混入任何對(duì)象之中,實(shí)現(xiàn)對(duì)自定義事件的資瓷將空格分割的事件綁定給對(duì)象,事件名為的話,事件回調(diào)函數(shù)在任何事件被觸發(fā)時(shí)都會(huì)調(diào)用。 帶注釋源碼 // Regular expression used to split event strings // 用于分割事件名的正則,識(shí)別空格 var eventSplitter = /s+...
摘要:系列文章讀源碼之篇提供基本的屬性添加獲取移除等功能。判斷是否為等對(duì)象特性檢查閉包實(shí)現(xiàn)塊作用域,不污染全局變量。找這個(gè)屬性,若沒有則返回空對(duì)象執(zhí)行函數(shù),返回被修改的值。 系列文章:讀 arale 源碼之 class 篇 attributes 提供基本的屬性添加、獲取、移除等功能。它是與實(shí)例相關(guān)的狀態(tài)信息,可讀可寫,發(fā)生變化時(shí),會(huì)自動(dòng)觸發(fā)相關(guān)事件 先來了解一下 Attribute 模塊要實(shí)...
摘要:在方法執(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...
閱讀 3395·2021-10-13 09:40
閱讀 2619·2021-10-08 10:17
閱讀 4030·2021-09-28 09:45
閱讀 960·2021-09-28 09:35
閱讀 1846·2019-08-30 10:51
閱讀 2931·2019-08-26 12:11
閱讀 1677·2019-08-26 10:41
閱讀 3120·2019-08-23 17:10