2

js中创建对象的方式有很多,尤其是基于原型的方式创建对象,是理解基于原型继承的基础。因此在这里汇总一下,并对各种方法的利弊进行总结和对比,不至于以后对这些概念有模糊。

简单方式创建

var o = new Object();

我们都知道,实际上在javascript中并没有所谓的类的概念。因此在创建对象时不能像面向对象语言那样,通过类A new出来一个a对象。但是javascript有一个特殊的Object。我们可以借助Object来new一个对象。
然后可以通过如下方式给对象增加属性或方法:

    o.name="abc";

但是因为没有类的约束,这种方式创建出来的对象无法实现对象的重复利用,并且没有一种固定的约数,操作起来可能会出现这样或者那样的意想不到的问题。
有这样一个例子:

    var a = new Object;  
    var b = new Object;  
    var c = new Object;  
    c[a]=a;  
    c[b]=b;  
    alert(c[a]===a); //输出什么

关于这个例子的具体解答详见 例子出处

工厂方法创建

直接看一个例子:

    function createPerson(){
        var o = new Object();
        o.name = "abc";
        o.age = 20;
        return o;    
    }

这种创建对象的方法是在一个function中new Object(),并且赋予属性和方法,最后return 带有这些属性和方法的对象。

但是当我们想通过

    var p1 = createPerson();
    alert(typeof(p1));//Object 仅能得到这个结果,实际上没有太大意义
    alert(p1 instanceof(类名???))//会发现其实并不存在一个Person类

构造函数的方式

    function Person(name,age){
        this.name = name;
        this.age = age;
        this.say = function(){
            //
        }
    }
    
    var p1 = new Person('abc',20);

构造函数的方式创建的对象:

  • 函数名即为类名

  • 通过this来定义属性

  • 通过new Person()创建对象

  • 并且有如下属性:

    alert(typeof(p1));// Person
    alert(p1 instanceof(Person));// true
  • 但是同时我们也发现这种方式创建对象的一个弊端。对于类中的say方法,每分配一个对象就会有一个say的内存空间被分配出来。有一个say的拷贝。如果方法特别多的时候,会造成内存空间的极大浪费。可以通过两种方式进行优化和改进:

    • 将say声明为全局的

          function say(){
          
          }

      然后在类的定义中通过:

          this.say = say;
    • 采用下面提到的基于原型的方式创建对象

让类中的行为统一指向全局的say方法。但是如果将所有的方法设为全局的时候,就可以被window对象调用,那么就破坏了对象的封装性;如果方法很多,会造成代码中充斥着大量的全局函数。

基于原型创建对象

    function Person(){
        
    }
    Person.prototype.name = "abc";
    Person.prototype.age= 20;
    Person.prototype.say= function(){
        alert(this.name+this.age);
    }
    
    
    var p1 = new Person();
    p1.say();//ok
    say();//no 完成了封装
    

原型是js中的一个特殊对象。当一个函数创建之后,会随之产生一个原型对象。该函数中的prototype属性指向该源性对象;
当通过该函数的构造函数创建一个具体对象时,在这个对象中,就会有一个_prop_属性指向原型。这些是js中的很重要的一种继承方式--基于原型的继承的基础。这里不再赘述。

基于原型创建对象时,如果对象的属性和方法特别多时,可以通过如下方式进行定义:

    Person.prototype = {
        name:"abc",
        age:20,
        say:function(){
        
        }
    }

称为原型重写。原型重写之后当我们再通过 var p1 = new Person()创建一个对象时,p1 的constructor != Person()了。由于原型重写了,而且没有通过prototype指向,从而指向了Object()。
如果constructor比较重要,可以再json格式的定义中手动制定

    constructor:Pserson

关于原型重写,我画了个示意图,比较容易理解:

图片描述

p1是原型重写前声明的对象,p2是原型重写

    Person.prototype.name = "123";

之后的声明的对象。
可以看出

  • constructor的指向确实没有自动变换,除非通过上述手动的方式进行修改。

  • 通过p2.name = "456",设置name时,会在自己的存储空间中存储。当然查找name属性时,也是从自己的内存空间中读取name值。


xuehen
806 声望14 粉丝

遇见更美的我