javascript变量类型

javascript的变量类型真的很要人纠结,即使你是很有经验的js工程师,你也很难说清楚js的类型和分类。
最近在讲javascript入门指南的时候,有人提出数组为什么不是基本类型,我通过演示typeof []的结果来说明,数组是对象类型派生出来的,而不是六种基本类型。
其实要理解javascript的变量类型很简单,就是我们要找到一个划分的标准。

javascript变量类型分类

在很多书中都提到了javascript的变量类型,每本书都有不同的划分标准,如果按照typeof和instanceof的返回值来区分,可以把javascript的变量类型分为两套类型系统:基本类型和对象类型衍生出来的对象类型系统。
基本类型包括:undefined,number,boolean,string,object,function,他们之前通过typeof的返回值来区分。
第二套对象类型系统是由第一套系统衍生发展而来的,例如前面提到的Array,还有Null,Number,Boolean等等,对象类型可以通过instanceof来判断。
那么对象类型中的Number和基本类型中的number又是什么关系呢?他们又是什么区别呢?
答案就是,他们是映射关系,即下例:

var a = new Number(123);
console.log(a.valueOf()===123);//true
var b = new String(123);
console.log(b.valueOf()===123);//false
console.log(b.valueOf()==='123');//true

obj.valueOf()返回的是该对象的原始值。

值类型和引用类型

IE的fireEvent方法

在IE中提供了一个fireEvent方法,顾名思义就是触发某个事件发生的意思。刚开始我以为是会跟平时使用onclick()一样,没想到最近在写javascript入门ppt的时候发现了,原来自己太自以为是了!看来还有很多javascript的细节没有掌握好啊!

现在根据自己的总结详细的记录下fireEvent方法的使用。fireEvent是IE提供的一种方法,msdn文档地址:http://msdn.microsoft.com/en-us/library/ms536423(v=vs.85).aspx

onclick()

我们先看第一段实例代码:


*   i am one;
*   i am two;
*   i am three;

这段代码中我们没有个id1的li添加onclick事件,点击button,会报错,提示“对象不支持此属性或方法”。由此可见,DOM.onclick()需要添加onclick事件之后才能使用。 假如我们把以上的代码修改为:

*   i am one;
*   i am two;
*   i am three;

javascript匿名函数

今天在整理javascript入门培训的PPT时,提到了匿名函数,所以拿来分享下心得。

匿名函数的写法

顾名思义,就是没有名字的函数(⊙﹏⊙b汗)。匿名函数通常用于javascript作用域的控制,可以有效的避免对全局变量的污染。常见的匿名函数有下面四种写法,欢迎补充:

匿名函数写法一


(function(){
    //do something
})();

匿名函数写法二


!function(){
    //do something
}();

上面!还可以写成+,!!等多种方式。
上面两种方法是我常用的,下面两种方法是google上找的,我没用过(好吧,我out啦)。

匿名函数写法三


(function(){
    //do something
}());

匿名函数写法四


void function(){
    //do something
}();

刚才微博上@朴灵 提出来:写法三比较安全,正如网友@Rain的留言,匿名函数上面的写法都存在前后文;问题,所以需要注意的是匿名函数在压缩工具打包压缩后会出现上下文错误合并()的问题,例如第一种写法,如果下面的代码,未压缩之前是正常的,压缩后就不正常了,所以要严格上下文的;问题,而第三种就不会出现类似问题:


var a = function(){}
(function(){
    alert(1);
})();

上例中就出现了错误,这就是因为a函数会把他后面的匿名函数作为参数传入a中,这也就解释了为什么有人习惯在匿名函数之前添加;了,就是为了防止上文没有严格遵循javascript语法,漏掉;的问题。

IE6中a标签location.href失效解决方法

今天遇见IE6一个location.href的bug,具体情况是这样的,IE6下,在a标签中,将href写成javascript:; javascript:void(0);,并且给这个标签绑定onclick事件,点击后,执行location.href实现页面跳转,例如下面的代码:

<a href="javascitp:;">点击跳转</a>

或者

<a href="javascitp:void(0);">点击跳转</a>

toURL函数的代码如下所示:

function toURL(){
    location.href = "http://js8.in";
}

这样在非IE6浏览器下都可以使用,但是在IE6下就是跳转不了,而且不报错,在location.href之后的代码,例如(alert(1);)也是执行的。

IE6下location.href失效解决的方法

解决IE6中a标签中location.href失效的方式就是a标签中的href不用使用javascript:;javascript:void(0);。具体原因还不清楚,不过我们可以使用href=”#”来代替。

javascript事件:获取事件对象getEvent函数

javascript开发中我们会经常获取页面中的事件对象,然后来处理这些事件,例如下面的getEvent函数就是获取javascript下的页面事件对象。


function getEvent(event){
    return event || window.event;
}

我们使用getEvent的时候可以这样调用。


function foo(event){
    var evt = getEvent(event);
    alert(evt);
}

并且把foo函数绑定到一个onclick事件上。
但是常用的是foo函数不会有参数,或者第一个参数没有传入,我们可以通过arguments[0]来获取第一个参数。
在IE中event是一个全局变量,即window.event,而在Firefox等浏览器,事件会作为第一个参数传入foo函数,所以getEvent可以改为:


function getEvent(event){
    return arguments[0] || window.event;
}

这时在一些版本的浏览器,例如Firefox 中会出现获取不到event对象的问题,其实我们可以看成事件的触发是下面的方式:


function onclick(event) {
    foo();
}

这样,第一个函数是onclick,即event会默认作为第一个参数传入,而foo函数没有将event作为参数传入,这样严重限制了getEvent的灵活性,所以我们需要修改下getEvent函数。

javascript继承的写法

严格来说javascript是基于对象而不是面向对象的语言,因为javascript没有像java语言那么丰富的类、继承、封装,但是javascript是基于原型(prototype)的面向对象开发,是一种动态、弱类型、基于原型的语言。前几天看了阿里UED的《重温javascript继承机制》,今天重新看了,很有感触,特地转载了重要的部分,记录一下,下次有需要的时候还要重新看一下!的确是不错的一篇文章。

继承的演变

1、采用new关键字生成实例

处理表单验证这样简单功能脚本语言显然是不需要”继承”机制的,然而如果Javascript里面都是对象,就需要有一种办法来把所有对象联系起来。最后,Brendan Eich还是设计了”继承”。只是,他并没有引入”类”(class)的概念,因为一旦有了”类”,Javascript就是一种完整的面向对象编程语言了,
这好像有点太正式了,与设计初衷也远了,同时增加了初学者的入门难度。
参照到C++和Java语言都使用new命令来生成实例:

C++这样写:


ClassName *object = new ClassName(param);

Java这样写:


Foo foo = new Foo();

那么,也可以把new命令引入了Javascript,用来从原型对象生成一个实例对象。但是,Javascript中没有”类”的话,怎样表示原型对象呢?
依然是参照C++和Java使用new命令时,都会调用”类”的构造函数(constructor)。Brendan Eich简化了设计,在Javascript语言中,new命令后面跟的是构造函数,不再是类。
我们举个例子来说,现在有一个叫做WD构造函数,表示前端开发(web-developper)对象的原型。


function WD(skill){
    this.skill = skill;
}

对这个构造函数使用new关键字,就会生成一个前端开发对象的实例。


var WD1 = new WD('html');
console.log(WD1.skill); // html

在构造函数中的this关键字,它其实代表的是新创建的实例对象。

2、new 出来对象的缺陷

采用new关键字,用构造函数生成实例对象无法共享属性和方法。
比如,在WD对象的构造函数中,设置一个实例对象的共有属性skill。


function WD(skill){
    this.skill = skill;
    this.sex = '男';
}

然后,生成两个实例对象:


var WD1 = new WD('html');
var WD2 = new WD('css');

这两个对象的skill属性是独立的,修改其中一个,不会影响到另一个。


WD1.skill= 'Javascript';
console.log(WD2.skill);//“css”,不受WD1的影响

每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。

3、引入prototype属性

为了实现属性和方法的共享,Brendan Eich决定为构造函数设置一个prototype属性。
这个属性包含一个对象(以下简称”prototype对象”),所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。
实例对象一旦创建,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。
还是以WD构造函数为例,现在用prototype属性进行改写:


function WD(skill){
    this.skill = skill;
}

WD.prototype = { sex : '男' };

var WD1 = new WD('html');
var WD2 = new WD('css');

console.log(WD1.sex); // 男
console.log(WD2.sex); // 男

现在,sex属性放在prototype对象里,是两个实例对象共享的。只要修改了prototype对象,就会同时影响到两个实例对象。


WD.prototype.sex = '女';
console.log(WD1.sex); //女
console.log(WD2.sex); // 女

由于所有的实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像”继承”了prototype对象一样。这就是Javascript继承机制的设计思想。

三、构造函数如何实现继承

现在有一个”MED”对象的构造函数(MED:Marketing Experience Design,营销体验设计)


function MED(){
    this.aim = "营销体验设计";
}

依然是”WD”对象的构造函数,


function WD(skill,sex){
    this.skill = skill;
    this.sex = sex;
}

怎样才能使”WD”继承”MED”呢?

NodeList集合跟Array数组的区别

首先来看看什么是NodeList,NodeList跟arguments都不是普通的数组,他们有数组的一些基本属性但是又不完全是数组。下面是在Mozilla上面找到的定义:

This is a commonly used type which is a collection of nodes returned by getElementsByTagName, getElementsByTagNameNS, and Node.childNodes. The list is live, so changes to it internally or externally will cause the items they reference to be updated as well. Unlike NamedNodeMap, NodeList maintains a particular order (document order). The nodes in a NodeList are indexed starting with zero, similarly to JavaScript arrays, but a NodeList is not an array.
由字面意思来看NodeList是DOM操作(getElementsByTagName等)取出来的集合,是集合而不是普通的数组,但是他们有数组的一些属性,例如length、下标索引,但是他们也有自己的属性,例如item,另外NodeList最大的特点就是时效性(live)。

NodeList的时效性

我们来看下面的代码:



*   index0
*   index1
*   index2
*   index3
*   index4

javascript代码如下:


var myUl = document.getElementById('nodelist');
var lis = myUl.getElementsByTagName('li');

lis是一个NodeList集合,具有时效性,所谓的时效性就是我们在修改li的同时,会反映到lis上来,这与array是不同的,例如我们把第一个li插入到ul的底部,那么lis也会发生相应的变化:


//把第一个li插入的ul的底部
myUl.appendChild(myUl.getElementsByTagName('li').item(0));
console.log(lis[0]);//输出的是原来ul的第二个li

所以我们应该在写代码的时候注意NodeList的时效性,不然就会犯一些错误,

IE6下javascript不能隐藏select标签的bug

昨天做了一个简单的项目,要求有三级联动的select菜单,可是伤不起的IE6下就出现了不能隐藏select的bug,之前我也介绍过IE6下select层高的问题,可以通过隐藏select或者使用iframe遮挡的方式来解决,今天的bug不同以前,大家如果在用js控制select显示隐藏的时候可能会经常遇到,调试一下午终于找到了解决的方案,特此记录一下。

IE6不能隐藏select重现代码

建立两个关联的select,通过javascript控制第一个select 发生变化后,第二个select根据第一个value重新添加options,如果为空则隐藏第二个select(示例中判断除第一个之外都隐藏),则由第一个select的第三个value切换到第二个value则第二个select就不会隐藏。
具体效果见下面的实例:

IE6 select隐藏bug

IE6下不能隐藏select标签解决方案

  1. 可以通过先设置select为显示,后设置为none的方式
  2. 通过给select添加class来控制显示
    具体方法,见示例代码源码注释的地方。

使用javascript将XML解析为JSON

今天看了David Walsh一篇把XML格式转换为JSON格式的文章,感觉不错,简单的转载了过来。
下面是神奇的XML转JSON的javascript代码:


    // Changes XML to JSON
function xmlToJson(xml) {

    // Create the return object
    var obj = {};

    if (xml.nodeType == 1) { // element
        // do attributes
        if (xml.attributes.length > 0) {
        obj["@attributes"] = {};
            for (var j = 0; j < xml.attributes.length; j++) {
                var attribute = xml.attributes.item(j);
                obj["@attributes"][attribute.nodeName] = attribute.nodeValue;
            }
        }
    } else if (xml.nodeType == 3) { // text
        obj = xml.nodeValue;
    }

    // do children
    if (xml.hasChildNodes()) {
        for(var i = 0; i < xml.childNodes.length; i++) {
            var item = xml.childNodes.item(i);
            var nodeName = item.nodeName;
            if (typeof(obj[nodeName]) == "undefined") {
                obj[nodeName] = xmlToJson(item);
            } else {
                if (typeof(obj[nodeName].length) == "undefined") {
                    var old = obj[nodeName];
                    obj[nodeName] = [];
                    obj[nodeName].push(old);
                }
                obj[nodeName].push(xmlToJson(item));
            }
        }
    }
    return obj;
};

最短的DOMReady代码

自己的js框架YQ还没有添加DOMReady代码,今天看到国外js牛人Dustin Diaz的文章,得到了一份最短的DOMReady代码,分享出来:


function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}

Dustin Diaz放弃了使用IE的document.documentElement.doScroll('left')(这也是多数框架的方法,详细介绍)。使用了setTimeout来定时检测document.readyStatedocument.readyState有以下五种状态: