PHP群:95885625 Hbuilder+MUI群:81989597 站长QQ:634381967
    您现在的位置: 首页 > 开发编程 > 编程杂谈 > 正文

    JavaScript语法陷阱

    作者:admin来源:网络浏览:时间:2020-09-30 00:07:50我要评论
    导读:没有一种编程语言是完美的,JavaScript 也不例外,它的语法陷阱重重,防不胜防:加号with分号自动插入声明提升eval多行字符串变量泄漏argu
    没有一种编程语言是完美的,JavaScript 也不例外,它的语法陷阱重重,防不胜防:
    • 加号
    • “with”
    • 分号自动插入
    • 声明提升
    • “eval”
    • 多行字符串
    • 变量泄漏
    • “arguments.callee”

    了解和熟悉这些陷阱,并在开发时规避它们,可以给我们省去很多麻烦。

    加号

    作为二元运算符时,+ 既是数学运算的加法,也是字符串的拼接。另外,它还可以作为一元符号,表示正数。

    看看下面的代码:

    // 1
    console.log( 1 + 2 ); // 3
    console.log( "3" + "4" ); // "34"
    // 2
    console.log( 1 + "3" ); // "13"
    console.log( "3" + 1 ); // "31"
    // 3
    console.log( 1 + null );
    console.log( 1 + undefined );
    console.log( 1 + NaN );
    // 4
    console.log( "3" + null );
    console.log( "3" + undefined );
    console.log( "3" + NaN );
    // 5
    console.log( 1 + {} );
    console.log( 1 + [] );
    // 6
    console.log( "3" + {} );
    console.log( "3" + [] );
    也许你可以准确的说出第1组代码的结果,甚至第2组也能答上,但剩下的几组你能毫不犹豫地给出答案吗?

    在 JavaScript 中,是如何决定一段代码中的 + 是数学运算还是字符串拼接呢?答案请看下面这段逻辑:

    a + b:
        pa = ToPrimitive(a)
        pb = ToPrimitive(b)
        if (pa is string || pb is string)
            return concat(ToString(pa), ToString(pb))
        else
            return add(ToNumber(pa), ToNumber(pb))

    收集 + 两端的操作数的原始值。

    1. 如果其中之一是字符串,则进行字符串拼接。
    2. 否则,执行数学加法。

    需要注意的是,JavaScript 的原始值类型包括 number, string, boolean, undefined, 而 null 也是一种特殊的原始值。另一方面,对于非原始值类型(即复合类型,也即 object )的变量,其原始值被认为是字符串。

    按这个逻辑,之前的测试结果就容易理解了。当然,像上面那样使用加号是不被推荐的,为了避免混淆,利用上面的加号逻辑,我们通常可以这样使用加号:

    // 确保数字相加
    a = +b + (+c);
    // 确保变量 d 为字符串
    d = "" + d;

    “with”

    使用 with 语句,可以将一个语句块的上下文绑定为一个指定对象。

    with (document) {
        write("foo");
        getElemntById("bar").innerHTML = "foobar";
        alert("Hello world!");
    }
    
    // 等同于以下代码
    document.write("foo");
    document.getElemntById("bar").innerHTML = "foobar";
    window.alert("Hello world!");

    但是咱们不推荐使用 with ,事实上,ECMAScript 5 中引入的严格模式也禁止使用 with :

    1. JavaScript 解释器引擎将难以对代码执行优化。解释器引擎的执行优化是建立在“明确的知道这个变量在运行时所指向的引用”的基础上的。而在 with 语句块中的变量或函数,在解释阶段无法判断其是属于 with 的上下文,还是其所在作用域,只有等到代码运行时才能确定。
    2. 代码可阅读性差。

    分号自动插入

    在语句结束时,你不必手动输入分号,换行即可。

    function foo() {
        var bar = "value"
        return bar
    }
    
    // `{}` 包围的语句块的最后一个语句的分号也可省略
    function bar() { return "foo" }

    开发者们每写一行代码,就可以少敲打一次键盘,这看起来很人性化。但过于依赖分号自动插入,会带来一些潜在问题。

    function foo() {
        return
        {
            bar: 1
        }
    }
    
    function bar() {
        var a, b, c, d, e
        a = b + c
        (d + e).toString()
    }
    看看上面的代码,foo() 将返回什么?bar() 又将怎么运行?

    事实上,前者将返回 undefined,而后者的后两行代码将被理解为 a = b + c(d + e).toString()

    JavaScript 的分号自动插入的规则并不那么清晰可辨,老实地多敲几次键盘,可以避免那些让你摸不着头绪的bug在某一天突然出现。

    声明提升

    看看下面这段代码,我们将得到什么结果?

    var foo = 1;
    function bar() {
        // 这个条件成立吗?
        if (! foo) {
            var foo = 10;
        }
        alert(foo);
    }
    bar();

    那么这段代码呢?

    var a = 1;
    function b() {
        a = 10;
        return;
        function a() {}
    }
    b();
    alert(a);

    第1个例子,也许你会觉得是 “1″ ,因为 ! 1 为假,if 里的代码不会执行。而第2个例子,可能你认为应该是 “10″ 。

    事实上,结果相反,我们将分别得到 “10″ 和 “1″ 。

    在 JavaScript 中,变量、函数的声明会被提升到当前函数主体的顶部,而不管这个声明语句是否出现在了不可到达的地方。

    上面的两段其实等同于:

    var foo = 1;
    function bar() {
        var foo;
        if (! foo) {
            foo = 10;
        }
        alert(foo);
    }
    bar();
    
    var a = 1;
    function b() {
        function a() {}
        a = 10;
        return;
    }
    b();
    alert(a);

    需要注意的是,只有变量或函数的声明被提升了,而赋值语句并没有。

    “eval”

    eval是 JavaScript 的动态特性之一,在运行时, eval 可以将给定的字符串当作代码语句执行:

    <script>
    var func = <?php echo json_encode($user_send['func']); ?>;
    eval(func + "()");
    function sayHello() {}
    function sayGoodbye() {}
    </script>

    在代码中用一组字符串与变量拼出另一串代码来运行,这看起来吊爆了。

    但请在使用 eval 之前考虑下它将带来的潜在问题:

    1. 使用了 eval 的代码可阅读性很差,你读到这样的代码时很
    转载请注明(B5教程网)原文链接:https://b5.mxunkeji.com/content-74-128-1.html
    相关热词搜索: 语法陷阱