zoukankan      html  css  js  c++  java
  • seajs 源码分析一

    说明下,写这个不是说明接下去不分析jquery了,只是这两天在准备面试,顺便把seajs给读了,调节下心情。把seajs的源码分析速度写完以后,就会继续jquery。。。

    ===================================================================================

    首先是seajs的大架构:

    1 /**
    2  * Sea.js 2.2.1 | seajs.org/LICENSE.md
    3  */
    4 (function(global, undefined) {
    5            //some code
    6 })(this)

    采用了闭包的结构,这样就不会干扰全局变量。在页面中的正常引用时,因未指定this具体指向,所以会自动设为window对象。这样就把global=window,undefined传入闭包,方便闭包里的特定对象属性暴露到全局。

    接下去这段就是说明把seajs变量暴露到全局,并附带版本号:

    //如果全局中已经有seajs就直接返回
    if (global.seajs) {
      return
    }
    
    var seajs = global.seajs = {
      // The current version of Sea.js being used
      version: "2.2.1"
    }
    
    var data = seajs.data = {}

    接下去的内容主要是用于判断类型的:

    function isType(type) {
      return function(obj) {
        return {}.toString.call(obj) == "[object " + type + "]"
      }
    }
    
    var isObject = isType("Object")
    var isString = isType("String")
    var isArray = Array.isArray || isType("Array")
    var isFunction = isType("Function")
    
    var _cid = 0
    function cid() {
      return _cid++
    }

    可以看到,seajs的类型判断基本是用isType的,它会根据传入值返回一个函数,这个函数能够判断接下来需要判断的类型和传入的是否相同。其内部调用的是Object.prototype.toString原生函数。

    cid用于返回特定唯一值。

    接下来是seajs里的事件机制,事件机制主要是方便插件的开发,在seajs运行到特定步骤时,都会激发一些列事件,插件通过将函数插入这些事件中,做到在特定点运行特定动作,这个原理在《javascript高级程序设计》里有提到过,源码如下:

    var events = data.events = {}
    
    // 绑定事件,将事件放入特定的事件名称下
    seajs.on = function(name, callback) {
      var list = events[name] || (events[name] = [])
      list.push(callback)
      return seajs
    }
    
    // 根据name,callback来移除事件
    // 如果name和callback都未定义的话,移除所有事件
    seajs.off = function(name, callback) {
      // 移除所有事件
      if (!(name || callback)) {
        events = data.events = {}
        return seajs
      }
    
      var list = events[name]
      if (list) {
        if (callback) {
          // 移除特定事件名称下的特定函数
          for (var i = list.length - 1; i >= 0; i--) {
            if (list[i] === callback) {
              list.splice(i, 1)
            }
          }
        }
        else {
          // 未指定callback的话,就直接移除所有该事件名称下的事件
          delete events[name]
        }
      }
    
      return seajs
    }
    
    // 运行事件,将所有该事件名称下的事件全部跑一遍
    var emit = seajs.emit = function(name, data) {
      var list = events[name], fn
    
      if (list) {
        // 复制事件,防止对原先事件的修改
        list = list.slice()
    
        // 运行每一项事件
        while ((fn = list.shift())) {
          fn(data)
        }
      }
    
      return seajs
    }

     接下来是seajs中关于路径处理的,将路径转化,加上alias,map之类的,源码如下:

    //文件夹目录匹配
    var DIRNAME_RE = /[^?#]*//
    // 匹配/./
    var DOT_RE = //.//g
    // 匹配/目录名/../
    var DOUBLE_DOT_RE = //[^/]+/..//
    // 匹配双斜杠
    var DOUBLE_SLASH_RE = /([^:/])///g
    
    // 得到文件夹目录
    // dirname("a/b/c.js?t=123#xx/zz") ==> "a/b/"
    function dirname(path) {
      return path.match(DIRNAME_RE)[0]
    }
    
    // 路径转化
    // realpath("http://test.com/a//./b/../c") ==> "http://test.com/a/c"
    function realpath(path) {
      // /a/b/./c/./d ==> /a/b/c/d
      path = path.replace(DOT_RE, "/")
    
      // a/b/c/../../d  ==>  a/b/../d  ==>  a/d
      while (path.match(DOUBLE_DOT_RE)) {
        path = path.replace(DOUBLE_DOT_RE, "/")
      }
    
      // a//b/c  ==>  a/b/c
      path = path.replace(DOUBLE_SLASH_RE, "$1/")
    
      return path
    }
    
    // 转化id成路径
    // normalize("path/to/a") ==> "path/to/a.js"
    // substring比slice和RegExp快
    function normalize(path) {
      var last = path.length - 1
      var lastC = path.charAt(last)
    
      // 若uri以#结尾,则直接返回去掉#后的结果
      if (lastC === "#") {
        return path.substring(0, last)
      }
      // 在最后加.js
      return (path.substring(last - 2) === ".js" ||
          path.indexOf("?") > 0 ||
          path.substring(last - 3) === ".css" ||
          lastC === "/") ? path : path + ".js"
    }
    
    // paths vars匹配
    var PATHS_RE = /^([^/:]+)(/.+)$/
    var VARS_RE = /{([^{]+)}/g
    
    // 取得相应id下的alias
    function parseAlias(id) {
      var alias = data.alias
      return alias && isString(alias[id]) ? alias[id] : id
    }
    
    // 根据设置的data.paths转化id
    function parsePaths(id) {
      var paths = data.paths
      var m
    
      if (paths && (m = id.match(PATHS_RE)) && isString(paths[m[1]])) {
        id = paths[m[1]] + m[2]
      }
    
      return id
    }
    
    // 根据设置的vars 转化id
    function parseVars(id) {
      var vars = data.vars
    
      if (vars && id.indexOf("{") > -1) {
        id = id.replace(VARS_RE, function(m, key) {
          return isString(vars[key]) ? vars[key] : m
        })
      }
    
      return id
    }
    
    // 根据在map中设置的规则转化uri
    function parseMap(uri) {
      var map = data.map
      var ret = uri
    
      if (map) {
        for (var i = 0, len = map.length; i < len; i++) {
          var rule = map[i]
    
          ret = isFunction(rule) ?
              (rule(uri) || uri) :
              uri.replace(rule[0], rule[1])
    
          // 只运行第一条匹配的规则
          if (ret !== uri) break
        }
      }
    
      return ret
    }
    
    // 绝对路径 根目录匹配
    var ABSOLUTE_RE = /^//.|://
    var ROOT_DIR_RE = /^.*?//.*?//
    
    function addBase(id, refUri) {
      var ret
      var first = id.charAt(0)
    
      // 绝对路径
      if (ABSOLUTE_RE.test(id)) {
        ret = id
      }
      // 关于相对路径处理,判断是否有传入refUri,否则用data.cwd
      else if (first === ".") {
        ret = realpath((refUri ? dirname(refUri) : data.cwd) + id)
      }
      // 根路径
      else if (first === "/") {
        var m = data.cwd.match(ROOT_DIR_RE)
        ret = m ? m[0] + id.substring(1) : id
      }
      // 其它
      else {
        ret = data.base + id
      }
    
      // 若以//开头,则添加默认的protocol
      if (ret.indexOf("//") === 0) {
        ret = location.protocol + ret
      }
    
      return ret
    }
    
    // id到uri的转化函数
    function id2Uri(id, refUri) {
      if (!id) return ""
    
      id = parseAlias(id)
      id = parsePaths(id)
      id = parseVars(id)
      id = normalize(id)
    
      var uri = addBase(id, refUri)
      uri = parseMap(uri)
    
      return uri
    }
  • 相关阅读:
    自执行函数的几种不同写法的比较
    Textarea与懒惰渲染
    备忘:递归callee.caller导致死循环
    围观STK
    某台机器上IE8抛“Invalid procedure call or argument”异常
    QWrap Selector之W3C版
    onclick与listeners的执行先后问题
    随机问题之洗牌算法
    selector4 之 巧妙的主体定义符
    神奇的"javascript:"
  • 原文地址:https://www.cnblogs.com/cyITtech/p/3603120.html
Copyright © 2011-2022 走看看