美文网首页程序员
Javascript创建对象的的方式

Javascript创建对象的的方式

作者: 麻不烧 | 来源:发表于2016-12-01 21:52 被阅读243次

    对象:即是一系列属性和方法的集合。

    • 1.字面量方式
    • 2.工厂模式
    • 3.构造函数
    • 4.原型模式
    • 5.组合使用构造函数和原型模式
    • 6.动态原型模式

    以上六种方式是javascript创建对象的方法,每种方法都有自己的特点,下面是对每种方式的具体介绍。

    1.字面量方式

        var obj = {};
            obj.name = "tom";
            obj.age = 18;
            obj.say = function(){
                console.log("my name is "+this.name);
        };
    

    上面这种字面量创建对象的方式,是我们在编写Javascript代码时,经常用来创建对象的方式。它和以下代码是等价的。

        var obj = new Object();
            obj.name = "tom";
            obj.age = 18;
            obj.say = function(){
                console.log("my name is "+this.name);
        };
    

    使用字面量方式创建对象,显示很方便,当我们需要时,随时创建即可。但是它们只适合创建少量的对象。当我们需要创建很多对象时,显然这种方式是不实用的。

    2.工厂模式

    当我们需要创建出多个对象的时候,通过字面量方式创建对象的方法显然是不合理的。我们希望有这样一个容器,当我们需要创建对象时,我们只需将数据扔进去,返回给我们的就是一个个对象,虽然对象的属性值不尽相同,但是他们都拥有共同的属性。工厂模式就是为我们提供这个服务的。

        function Factory(name,age){
            var obj = {};
            obj.name = name;
            obj.age = age;
            obj.say = function(){
                console.log("my age is "+this.age+"old");
            }
            return obj;//创建的对象以返回值的方式返回给我们。
        }
    

    当我们需要创建对象的时候,我们将对象的私有数据扔进Factory这个机器工厂即可

    代码示例:

        var person1 = Factory("jack",18);
        var person2 = Factory("mark",22);
    

    工厂方式的问题:使用工厂模式能够创建一个包含所有信息的对象,可以无数次的调用的这个函数。虽然其解决了创建多个相似对象的问题,但却没有解决对象识别的问题,因为创建出来的对象始终是个Object实例(即如何得知一个对象的类型)。

        console.log(person1.constructor);//object()
        console.log(person1 instanceof Factory)//false
    

    对象的constructor属性,指向创建该对象的构造函数。
    instanceof是用来判断一个对象是否为一个构造函数的实例。

    3.构造函数

    在上文中,我们可以通过 new Object() 来实例化对象,Object()Array()Date()都是Javascript为我们提供的原生构造函数,我们可以通过这些构造函数来创建出不同类型的对象。

        var arr = new Array();//创建一个数组对象
        var date= new Date();//创建一个时间对象
    

    除了JS为我们提供的原生构造函数,我们也可以自己创建一个构造函数。

        function Friend(name,age,job){
            this.name = name;
            this.age = age;
            this.job = job;
            this.introduce = function(){
                console.log("my name is "+this.name+",my job is "+this.job);
            };
        };
        
        var friend1 = new Friend("Bob",17,"student");
        var friend2 = new Friend("Alens",25,"teacher");
        console.log(friend1.introduce==friend2.introduce)//false
    

    构造函数的特点

    • 1.函数名首字母大写,从而区别与普通的函数。
    • 2.在构造函数中使用this关键字
    • 3.创建对象的时候使用new关键字

    当我们通过new+构造函数实例化一个对象的时候,实际上代码会执行以下操作。

        var friend1 = new Friend("Bob",17,"student");;
        //1.首先会先创建一个实例化对象friend1
        //2.然后将构造函数Friend的作用域赋给该对象(this即指向该对象),也就相当于为该对象添加以下属性
        friend1.name = "Bob";
        friend1.age = 17;
        firend1.job = "student";
        //3.最后将该对象返回
    

    通过构造函数创建出来的对象,一眼看上去,感觉和工厂模式创建对象并没有什么区别,但是通过这种方式创建出来的对象,我们可以明确的判断创建该对象的构造函数。

        console.log(friend1.constructor);//function Friend()
        console.log(friend1 instanceof Friend)//true
    

    通过构造函数创建对象的方式,既解决了字面量方式创建对象的局限性(无法适应创建多个对象),也解决了工厂模式创建出来对象的不可识别类型的欠缺性。但是,当我们在通过构造函数创建多个对象的时候,虽然他们的属性是不同的,但是这些对象却拥有共同的方法introduce,这样的话,创建多少个对象,就会有多少个方法。这样的话,显得有些多余,并且对计算机内存也不利。

    使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍。——《JavaScript高级程序设计(第3版)》

    我们可以将这些对象共有的方法,定义在构造函数外面,有一个变量来引用该方法。这样的话,创建出来的每一个对象,都可以享用该方法。

    function Friend(name, age, job){
        this.name = name;
        this.age = age;
        this.job = job;
        this.introduce = introduce; 
    }  
    function introduce(){
         console.log("my name is "+this.name+",my job is "+this.job)
    } 
    var person1 = new Friend("Bob",17,"student");
    var person2 = new Friend("Alens",25,"teacher"); 
    console.log(person1.introduce === person2.introduce);  //true
    

    虽然这种做法,可以解决内存消耗,实现多个对象共享一个方法,但是显然这种做法是不利于整体代码的阅读性。并且,构造函数也被硬生生的拆开了。

    4.原型模式

    原型模式,是Javascript中的一种设计模式。指的是每一个构造函数,都有自己的一个原型对象prototype,而通过该构造函数创建出来的对象,都有一个属性__proto__,该属性指向创建该对象的构造函数的原型对象。即__proto__指向构造函数的prototype对象。正是这一连接赋予了JS对象属性的动态搜索特性:如果在对象本身找不到某个属性,那么就会通过这个连接到其原型对象中去找。

    示例代码:

    
    //先声明一个无参数、无内容的“空”构造函数
    function Person() {
    }
    
    //使用对象字面量语法重写Person的原型对象
    Person.prototype = {
        name: 'jack',
        age: '25',
        job: 'front-end web developer',
        sayName: function () {
           console.log(this.name);
        }
    };
    
    //因为上面使用对象字面量的方式完全重写了原型对象,
    //导致初始原型对象(Person.prototype)与构造函数(Person)之间的联系(constructor)被切断,
    //因此需要手动进行连接
    Object.defineProperty(Person.prototype, 'constructor', {
        enumerable: false,
        value: Person
    });
    
    //测试
    var person1 = new Person();
    var person2 = new Person();
    person2.name = 'Faker';//将person2对象的名字改为自身的名字
    console.log(person1.name);    //'jack'
    console.log(person2.name);    //'Faker'
    console.log(person1.sayName());    //'jack'
    console.log(person2.sayName());    //'Faker'
    console.log(person1.sayName === person2.sayName);    //true
    

    可以看到,通过原型模式,我们同样可以轻松地创建对象,而且可以像“继承”一般得到我们在原型对象中定义的默认属性,在此基础上,我们也可以对该对象随意地添加或修改属性及值。此外,通过上面最后一句测试代码还可以看出,其函数实现了完美的引用共享,从这一点上来说,原型模式真正解决了构造函数模式不能共享内部方法引用的问题。

    原型模式看起来不错,不过它也不是没有缺点。第一,它不像构造函数模式那样,初始化时即提供参数,这使得所有新创建的实例在一开始时长得一模一样;第二,封装性欠佳;第三,对于包含引用类型值的属性,会导致不应该出现的属性共享。

    对于第三个缺点,用代码更能说明问题:

    function Person() {
    }
    Person.prototype = {
        constructor: Person,    //这样恢复连接会导致该属性的[[Enumerable]]特性变为true。上面的Object.defineProperty()才是完美写法。
        name: 'Chuck',
        age: '25',
        job: 'Software Engineer',
        friends: ['Frank', 'Lambert'],
        sayName: function () {
            console.log(this.name);
        }
    };
    var person1 = new Person();
    var person2 = new Person();
    person1.friends.push('Lily');
    console.log(person1.friends);    //["Frank", "Lambert", "Lily"]
    console.log(person2.friends);    //["Frank", "Lambert", "Lily"]
    

    一般而言,我们都希望各个对象各有各的属性和值,相互没有影响。可像上面示例一样,原型模式共享了不应该共享的属性,这绝对不会是我们想要的结果

    5.组合使用构造函数和原型模式

    通过对上面几种创建对象方法的分析,我们希望是否有一种方式,能够在快速创建多个对象的同时,让这些对象既拥有自己的私有属性,也拥有一些共有的方法。同时,也不破坏构造函数创建对象的纯粹性。

    代码示例:

        function Person(name, age, job){
        this.name = name;
        this.age = age;
        this.job = job;
        this.friends = ['Frank', 'Lambert'];
    }
    Person.prototype = {
        constructor: Person,
        sayName: function(){
            console.log(this.name)
        }
    };
    var person1 = new Person('tom', 25, 'Software Engineer');
    var person2 = new Person('cindy', 18, 'doctor'); 
    person1.friends.push('Lily');
    console.log(person1.friends);    //["Frank", "Lambert", "Lily"]
    console.log(person2.friends);    //["Frank", "Lambert"]
    console.log(person1.sayName === person2.sayName);    //true
    

    通过这种方式创建出来的对象,使得对象实例拥有自己可完全支配的全部属性,同时还共享了方法引用以节省内存开销。

    6.动态原型模式

    到上一步的“组合模式”为止,就功能和性能上而言可以说已经达到我们的要求了,现在我们考虑是否可以对代码进一步优化,毕竟“组合模式”有两段代码,起码封装性看起来不够好。

    我们把需要共享的函数引用通过原型封装在构造函数中,在调用构造函数初始化对象实例的同时将该函数追加到原型对象中。当然,为了避免重复定义,需要加一个if判断。代码如下:

    function Person(name, age, job, friends){
        this.name = name;
        this.age =  age;
        this.job = job;
        this.friends = friends;
        if (typeof Person.prototype.sayName !== 'function') {
            Person.prototype.sayName = function(){
                return this.name;
            }
        }
    }
    
    var person1 = new Person('chuck', 25, 'Software Engineer', ['A','B','C']);
    console.log(person1.sayName());    //'chuck'
    

    以上六种创建对象的方式,是我们在编写代码时,经常用到的方法,具体要用哪中方法,还是要根据实际情况决定的。

    相关文章

      网友评论

        本文标题:Javascript创建对象的的方式

        本文链接:https://www.haomeiwen.com/subject/jalfmttx.html