Javascript创建对象的的方式

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

  • 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'

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容