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