编写可维护的javascript(四):变量、函数和运算符

来源:互联网 发布:制造业企业大数据分析 编辑:程序博客网 时间:2024/05/17 04:39

var变量声明
Javascript允许多次使用var语句,但无论var是否真正会被执行,所有var语句都提前到包含这段逻辑的函数的顶部执行,即变量声明提前。比如:

    function doSomething() {        var result = 10 + value;        var value = 10;        return result;    }    doSomething();  //NaN

了解其中的 原理,需要清除这段代码被JavaScript理解为如下模样:

    function doSomething() {        var result;        var value;        result = 10 + value;        value = 10;        return result;    }

var语句提前至函数的顶部,在value=10代码之前,value的值是一个特殊值undefined,因此result的值是NaN(非数字)。

在某些场景下,开发者往往漏掉变量的声明提前,for语句就是一个常见的场景。

    function doSomethingWithItems(items) {       for (var i=0, len=items.length; i < len; i++) {           doSomething(items[i]);       }    }

在没有声明块级变量之前,这段代码等价于下面这段代码:

   function doSomethingWithItems(items) {        var i, len;        for (i=0, len=items.length; i < len; i++) {            doSomething(items[i]);        }    }

变量声明提前意味着:在函数内部任意地方定义变量和在函数顶部定义变量是完全一样的。
因此,一种流行的风格就是将所有变量声明放在函数顶部而不是散落在各个角落。写法如下:

    function doSomethingWithItems(items) {        var i, len;        var value = 10;        var result = value + 10;        for (i=0, len=items.length; i < len; i++) {            doSomething(items[i]);        }    }

当然进一步推荐在函数顶部使用单var语句,
每个变量的初始化单独占一行(让你得代码更短,下载最快)。赋值运算符比较对齐,对于那些没有初始化的变量来说,它们应当出现在var尾部。如下:

    function doSomethingWithItems(items) {        var value = 10,            result = value + 10,            i,            len;        for (i=0, len=items.length; i < len; i++) {            doSomething(items[i]);        }    }

函数声明
和var变量声明一样,函数声明也会被Javascript引擎提前。
因此,总是先声明函数,后使用函数。如下:

    function doSomething() {        alert("Hello world!");    }    doSomething();

*注意
函数的声明不应当出现在语句块内,代码可能不会按照我们的意图来执行,如下:

    if (condition) {        function doSomething() {            alert("Hi");        }    } else {        function doSomething() {            alert("Yo!");        }    }

这种场景是ECMAScript的一个灰色地带,应当尽可能避免。
立即调用函数
Javascript允许声明匿名函数(本身没有命名的函数),并将匿名函数赋值给变量或者属性。
为了让立即执行函数能够被一眼认出,可以将函数用一对圆括号包裹起来(编程规范)。比如:

    var value = (function() {        return {            message: 'Hi';        }    }());

严格模式
ES5引入“严格模式”,希望通过这种方式来解析执行Javascript,以减少错误。通过使用如下指令:

"use strict";

尽管这看起来是一个字符串,但ES5引擎会将其识别为一条指令,以严格模式来解析代码。
这条编译指令不仅用于全局,也可用于局部。但是极力不推荐在全局作用域中启用严格模式。如下:

    // bad    "use strict";    function doSomething() {        // 代码    }    // good    function doSomething() {        "use strict";        // 代码    }

如果你想要多个函数应用在严格模式下,而不必写很多行“use strict”的话,可以使用立即执行函数:

    (function() {        "use strict";        function doSomething() {            // 代码        }        function doSomethingElse() {            // 代码        }    })();

相等
由于Javascript具有强制类型转换机制,Javascript中的判断操作是很微妙的。
最常见的场景使用运算符==和!=的时候,当两个值得类型不同的时候,这两个运算符都会有强制类型转换(当数据类型相同时则不会有强制类型转换)如下:

    // 比较数字5和字符串"5"    console.log(5 == "5"); // true

如果一个布尔值和数字比较,布尔值会先转换为数字,再比较。false值为0、true值为1:

    console.log(1 == true); // true    console.log(1 == false); // false    console.log(2 == true); // false

如果其中一个是对象而另一个不是,则会首先调用对象的 valueOf() 方法,得到原始类型之再进行比较。
如果没有定义valueOf() 方法,则调用 toString() 方法,之后的比较操作和前面一样,如下:

    var object = {        toString: function() {            return "0x19";        }    };    console.log(object == 25); // true

因为toString() 方法返回了十六进制的字符串“0x19”,这个值先转换为数字,然后和25进行比较。
*注
由于强制类型的缘故,所以推荐不要使用==和!=,而应当使用===和!==。
因为用这些操作符比较不会涉及到强制类型转换。如下:

    console.log(5 === "5"); // false    console.log(1 === true); // false    console.log(0 === false); // false    console.log(2 === true); // false    var object = {        tring: function() {            return "0x19";        }    };    console.log(object === 25); // false

eval()
在Javascript中,eval() 的参数是一个字符串,eval() 会将字符串当作代码来执行。
开发者可以通过这个函数来载入外部的Javascript代码,或者随即生成Javascript代码并执行。如:

    eval("alert('Hi!')");    var count = 10;    var number = eval("5 + count");    console.log(number); // 15

在Javascript中eval() 并不是唯一可以执行Javascript字符串的函数,使用Function构造函数也可以做到这一点,setTimeout() 和 setInterval() 也可以,但是编码规范明确禁止给Function构造函数、setTimeout()、setInterval() 传入字符串参数。

原始包装类型
Javascript里有三种原始包装类型:String、Boolean和Number。
每种类型都代表全局作用域中的一个构造函数,并分别表示各自对应的原始值的对象。
原始包装类型的主要作用是让原始值具有对象般的行为,如下:

    var name = "Mangoyi";    console.log(name.toUpperCase()); // MANGOYI

之所以,字符串可以调用toUpperCase() 方法,是因为在这条语句的表象背后Javascript引擎创建了String类型的新实例,紧跟着就被销毁了,当再次需要时又创建另外一个对象。
通过给一个字符串增加属性来检验这种行为:

    var name = "Mangoyi";    name.author = true;    console.log(name.author); // undefined

上面的代码中,在第2行结束以后,author属性就不见了。因为表示这个字符串的临时String对象在第2行代码执行结束就销毁了。然后第3行又创建了一个新的String对象。
同样你可以手动创建这些对象,如下:

    // bad    var name = new String('Mangoyi');    var author = new Boolean(true);    var count = new Number(10);

尽管可以使用这些包装类型,但强烈推荐大家避免这样使用它们。

文章内容从《编写可维护的Javascript》[美] Nicholas C. Zakas著 一书中总结写出。

阅读全文
0 0