Arale源码剖析(1)——Class

本文同步自我的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函数,这个函数是对外暴露的,一切要领都能够在它上面挪用。可在Class上挪用的要领只要两个,分别是Class.create()Class.extend()。先来看Class.create()

源码中,起首是做的是参数的处置惩罚工作,针对某些参数未传的状况作了调解,末了到达的结果是parent的值为传入的父类组织函数,假如没有,设为nullproperties为须要混入属性的对象,个中能够有些Arale划定的迥殊的属性会举行迥殊处置惩罚,这个背面会说。

下面一步,针对parentnull的状况,parentnull时,如properties中有Entends属性,则将该属性值赋给parent,假如没有Extends,则将Class赋给parent。意义就是,有Extends属性时,属性值作为子类的父类,假如没有,Class作为父类。然后将parent转头赋给properties.Extends,这是针对parentClass的状况。

再今后声清楚明了子类的组织函数雏形——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上到场extendimplement要领,让子类也能够具有这些要领。

Class.Mutators中的要领详细完成就不说了,看解释即可,横竖都是在SubClass上挪用的。

至于Class.extend(每一个子类都有的)要领,末了实在挪用的照样Class.create,只是对properties做了一些处置惩罚,轻易由子类直接挪用再天生子类的一种简化API,以免再写一次相似Class.create(SubClass, properties)这么长的语句。

组织历程当中,对原型链的处置惩罚是比较主要的一个环节,这是JavaScript的一大特征,注重一下就好。

    原文作者:classicemi
    原文地址: https://segmentfault.com/a/1190000000758335
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞