• JavaScript对象创建


    1 JavaScript对象

        ECMA-262将对象(object)定义为"属性的无序集合,每个属性存放一个原始值、对象或函数"(unordered collection of properties each of which contains a primitive value, object, or function)。这意味着对象是无特定顺序的值的数组。在ECMAScript中,对象由特性(Attribute)构成,特性可以是原始值,也可以是引用值。如果特性存放的是函数,它将被看作对象的方法(Method),否则该特性被看作属性(Property)。

    2 对象的废除

        ECMAScript有无用存储单元收集程序(就像C#的垃圾收集器)意 味着不必专门销毁对象来释放内存。当再没有对对象的引用时,该对象就被废除了。运行无用存储单元收集程序时,所有废除的对象都会被销毁。每当函数执行完它 的代码,无用存储单元收集程序都会运行,释放所有的局部变量,还有在一些其它不可预知的情况下,无用存储单元收集程序也会运行。 
        
    把对象的所有引用都设置为null,可以强制性的废除对象。例如:

        Var oObject=new Object();

        // 程序逻辑

        oObject=null;

        当变量oObject设置为null后,对第一个创建的对象的引用就不存在了。这意味着下次运行无用存储单元收集程序时,该对象将被销毁。 
        
    每用完一个对象后,就将其废除,来释放内存,这是个好习惯。这样还 确保不再使用已经不能访问的对象,从而防止程序设计错误的出现。此外,旧的浏览器(如IE)没有完全的无用存储单元收集程序,所以卸载页面时,对象可能不 能被正确地销毁。以前IE6就有这样的诟病,当页面被关闭后对象还是没有被释放,所以总是会导致内存溢出的错误。废除对象和它所有的特性是确保内存正确使 用的最好方法。

    3 对象的类型

        JavaScript中对象分为:本地对象(native object)、内置对象(built-in object)、宿主对象(host object)。其中本地对象和宿主对象大家一般用的比较多,比较熟。这里我就重点说明一下内置对象。 
        
    ECMA-262把内置对象定义为"由ECMAScript实现提供的、独立于宿主环境的所有对象,在ECMAScript程序开始执行时出现"(any object supplied by an ECMAScript implementation, independent of the host environment, which is present at the start of the execution of an ECMAScript program.)。这意味着开发者不必明确实例化内置对象,它已经被实例化了。但ECMAScript只定义了两个内置对象:

    3.1 Math对象

        Math对象就是解决数学问题的所有公式。这个在各种编程语言中都有类似的实现,就不做介绍了。

    3.2 Global对象

        园子里很多搞ASP.net的,相信大家对其中的Global.asax非常熟悉了。但这个对象在ECMAScript中却比较特殊。因为它实际上根本就不存在。如果尝试编写下面的代码去实例化它,将得到错误: 
        
    Var _global=new Global(); 
        
    错误消息显示Global不是对象,但上文却说Global是一个 内置对象,这不就自相矛盾了吗?不矛盾。这里需要理解的主要概念是,在ECMAScript中,不存在独立的函数,所有的函数都必须是某个对象的方法。 ECMAScript中常用的函数例如isNaN()、isFinite()等,看起来都像独立的函数。实际上,它们都是Global对象的方法。而且 Global对象的方法还不止这些。

    4 定义类或对象

        虽然ECMAScript越来越正规化了,但创建对象的方法却被置之不理。在高级的编程语言(如C#)中,创建对象的方法被明确的定义了。所以不会有太大的分歧。但在脚本语言中创建对象的方法就是多种多样了。

    4.1 工厂方式

    由于对象的属性可在对象创建后动态定义,所以许多开发者都在初次引入JavaScript时编写类似下面的代码: 
        
    Var oCar=new Object(); 
        
    oCar.color="red"; 
        
    oCar.doors=4; 
        
    oCar.mpg=23; 
        
    oCar.showColor=function(){alert(this.color);}; 
        在这段代码中,创建对象car。然后给它设置几个属性:它的颜色是红色,有四个门,每加仑油23英里。最后一个属性是指向函数的指针,意味着该属性其实是个方法。执行这段代码后,就可以使用对象car了。可是要创建多个car实例就麻烦了。 
        
    要解决此问题,开发者可以创建并返回特定类型的对象的工厂函数。例如,函数CreateCar()可用于封装前面列出的创建car对象的操作: 
        
    Function createCar() 
        

            
    Var oTempCar=new Object(); 
            
    oTempCar.color="red"; 
            
    oTempCar.doors=4; 
            
    oTempCar.mpg=23; 
            
    oTempCar.showColor=function(){alert(this.color)};
        
            
    return oTempCar; 
        


        Var oCar1=createCar(); 
        
    Var oCar2=createCar();

        这里,前面的所有代码都包含在createCar()函数中,此外还有一行 额外的代码,返回Car对象作为函数值。调用此函数时,将创建新对象,并赋予它所有必要的属性,复制出一个前面说明的car对象。使用该方法,可以容易地 创建car对象的两个版本,他们的属性完全一样。当然,还可以修改creatCar()函数,给它传递各个属性的默认值,而不是赋予属性默认值: 
        
    Function createCar(sColor,iDoors,iMpg) 
        

            
    Var oTempCar=new Object(); 
            
    oTempCar.color= sColor; 
            
    oTempCar.doors= iDoors; 
            
    oTempCar.mpg= iMpg; 
            
    oTempCar.showColor=function(){alert(this.color)};

            return oTempCar; 
        

        
        
    Var oCar1=createCar("red",4,23); 
        
    Var oCar2=createCar("blue",2,26); 
        
    oCar1.showColor();            // 输出"red" 
        
    oCar2.showColor();            // 输出"blue"

        给createCar()函数加上参数,即可为要创建的car对象的 color、doors和mpg属性赋值。使这两个对象具有相同的属性,却有不同的属性值。但这里有个问题:每次调用函数createCar(),都要创 建新函数showColor(),意味着每个对象都有自己的showColor()版本。事实上,每个对象用的都是同一段代码。这样的定义方法还有一个如 下的变形:
        Function Car(sColor,iDoors,iMpg) 
        

            
    this.color= sColor; 
            
    this.doors= iDoors; 
            
    this.mpg= iMpg; 
            
    this.showColor=function(){alert(this.color)}; 
        
    }

        Var oCar1=new Car("red",4,23); 
        
    Var oCar2=new Car("blue",2,26);

        oCar1.showColor();            // 输出"red" 
        
    oCar2.showColor();            // 输出"blue" 
        这个方法和上一个方法有个一样的缺陷:重复的创建了showColor()函数。为了解决这个缺陷我们可以用下面的方法。

    4.2 原型方式

        该方法利用了对象的Prototype属性。用空构造函数来设置类名,然后所有的属性和方法都被直接赋予prototype属性: 
        
    Function Car() 
        
    {} 

        
    Car.prototype.color="red"; 
        
    Car.prototype.doors=4;
        
    Car.prototype.mpg=23; 
        
    Car.prototype.showColor=function(){alert(this.color)};

         Var oCar1=new Car(); 
        
    Var oCar2=new Car();

        使用这个方法可以解决重复创建showColor()函数,但又会有新的问题,考虑下面的例子: 

        
    Function Car() 
        
    {} 

        
    Car.prototype.color="red"; 
        
    Car.prototype.doors=4; 
        
    Car.prototype.mpg=23; 
        
    Car.prototype.drivers=new Array("Mike","Sue"); 
        
    Car.prototype.showColor=function(){alert(this.color)};

         Var oCar1=new Car(); 
        
    Var oCar2=new Car();

        oCar1.drivers.push("Matt"); 

        
    alert(oCar1.drivers);        // 输出"Mike,Sue,Matt" 
        
    alert(oCar2.drivers);        // 输出"Mike,Sue,Matt"

        这里,属性drivers是指向Array对象的指针。改变指针指向的内容,所有的实例都会改变。看来这种方法也不可取

        4.3 混合方式

        这种方式就是用构造函数定义对象的所有非函数属性,用原型方式定义对象的函数属性(方法)。结果所有的函数只创建一次,而每个对象都具有自己的对象属性实例。 
        
    Function Car(sColor,iDoors,iMpg) 
        

            
    this.color= sColor; 
            
    this.doors= iDoors;

    this.mpg= iMpg;
    Car.drivers=new Array("Mike","Sue"); 


    Car.prototype.showColor=function(){alert(this.color)}; 

    Var oCar1=new Car("red",4,23); 
    Var oCar2=new Car("blue",3,25); 

    oCar1.drivers.push("Matt"); 

    alert(oCar1.drivers);        // 输出"Mike,Sue,Matt" 
    alert(oCar2.drivers);        // 输出"Mike,Sue"

    这种方式是ECMAScript主要采用的方式,它具有其他方式的特性,却没有它们的缺陷。在实际编程中应用的也是最多了。 
    另外还有JSON创建方式。其创建的方式如下:

    var Car = 

        
    color: "red", 
        
    doors: 4, 
        
    mpg: 23, 
        drivers: [{name: "Mike", age: 20, Married: false}, {name: "Sue", age: 30, Marred: true}],
        showColor: function() {alert(this.color)} 
    }; 
    这种创建对象的方式也比较优雅。可作为Ajax返回的文本,然后用eval()函数将其还原成一个对象。著名的脚本框架JQuery就有专门接收返回文本为JSON对象的异步方法。

    ================华丽的分割线=====================

      其实javascript的new没什么难的,只是对class的一种模拟,这个也是业界说javascript的类根本不是类的原因——因为它是模拟的。

      new关键字只不过做了五件事情。

      1.创建Object

      2.查找class的prototype上的所有方法、属性,复制一份给创建的Object(注意,如果prototype上有属性是function或者数组或者Object,那么只复制指针)

      3.将构造函数classA内部的this指向创建的Object

      4.创建的Object的__proto__指向class的prototype

      5.执行构造函数class

      还摸不着头脑?

      没问题,我给你准备了代码,只要你会用javascript写一些基本功能,就应该可以看懂这段代码。  

    //定义类 类名字是 classA     
        function classA() {
            this.b = 1;
        }
        classA.prototype.b = 44;
        classA.prototype.show = function() {
            alert(this.b);
        };
        // 用new实例化   
        var b = new classA();
        b.show();
        // 用函数实例化    
        function newClass(cls, args) {
            var obj = {};
            for ( var p in cls.prototype)
                obj[p] = cls.prototype[p];
            obj.__proto__ = cls.prototype;
            cls.apply(obj, args || []);
            return obj;
        };
        var k = newClass(classA);
        k.show();

      就这么简单,当然,还有一些constructor神马的你可以自己填上去的。

      还有一个重要的问题,原型链,网上很多相关文章,这里就不介绍了。

    -------------------------------补充constructor------------------------------

    除了创建对象,构造函数(constructor) 还做了另一件有用的事情—自动为创建的新对象设置了原型对象(prototype object) 。原型对象存放于 ConstructorFunction.prototype 属性中。

    // 构造函数
    function Foo(y) {
      // 构造函数将会以特定模式创建对象:被创建的对象都会有"y"属性
      this.y = y;
    }
     
    // "Foo.prototype"存放了新建对象的原型引用
    // 所以我们可以将之用于定义继承和共享属性或方法
    // 所以,和上例一样,我们有了如下代码:
     
    // 继承属性"x"
    Foo.prototype.x = 10;
     
    // 继承方法"calculate"
    Foo.prototype.calculate = function (z) {
      return this.x + this.y + z;
    };
     
    // 使用foo模式创建 "b" and "c"
    var b = new Foo(20);
    var c = new Foo(30);
     
    // 调用继承的方法
    b.calculate(30); // 60
    c.calculate(40); // 80
     
    // 让我们看看是否使用了预期的属性
     
    console.log(
     
      b.__proto__ === Foo.prototype, // true
      c.__proto__ === Foo.prototype, // true
     
      // "Foo.prototype"自动创建了一个特殊的属性"constructor"
      // 指向a的构造函数本身
      // 实例"b"和"c"可以通过授权找到它并用以检测自己的构造函数
     
      b.constructor === Foo, // true
      c.constructor === Foo, // true
      Foo.prototype.constructor === Foo // true
     
      b.calculate === b.__proto__.calculate, // true
      b.__proto__.calculate === Foo.prototype.calculate // true
     
    );

    上述代码可表示为如下的关系:

    上述图示可以看出,每一个object都有一个prototype. 构造函数Foo也拥有自己的__proto__, 也就是Function.prototype, 而Function.prototype的__proto__指向了Object.prototype. 重申一遍,Foo.prototype只是一个显式的属性,也就是b和c的__proto__属性。

  • 相关阅读:
    根据坐标点画图形
    js 解析geojson格式
    devexpress 安装
    DataTable 获取列名
    ADO.NET 注册
    css:outline
    javascript函数sort
    引用类型-2015/10/06
    2015-11-02-js
    jquery
  • 原文地址:https://www.cnblogs.com/onlywujun/p/3515882.html
Copyright © 2020-2023  润新知