Javascript 中定义对象的几种方式

Javascript 中定义对象的几种方式.

方式1:基于已有对象(obj) 扩充其属性和方法

js 中没有类的概念,只有对象。

<script type="text/javascript">
    var obj = new Object();
    obj.name = "zhangsan"; //定义一个属性
    obj.sayName = function (name)//定义一个方法
    {
        this.name = name;
        alert (this.name);
    }
    obj.sayName("lisi");
</script>

弊端:

对象只有一个(只有一个 obj),如果再想要一个只能在来一个 obj2

方式2: 工厂方式创建对象

类似于 java 中的静态方法。调用一个就new出来一个对象。

<script type="text/javascript">
    //工厂方式创建对象
    
    function createObject()
    {
        var obj = new Object();
        
        obj.username = "zhangsan";
        obj.password = "123";
    
        obj.get = function ()
        {
            alert (this.username + ", " + this.password);
        }
        return obj;    
    }
    var obj1 = createObject(); //通过调用工厂方法 createObject()创建第一个对象。
    var obj2 = createObject(); //通过调用工厂方法 createObject()创建第二个对象。
    obj1.get();
    obj2.get();
</script>

结果:创建出来了2个对象

但是我们注意到上面把 username passwword 写死了。稍微改一下:

<script type="text/javascript">
    //工厂方式创建对象
    
    function createObject(username, password)
    {
        var obj = new Object();
        
        obj.username = username;
        obj.password = password;
    
        obj.get = function ()
        {
            alert (this.username + ", " + this.password);
        }
        return obj;    
    }
    var obj1 = createObject("allen","qqq"); //通过调用工厂方法 createObject()创建第一个对象。
    var obj2 = createObject("jenny", "ooo"); //通过调用工厂方法 createObject()创建第二个对象。
    obj1.get();
    obj2.get();
</script>

结果:打印出 allen,qqq                   jenny,ooo

但是我们仍然觉得可以改进。我们注意看,上面的 obj.get 函数是定义在 createObject()中的。也就是说任何调用 createObject() 方法创建对象的时候,都要在内存中给 obj.get() 函数一个空间。这样明显很浪费。

最好的方式是只要多次调用 createOjbect() 就可以创建出多个对象。而这些对象在内存中共享一份 get() 函数代码。

我们可以改一下,把 get() 放在外面。如下:

<script type="text/javascript">
    //工厂方式创建对象
    function createObject(username, password)
    {
        var obj = new Object();
        
        obj.username = username;
        obj.password = password;
    
        obj.get = get;
        return obj;
    }
    function get()
    {
        alert (this.username + ", " + this.password);
    }
    
    var obj1 = createObject("allen","qqq"); //通过调用工厂方法 createObject()创建第一个对象。
    var obj2 = createObject("jenny", "ooo"); //通过调用工厂方法 createObject()创建第二个对象。
    
    obj1.get();
    obj2.get();
</script>

 那么最后这种做法是比较好的做法:让一个函数 被多个对象所共享,而不是每一个对象拥有一个函数对象。

当然也有一个潜在问题,就是一般我们希望属性和方法等都定义在一起,但是现在分开了。但这个没有解决方式。

3。 构造函数方式:

<script type="text/javascript">
    //构造函数方式创建对象
    function Person()//这是一个构造函数
    {
        //如果我们用new的方式,那么在执行第一行代码之前,js引擎会为我们生成一个对象
        this.username = "zhangsan";
        this.password = "qqq";
        
        this.getInfo = function()
        {
            alert(this.username + ", " + this.password);
        }
      //这里有一个隐藏的 return 语句, 用于将之前生成的对象返回。所以不需要显示的 return }
var person = new Person(); person.getInfo(); </script>

我们看,过去我们利用 createObject()创建对象的时候,代码中要return obj;

但是我们现在这个构造函数方法没有 return。而是利用了隐式的 return。

我们继续改一下,不把 username password hard code.

<script type="text/javascript">
    //构造函数方式创建对象
    function Person(username, password)
    {
        //如果我们用new的方式,那么在执行第一行代码之前,js引擎会为我们生成一个对象
        this.username = username;
        this.password = password;
        
        this.getInfo = function()
        {
            alert(this.username + ", " + this.password);
        }
        //这里有一个隐藏的 return 语句, 用于将之前生成的对象返回。所以不需要显示的 return
    }
    
    var person = new Person("user", "passwd");
    person.getInfo();
</script>

这个方法可以在构造对象的时候传递参数。

方式4:原型(prototype)方式

    <script type="text/javascript">
        //使用原型(prototype)方式创建对象
        function Person()
        {
        }
        Person.prototype.username = "zhangsan"; //给 Person 的原型附加属性
        Person.prototype.password = "qqq";
        Person.prototype.getInfo = function()
        {
            alert(this.username + ", " + this.password);
        }
        
        var person = new Person();
        var person2 = new Person();
        person.username = "lisi"; //修改 person对象的 username 值
        person.getInfo();
        person2.getInfo();

结果:

zhangsan, qqq

lisi, qqq

单纯使用 prototype 方式有2个问题:

1. 无法在构造函数中通过传参数为属性赋初值,只能在对象生成后再后续修改属性值。

2. 容易导致程序错误。

为什么容易导致程序错误?我们把code改一下, 把 username 变成 array

    <script type="text/javascript">
        //使用原型(prototype)方式创建对象
        function Person()
        {
            
        }
        Person.prototype.username = new Array("mary"); //username 变成 Array初始值 Mary
        Person.prototype.password = "123";
        Person.prototype.getInfo = function()
        {
            alert(this.username + ", " + this.password);
        }
        
        var person = new Person();
        var person2 = new Person();
        person.username.push("zhangsan");
        person.username.push("lisi");
        person.password = "456";
        person.getInfo();
        person2.getInfo(); //注意,这里我们没有对 person2 的属性进行修改
    </script>

我们注意,上面的程序只对 person 进行了属性修改, 没有对 person2对象更改属性,那么结果是不是应该是

mary, zhangsan, lisi, 456

mary, 123

但结果却是:

mary, zhangsan, lisi,456

mary, zhangsan, lisi, 123

奇怪,为什么没有改 person2 的 username, 它却也变成和person一样了呢?这是为什么呢?

这是因为我们是通过原型创建的对象,他们的属性值只有一份,是共享给 person 和 person2 的。

注意,这里 username 是 Array, Array是引用型的数据类型; password 是 string, string是常量型的数据类型。

当 person 对象更改 username 的时候,由于 Array是引用型的,所以不会创建新的 Array,仅仅是把内容变化了。

而当 person 对象更改 password 的时候,由于 String 是常量型的,所以会创建出一个新的 String 出来让 person去指向。而 person2仍然在指向原来的 String。

因此,我们看到在结果中 person 和 person2的 username 都相同,而 password各自指向自己的。

 总结一下就是:如果使用原型方法生成对象, 那么生成的所有对象会共享原型中的属性, 这样如果一个对象更改了属性,那么也会反映到其他对象中。

 4. 使用原型+构造函数的方式来定义对象

前面我们看到如果使用纯原型的方式生成对象,那么所有生成对象都会共享圆形中的一份属性,这样显然不太好。因为我们希望的是不同的对象有不同的属性,但是方法是一样的用一份就行了。那么怎么办呢?我们使用原型+构造函数的方法。

<html>
<head>
<script type="text/javascript">
    function Person() {
        this.username = new Array();
        this.password = "123";
    }
    Person.prototype.getInfo = function() {
        alert(this.username + ", " + this.password);
    }
    var p = new Person();
    var p2 = new Person();

    p.username.push("zhangsan");
    p2.username.push("lisi");

    p.getInfo();
    p2.getInfo();
</script>
</head>
<body>
</body>
</html>

结果:

zhangsan, 123

lisi, 123

当然你可以给这种方式传递参数

5. 动态原型方式

 那么我们看上面的方式 getInfo() 函数只能写在构造函数外面。我们是否能够写在里面,但是仍然该方法只被调用一次,即不会每个对象都要有一份浪费内存空间?

我们可以这样:

<html>
<head>
<script type="text/javascript">
    function Person(username, password) {
        this.username = username;
        this.password = password;
    
        if (typeof Person.flag == "undefined")
        {
            alert("invoked"); //判断是否确实只被执行了一次。
            Person.prototype.getInfo = function() {
                alert(this.username + ", " + this.password);
            }
            Person.flag = true;
        }
        
    }
    var p = new Person("zhangsan", "qqq");
    var p2 = new Person("lisi", "ff");

    p.getInfo();
    p2.getInfo();
</script>
</head>
<body>
</body>
</html>

结果:一次弹出

invoked

zhangsan, qqq

lisi, ff

动态原型方式:在构造体中通过标帜量让所有对象共享一个方法,而每个对象拥有自己的属性。

原文地址:https://www.cnblogs.com/backpacker/p/2619203.html