無標題文檔

YUI 读码日记之 YAHOO.lang.is*

在 YUI 框架的 %BULID%/yahoo/yahoo.js 中,包含了一系列的变量类型检测方法,它们被分装成 YAHOO.lang.is* 。其中,这些函数的大部分封装都是 typeof 操作符的封装,我个人比较敢兴趣的事 isArray 与 isValue 的两个函数。

YAHOO.lang = YAHOO.lang || {
    isArray: function(o) { 
        if (o) {
           var l = YAHOO.lang;
           // 如果该对象有 length 这个属性,同时支持 splice 方法,
           // 那么就认为它为数组。
           return l.isNumber(o.length) && l.isFunction(o.splice);
        }
        return false;
    },

    isBoolean: function(o) {
        return typeof o === 'boolean';
    },

    isFunction: function(o) {
        return typeof o === 'function';
    },
        
    isNull: function(o) {
        return o === null;
    },
        
    isNumber: function(o) {
        return typeof o === 'number' && isFinite(o);
    },
      
    isObject: function(o) {
        return (o && (typeof o === 'object' || 
                            YAHOO.lang.isFunction(o))) || false;
    },
        
    isString: function(o) {
        return typeof o === 'string';
    },
        
    isUndefined: function(o) {
        return typeof o === 'undefined';
    },
    
    //...

    isValue: function(o) {
        // Infinity fails
        // return (o || o === false || o === 0 || o === '');
        var l = YAHOO.lang;
        return (l.isObject(o) || l.isString(o) || 
                           l.isNumber(o) || l.isBoolean(o));
    }
};

…… 复制粘贴分割线 ……

据悉 ,在 YUI 2.2.0 版本以前,YAHOO.lang.isArray 是这样写的。

isArray: function(obj) { 
    // safari 有 bug,只好处理字符串
    if (obj && obj.constructor && 
                 obj.constructor.toString().indexOf('Array') > -1) {  
        return true;  
    } else {
        return YAHOO.lang.isObject(obj) && obj.constructor == Array;  
    }  
},

而这样的判断数组类型是有缺陷的,比如下面的代码

function myArray() {
   this.name = 'name';  
}  
var o2 = new myArray();  
alert(YAHOO.util.isArray(o2));  // 弹出true  
// 因为 obj.constructor.toString() 中包含 myArray 字样,所以返回true  
  
function Obj() {  
    this.name = 'name';  
}  
var o = new Obj();  
o.constructor = Array;
alert(YAHOO.util.isArray(o));  // 弹出 true  
// 因为在 JavaScript 里,constructor 也是属性
// 可以动态指定,所以返回 true

因此,在 YUI 的后续版本,YAHOO.lang.isArray 被修改成了目前的这个样子

isArray: function(o) { 
    if (o) {
       var l = YAHOO.lang;
       // 如果该对象有 length 这个属性,同时支持 splice 方法,
       // 那么就认为它为数组。
       return l.isNumber(o.length) && l.isFunction(o.splice);
    }
    return false;
},

新的实现用了另外的思路:如果该对象有 length 这个属性,同时支持 splice 方法,那么就认为它为数组。当然,它依然有漏洞,我们仍然可以创建一个对象,使其拥有 length 属性和 splice 方法。但我觉得现在的实现更为合理,因为一来可能性不大,二来避免了诡异的浏览器的 BUG 。

再看 YUI 2.3.0 后引入的 YAHOO.lang.isValue,其实就是判断参数是否是一个有意义的值,只要参数不是 null/undefined/NaN,那么都返回 true。(注意这和一般的判断真假的不同就是, 0/false/''(空字符串) 这些都算是有效的值),所以 YAHOO.lang.isValue 非常适合用来判断表单域的值是否为有效值。

人格分裂的前兆

由于开发的需要,不得不在各浏览器下测试页面的表现情况。于是安装了 云烈 提供的某个软件(据说可以多个 IE 共存)以后,桌面就变成了这个样子。

https://friable.rocks/_/2008_03_03/1204540590.png

改编下 以前说的一些话 ,如有雷同纯属正常。

「你甭管什么浏览器,Internet Explorer、Firefox、Opera 还是 Safari,都给装上去。
这个还不够,光 Internet Explorer 你就要装六个版本的。你还别嫌少,装少了你都不
好意思和人家打招呼。这叫什么?专业!任务栏里看见一排各版本的 Internet Explorer
倍儿有面子!

我们搞开发的起码怎么的也得通过 Internet Explorer 测试吧?那是最基本的,你还别嫌
烦。什么Firefox 啊、Opera 啊、Safari 啊,你都要测试一遍,既然能同时通过
Internet Explorer 两个版本(6.0 与 7.0)的测试,就不在乎再多几个浏览器。

这叫什么,叫 Web 标准!」

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,会让你读出更深刻的知识。

我的照片

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

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

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

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

分类

搜索

文章