zoukankan      html  css  js  c++  java
  • -高级Javascript编程学习笔记----Javascript编程及架构设计最应该注意的基本点

    最小全局变量

    JavaScript通过函数管理作用域。在函数内部生命的变量只在这个函数内部,别的地方不可用。全局变量是指在函数外或是未声明直接简单使用的。每个Javascipt环境有一个全局对象,当你在任意函数外使用this都可以访问。你创建的每一个全部变量都成了这个全局对象的属性。在浏览器里,方便起见,该全局对象有个附件属性叫做window,此window指向该全局对象本身。

    myglobal = "hello"; // 不推荐写法
    console.log(myglobal); // "hello"
    console.log(window.myglobal); // "hello"
    console.log(window["myglobal"]); // "hello"
    console.log(this.myglobal); // "hello"

    全局变量问题

    尽量少使用全局变量,并且必须使用var声明。

    不推荐写法:

    function sum(x, y) {
       // 不推荐写法: 隐式全局变量 
       result = x + y;
       return result;
    }
    // 反例,勿使用 
    function foo() {
       var a = b = 0;
       // ...
    }

    推荐写法:

    function foo() {
       var a, b;
       // ... a = b = 0; // 两个均局部变量
    }

    Var的好处

    1.通过var创建的全局变量是不能被删除的。

    2.无var创建的隐式的全局变量是能被删除的。

    如代码示例:

    // 定义三个全局变量
    var global_var = 1;
    global_novar = 2; // 反面教材
    (function () {
       global_fromfunc = 3; // 反面教材
    }());
    
    // 试图删除
    delete global_var; // false
    delete global_novar; // true
    delete global_fromfunc; // true
    
    // 测试该删除
    typeof global_var; // "number"
    typeof global_novar; // "undefined"
    typeof global_fromfunc; // "undefined"

    访问全局对象

    在浏览中,全局对象可以通过window属性在代码的任何位置访问。但是在其他环境中,这个方便的属性可能被叫做其他东西,甚至在程序中不可用。如果你需要在没有硬编码的window标示符下访问全局对象,你可以在任何层级的函数作用域中做如下操作:

    var global = (function () {
       return this;
    }());

    这种方法可以随时获得全局对象,因为其在函数中被当做函数调用了(不是通过new操作),this总是指向全局对象。

    单Var模式

    在函数顶部使用单Var语句是比较有用的一种形式,其好处在于:

    1.提供了一个单一的地方去寻找功能所需要的局部变量。

    2.防止变量在定义之前使用了逻辑错误。

    3.帮助你记住声明的全局变量,因此较少了全局变量。

    4.少代码。

    function func() {
       var a = 1,
           b = 2,
           sum = a + b,
           myobject = {},
           i,
           j;
       // function body...
    }

    你可以使用一个var语句声明多个变量,并以都好分割。像这种初始化变量同时初始化值的做法很好。这样子可以防止逻辑错误和增加代码的可读性。

    预解析:var散布的问题

    Javascript中,你可以在函数中的任何位置声明多个var语句,并且他们就好像是在函数的顶部声明一样发挥作用,这种行为叫做hoisting(悬置,置顶解析,预解析)。当你使用了一个变量,然后不久在函数中又重新声明的话,就可能产生逻辑错误。对于Javascript,只要你的变量在同一个作用域中,他都被当做是声明的,即使是他在var声明前使用的时候。

    // 反例
    myname = "global"; // 全局变量
    function func() {
        alert(myname); // "undefined"
        var myname = "local";
        alert(myname); // "local"
    }
    func();

    var是预解析的,只有当实际的走到这里的时候,才会将var后边的常量放进堆栈中。但是var又是制定解析的并且后方替换前方已声明的变量时,所以才会有上边的undefiend。

    for循环

    针对for循环,要注意,特别是数组性能损失的问题,如:

    // 次佳的循环
    for (var i = 0; i < myarray.length; i++) {
       // 使用myarray[i]做点什么
    }

    每次循环myarray.length都要向数组遍历一次,这样性能损失时很大的。

    for (var i = 0, max = myarray.length; i < max; i++) {
       // 使用myarray[i]做点什么
    }

    这样写最好,获取length时,仅仅获取了一次。并且这样的写法也很好的体现了单var模式。

    for-in循环

    for-in循环应该用在非数组对象的遍历上,使用for-in进行循环也被称为“枚举”。

    从技术上将,你可以使用for-in循环数组(因为JavaScript中数组也是对象),但这是不推荐的。因为如果数组对象已被自定义的功能增强,就可能发生逻辑错误。另外,在for-in中,属性列表的顺序(序列)是不能保证的。所以最好数组使用正常的for循环,对象使用for-in循环。

    有个很重要的hasOwnProperty()方法,当遍历对象属性的时候可以过滤掉从原型链上下来的属性。

    思考下面一段代码:

    // 对象
    var man = {
    hands: 2,
    legs: 2,
    heads: 1
    };

    // 在代码的某个地方
    //
    一个方法添加给了所有对象
    if (typeof Object.prototype.clone === "undefined") {
    Object.prototype.clone = function () {};
    }

     

    在这个例子中,我们有一个使用对象字面量定义的名叫man的对象。在man定义完成后的某个地方,在对象原型上增加了一个很有用的名叫 clone()的方法。此原型链是实时的,这就意味着所有的对象自动可以访问新的方法。为了避免枚举man的时候出现clone()方法,你需要应用hasOwnProperty()方法过滤原型属性。如果不做过滤,会导致clone()函数显示出来,在大多数情况下这是不希望出现的。

    // 1.
    //
    for-in 循环
    for (var i in man) {
    if (man.hasOwnProperty(i)) { // 过滤
    console.log(i, ":", man[i]);
    }
    }
    /* 控制台显示结果
    hands : 2
    legs : 2
    heads : 1
    */
    // 2.
    //
    反面例子:
    //
    for-in loop without checking hasOwnProperty()
    for (var i in man) {
    console.log(i, ":", man[i]);
    }
    /*
    控制台显示结果
    hands : 2
    legs : 2
    heads : 1
    clone: function()
    */

     

    另外一种使用hasOwnProperty()的形式是取消Object.prototype上的方法。像是:

    for (var i in man) {
    if (Object.prototype.hasOwnProperty.call(man, i)) { // 过滤
    console.log(i, ":", man[i]);
    }
    }

    其好处在于在man对象重新定义hasOwnProperty情况下避免命名冲突。也避免了长属性查找对象的所有方法,你可以使用局部变量“缓存”它。

    var i, hasOwn = Object.prototype.hasOwnProperty;
    for (i in man) {
    if (hasOwn.call(man, i)) { // 过滤
    console.log(i, ":", man[i]);
    }
    }

    严格来说,不使用hasOwnProperty()并不是一个错误。根据任务以及你对代码的自信程度,你可以跳过它以提高些许的循环速度。但是当你对当前对象内容(和其原型链)不确定的时候,添加hasOwnProperty()更加保险些。

    格式化的变化(通不过JSLint)会直接忽略掉花括号,把if语句放到同一行上。其优点在于循环语句读起来就像一个完整的想法(每个元素都有一个自己的属性”X”,使用”X”干点什么):

    // 警告: 通不过JSLint检测
    var i, hasOwn = Object.prototype.hasOwnProperty;
    for (i in man) if (hasOwn.call(man, i)) { // 过滤
    console.log(i, ":", man[i]);
    }

    要记住eval()是魔鬼、

    此方法接受任意的字符串,并当做JavaScript代码来处理。

    // 反面示例
    var property = "name";
    alert(eval("obj." + property));
    
    // 更好的
    var property = "name";
    alert(obj[property]);

    使用eval()也带来了安全隐患,因为被执行的代码(例如从网络来)可能已被篡改。这是个很常见的反面教材,当处理Ajax请求得到的JSON 相应的时候。在这些情况下,最好使用JavaScript内置方法来解析JSON相应,以确保安全和有效。若浏览器不支持JSON.parse(),你可 以使用来自JSON.org的库。

    同样重要的是要记住,给setInterval(), setTimeout()和Function()构造函数传递字符串,大部分情况下,与使用eval()是类似的,因此要避免。在幕后,JavaScript仍需要评估和执行你给程序传递的字符串:

    // 反面示例
    setTimeout("myFunc()", 1000);
    setTimeout("myFunc(1, 2, 3)", 1000);

    // 更好的
    setTimeout(myFunc, 1000);
    setTimeout(function () {
    myFunc(1, 2, 3);
    }, 1000);

    使用新的Function()构造就类似于eval(),应小心接近。这可能是一个强大的构造,但往往被误用。如果你绝对必须使用eval(),你 可以考虑使用new Function()代替。有一个小的潜在好处,因为在新Function()中作代码评估是在局部函数作用域中运行,所以代码中任何被评估的通过var 定义的变量都不会自动变成全局变量。另一种方法来阻止自动全局变量是封装eval()调用到一个即时函数中。

    考虑下面这个例子,这里仅un作为全局变量污染了命名空间。

    console.log(typeof un);    // "undefined"
    console.log(typeof deux); // "undefined"
    console.log(typeof trois); // "undefined"

    var jsstring = "var un = 1; console.log(un);";
    eval(jsstring); // logs "1"

    jsstring = "var deux = 2; console.log(deux);";
    new Function(jsstring)(); // logs "2"

    jsstring = "var trois = 3; console.log(trois);";
    (function () {
    eval(jsstring);
    }()); // logs "3"

    console.log(typeof un); // number
    console.log(typeof deux); // "undefined"
    console.log(typeof trois); // "undefined"

    另一间eval()和Function构造不同的是eval()可以干扰作用域链,而Function()更安分守己些。不管你在哪里执行 Function(),它只看到全局作用域。所以其能很好的避免本地变量污染。在下面这个例子中,eval()可以访问和修改它外部作用域中的变量,这是 Function做不来的(注意到使用Function和new Function是相同的)。

    (function () {
    var local = 1;
    eval("local = 3; console.log(local)"); // logs "3"
    console.log(local); // logs "3"
    }());

    (function () {
    var local = 1;
    Function("console.log(typeof local);")(); // logs undefined
    }());
     
  • 相关阅读:
    nginx使用https协议
    DUBBO入门
    Zookeeper学习笔记4
    maven dependency:tree中反斜杠的含义
    CATALINA_BASE与CATALINA_HOME的区别
    log4j打印抛出异常时堆栈内容
    如何获取e.printStackTrace()的内容
    springboot+RabbitMQ 问题 RabbitListener 动态队列名称:Attribute value must be constant
    详细介绍Spring Boot + RabbitMQ实现延迟队列
    springboot集成rabbitmq(实战)
  • 原文地址:https://www.cnblogs.com/fang-beny/p/3633130.html
Copyright © 2011-2022 走看看