無標題文檔

YUI 读码日记之 YAHOO.namespace

YUI 提供了类似于 Java 的 命名空间 机制。%BULID%/yahoo/yahoo.js 文件中,YAHOO 基类定义好了以后,第一个定义的方法就是 namespace(在 YUI 2.5 中,第 75 行)。下面是 YUI 手册上的介绍

namespace

static Object namespace ( arguments )
Returns the namespace specified and creates it 
if it doesn't exist

YAHOO.namespace("property.package");
YAHOO.namespace("YAHOO.property.package");

Either of the above would create YAHOO.property, then 
YAHOO.property.package Be careful when naming packages. 
Reserved words may work in some browsers and not others. 
For instance, the following will fail in Safari:

YAHOO.namespace("really.long.nested.namespace");

This fails because "long" is a future reserved word in 
ECMAScript

Parameters:
    arguments <String*> 1-n namespaces to create 

Returns: Object
    A reference to the last namespace object created

下面看下相应的代码。

YAHOO.namespace = function() {
    var a=arguments, o=null, i, j, d;

    // 根据参数的长度循环获取属性或方法名   
    for (i=0; i<a.length; i=i+1) {
        d=a[i].split(".");
        o=YAHOO;

        // 避免定义 YAHOO.YAHOO
        for (j=(d[0] == "YAHOO") ? 1 : 0; j<d.length; j=j+1) {
            // 加入相应的方法或属性
            o[d[j]]=o[d[j]] || {};
            o=o[d[j]];
        }
    }
    
    // 返回 YAHOO 本身
    return o;
};

根据文档,YUI 默认会添加 \"util\"、\"widget\"、\"example\" 三个命名空间,相应的代码如下。

(function() {
    // 添加默认的命名空间
    YAHOO.namespace("util", "widget", "example");

    if ("undefined" !== typeof YAHOO_config) {
        var l=YAHOO_config.listener,ls=YAHOO.env.listeners,unique=true,i;
        if (l) {
            // if YAHOO is loaded multiple times we need to check to see if
            // this is a new config object.  If it is, add the new component
            // load listener to the stack
            for (i=0;i<ls.length;i=i+1) {
                if (ls[i]==l) {
                    unique=false;
                    break;
                }
            }
            if (unique) {
                ls.push(l);
            }
        }
    }
})();

从 namespace 的函数写法可以得知,在 定义 Javascript 类的方法或者属性 也可以使用类似于数组的声明方式。比如

var o = {};
o.method = function () {
    alert("hello, world!");
}
o.method();

就可以写成

var o = {};
o["method"] = function () {
    alert("hello, world!");
}
o.method();

但请小心使用此种特性,毕竟数组操作与类的声明显性的区分,有利于以后的代码维护。

更新,感谢 小马 的指证:

「但请小心使用此种特性,毕竟数组操作与类的声明显性的区分」

    * {} 不是类,是对象
    * Javascript 里的对象就是关联数组
    * 什么时候使用 o.method() ,什么时候使用 o['method']() ,依据实际应用环境

建议结合 《JavaScript 权威指南》读 YUI,会让你读出更深刻的知识。

YUI 读码日记之 YAHOO.env.ua

正如 YAHOO.env.ua 字面所指(ua 就是 user-agent 的意思),这篇文章主要分析 YUI 如何检测浏览器的类型与版本。有关 YAHOO.en.ua 的文档 在这里说明

目前为止,检测浏览器与版本主要有两大主流的做法。其一,就是分析浏览器提供的 user-agent 字符串;其二,就是根据其功能判别。YUI 使用第一种判断方式,也就是分析 user-agent 字符串。

下面是 YUI 相应的代码实现,经过本人精简以后并加了点注释,这段程序在 %BUILD%/yahoo/yahoo.js 中。

YAHOO.env.ua = function() {
    var o={ie:0, opera:0, gecko:0, webkit:0, mobile: null};
    var ua=navigator.userAgent, m;

    // 是否为基于 KHTML 引擎的浏览器,比如 Konqueror 。
    if ((/KHTML/).test(ua)) {
        o.webkit=1;
    }

    // 检测 Apple 系列的浏览器,包括移动版本
    m=ua.match(/AppleWebKit\/([^\s]*)/);
    if (m&&m[1]) {
        o.webkit=parseFloat(m[1]);

        // YUI 是我见过的唯一一个检测移动浏览器的框架 :^)
        if (/ Mobile\//.test(ua)) {
            o.mobile = "Apple"; // iPhone or iPod Touch
        } else {
            m=ua.match(/NokiaN[^\/]*/);
            if (m) {
                o.mobile = m[0]; // Nokia N-series, ex: NokiaN95
            }
        }
    }

    if (!o.webkit) { // 检测其他浏览器
        // 检测 Opera
        m=ua.match(/Opera[\s\/]([^\s]*)/);
        if (m&&m[1]) {
            o.opera=parseFloat(m[1]);
            // 是否为 Opera 移动版本
            m=ua.match(/Opera Mini[^;]*/);
            if (m) {
                o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316
            }
        } else {
            // 检测 IE 和 Mozilla
            m=ua.match(/MSIE\s([^;]*)/);
            if (m&&m[1]) {
                o.ie=parseFloat(m[1]);
            } else {
                m=ua.match(/Gecko\/([^\s]*)/);
                if (m) {
                    o.gecko=1;
                    m=ua.match(/rv:([^\s\)]*)/);
                    if (m&&m[1]) {
                        o.gecko=parseFloat(m[1]);
                    }
                }
            }
        }
    }
    
    return o;
}();

根据代码的流程,我们可以很容易理解 YUI 判断浏览器的逻辑。而我在之前也分析过 jQuery 在这方面是如何做的 ,可以对比参照下。

从代码量上考虑,jQuery 的代码是非常的精简的,而 YUI 「看起来」更像是我们平常一般的写法:保守、但是有效。而我个人更倾向于 jQuery 的写法,当然 YUI 这这样的判断流程自然也是有它的道理的。

请允许我多事一下,下面是本人根据 jQuery 的代码「重写」 YUI 的 YAHOO.env.ua ,让大家见笑了。

YAHOO.env.ua = function() {
    var ua = navigator.userAgent.toLowerCase();
    var version = (ua.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1];
    return {
        webkit: /webkit/.test(ua) ? version : 0,
        opera: /opera/.test(ua) ? version : 0,
        ie: /msie/.test(ua) && !/opera/.test(ua) ? version : 0,
        gecko: /mozilla/.test(ua)&&!/(compatible|webkit)/.test(ua) ? version : 0,
        mobile: / mobile\//.test(ua) || ua.match(/nokian[^\/]*/) || 
                                       ua.match(/opera mini[^;]*/) ? version : 0
    };
}();

YUI 读码日记之 YAHOO.util.later

由于项目的需要,不得不重新认识一个 Javascript 代码框架, YUI 。在本人印象中,与 jQuery 的「 作用链 」处理方式不同,YUI 更像是 Java 的处理方式 - 由大堆的类封装,并提供调用。

接下来的日子,我会抽空将 YUI 的代码阅读一遍,并作一些摘录。这些笔记就形成了一系列没有「创意的文章」,我称之为「YUI 读码日记」。

首先,开始的是 YAHOO 这个基类。YUI 框架的功能都是以 YAHOO 为基类封装而成,因此 %BUILD%/yahoo/yahoo.js 是理解此框架代码的重头戏。

在阅读代码的过程中,本人对于 YAHOO.lang.later 机制比较感兴趣,它提供的功能是延时运行某个函数(或者是类的方法)。下面是它的介绍(具体的 YAHOO.lang 文档可以从 这里获得 )。

later

void later ( when , o , fn , data , periodic )

Executes the supplied function in the context of the supplied 
object 'when' milliseconds later. Executes the function a single
 time unless periodic is set to true.

Parameters:

    when <int> the number of milliseconds to wait until the fn
 is executed 

    o <object> the context object 

    fn <Function|String> the function to execute or the name 
of the method in the 'o' object to execute 

    data <object> [Array] data that is provided to the function.
 This accepts either a single item or an array. If an array is 
provided, the function is executed with one parameter for each 
array item. If you need to pass a single array parameter, it 
needs to be wrapped in an array [myarray]

    periodic <boolean> if true, executes continuously at supplied 
interval until canceled 

Returns: void
    a timer object. Call the cancel() method on this object to 
stop the timer.

程序员似乎更喜欢看代码去理解上述的功能,代码如下(注, later 是 YAHOO.lang 的一个方法)。

later: function(when, o, fn, data, periodic) {
    // 初始化变量
    when = when || 0; 
    o = o || {};
    var m=fn, d=data, f, r;

    // 如果仅仅提供了 fn 的函数名称,判断 fn 是否
    // 为 o 的一个方法。
    if (YAHOO.lang.isString(fn)) {
        m = o[fn];
    }

    if (!m) {
        throw new TypeError("method undefined");
    }
    
    // 将 data 转换成数据,并供 apply 调用
    if (!YAHOO.lang.isArray(d)) {
        d = [data];
    }

    f = function() {
        m.apply(o, d);
    };

    // 判断是否重复运行
    r = (periodic) ? setInterval(f, when) : setTimeout(f, when);

    // 返回 timer 类,可以使用 cancel 方法取消该定时器
    return {
        interval: periodic,
        cancel: function() {
            if (this.interval) {
                clearInterval(r);
            } else {
                clearTimeout(r);
            }
        }
    };

可以看得出,YAHOO.lang.later 写得非常的明了,并且很容易理解。最后,有关 setTimeout 与闭包的相关应用,可以 参看这里

我的照片

嗨!我叫「明城」,八零后、码农、宁波佬,现居杭州。除了这里,同时也欢迎您关注我的 GitHubTwitterInstagram 等。

这个 Blog 原先的名字叫 Gracecode.com 、现在叫 「無標題文檔」 。 要知道作为码农取名是件很难的事情,所以不想在取名这事情上太费心思。

作为八零后,自认为还仅存点点可能不怎么被理解的幽默感,以及对平淡生活的追求和向往。 为了避免不必要的麻烦,声明本站所输出的内容以及观点仅代表个人,不代表自己所服务公司或组织的任何立场。

如果您想联系我,可以发我邮件 `echo bWluZ2NoZW5nQG91dGxvb2suY29tCg== | base64 -d`

分类

搜索

文章