無標題文檔

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 与闭包的相关应用,可以 参看这里

燃烧脑细胞时间

烧脑细胞时间,请分别说明下述代码的输出。

代码一

var hello = new function() {
    return "hello, world!"
}; 
alert(hello);

代码二

function hello() {
    return "hello, world!";
}
var hello_sample = new hello();
alert(hello_sample);

代码三

var hello = new function() {
    return new String("hello, world!")
};
alert(hello);

代码四

var hello = new function() {
    return function() {
        return 'hello, world!';
    };
};
alert(hello);

代码五

var hello = function() {return "hello, world!"}();
alert(hello);

可以参考 怿飞 Blog 上的 一篇文章

我的照片

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

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

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

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

分类

搜索

文章