zoukankan      html  css  js  c++  java
  • 探究javascript对象和数组的异同,及函数变量缓存技巧

      javascript中最经典也最受非议的一句话就是:javascript中一切皆是对象。这篇重点要提到的,就是任何jser都不陌生的Object和Array。

      有段时间曾经很诧异,到底两种数据类型用来存储数据有什么不同。于是,我打算探究探究。

     一、掌握三种数据类型

      首先,一个前提必须掌握的,就是必须理解javascript的数据类型分类,主要分为以下三种:

      第一种类型是标量(scalar),也就是一个单独的字符串(string)或数字(numbers),比如"北京"这个单独的词。

      第二种类型是序列(sequence),也就是若干个相关的数据按照一定顺序并列在一起,又叫做数组(array)或列表(List),比如"北京,上海"。

      第三种类型是映射(mapping),也就是一个键/值对(key/value),即数据有一个键名,还有一个与之相对应的键值,这又称作散列/哈希(hash)或字典(dictionary),比如"首都:北京"。

      从这里也可知道对象就是一种映射类型的数据数组是一种序列类型的数据

      比较细致的解答,可以参考阮一峰的博文《数据类型和Json格式

      

     二、声明和实例化方式

      1.对象的声明和实例化  
      

    //第一种方式
    var obj = new Object();
    obj.name = "wall";
    
    //第二种方式
    var obj = {};
    obj.name = "wall";
    
    //第三种方式
    var obj = {
       "name" : "wall" 
    };
    
    //第三种方式的简约版
    var obj = {
       name : "wall" //key减少了一对双引号
    };
    
    //第四种方式
    var obj = {};
    obj["name"] = "wall";
    

      

      2.数组的声明和实例化

      

    //第一种方式
    var arr = new Array();
    arr[0] = "wall";
    
    //第二种方式
    var arr = [];
    arr[0] = "wall";
    
    //第三种方式
    var arr = ["wall"];
    
    //第四种方式
    var arr = [];
    arr.push("wall");
    
    //诡异的第五种方式,也是后面讲的一个重点
    var arr = [];
    arr["name"] = "wall";
    

     三、使用方式

    //输出对象中name的值
    console.log(obj.name);
    //或
    console.log(obj["name"]);
    
    //输出数组中name的值
    console.log(arr[0]);
    
    //输出数组声明的第五种方式中的值
    console.log(arr["name"]);
    

     四、比较

      以上可以看出,其实在简单的数据存储要求上,选择数组和选择对象进行存储,差别不是很大,而且两者均可以用下标的方式进行访问,所以有时自己都有疑惑到底二者区别到底在哪里。

      最简单的不同就是:对象,系统默认是没有length属性的,而数组是默认有length属性的。而这,又牵扯出一个有趣的现象:

      在chrome的控制台下,输出一个简单的例子

      

      到这里,肯定会有人感到奇怪,为什么变量b的长度会是0! 为什么不是1。

      为了验证我的想法,就再编写一个例子:

      

      原来,b["test"]这个键值并没有放入数组的序列中去,那会跑哪里去了?于是我查了下b这个变量的结构,然后再重新实例化一个新的变量b,比较其不同,终于找到原因:

                          

      原来,test是作为数组的一个属性值进行存储,而不是添加到数组的数据序列中去,顿时豁然开朗,呵呵~

      这里也可以得出一个结论:数组是一个可以存储序列类型数据的对象,即是对对象的继承和扩展。数组的序列类型数据,可以通过整数下标进行读写操作,而其自定义的属性值,则可以通过对象访问属性值的方式进行访问,二者互不干扰。

      五、对象实例化中需要注意的地方  

    //第三种方式
    var obj = {
       "name" : "wall" 
    };
    
    //第三种方式的简约版
    var obj = {
       name : "wall" //key减少了一对双引号
    };
    

      虽然这两种方式看起来没什么不同,都可以使用obj.name进行访问,但是这里推荐第三种方式,而不要用简约版。

      原因就是如果用简约版的方式进行实例化,键名如果是javascript保留的关键字,在 ECMAScript 5 之前会抛出SyntaxError 的错误(来自javascript秘密花园的解释)。

     六、函数变量缓存的技巧

      我就不多说,直接先上代码:

    //定义函数
    var fun = function(options){
        if(!arguments.callee.arr){
          arguments.callee.arr = {
             "name":"wall",
             "who":"jser"        
          };
           console.log("init");//标识是否调用初始化
        }
    
        return arguments.callee.arr[options];
    }    
    
    //访问name
    console.log(fun("name"));
    
    //访问who
    console.log(fun("who"));
    

      运行结果如下:

      所以,很明显地,数据只要初始化一次,就会保存在当前函数对象下,作为它的一个属性值,下次操作就可以避免重复性的工作了。

      可能很多读者还不是很明白arguments.callee是什么,其实它指向的就是调用当前方法的对象,也即是fun。

      这样写的原因是,在实际生产过程中,多人协作编码,可能有人会一不小心把你这里的function改个名字什么的,那缓存的作用就失效了,甚至会出错。不过有个缺点就是在严格模式下,arguments.callee会被禁用。

      最后,再贴上一个未做上面这个优化的代码,其实效果也一样(其实就是将arguments.callee替换成fun)。

    //定义函数
    var fun = function(options){
        if(!fun.arr){
          fun.arr = {
             "name":"wall",
             "who":"jser"        
          };
           console.log("init");//标识是否调用初始化
        }
    
        return fun.arr[options];
    }    
    
    //访问name
    console.log(fun("name"));
    
    //访问who
    console.log(fun("who"));
    

      另外,这里的arguments.callee也可以替换成this指针,不过,调用的时候就要注意this指针的指向,这里可以用new function()的方式调用。

    via:cnblogs.com/walls/p/4281531.html 

  • 相关阅读:
    Spring bean作用域
    软件类说明文档排版建议
    fit_line_contour_xld拟合直线的五种算法的准确度比较
    .Net优秀开源(5)SqlSugar
    .NET[C#]中实现实体对象深拷贝(克隆/复制)的几种方法
    spring框架学习(14)AOP(中)
    .Net优秀开源(4)Castle.Core
    .Net优秀开源(3)Dapper
    .Net优秀开源(2)Autofac
    .Net优秀开源(1)
  • 原文地址:https://www.cnblogs.com/walls/p/4281531.html
Copyright © 2011-2022 走看看