JavaScript面向对象-基于组合和动态原型创建对象

前面两篇文章我们介绍了JavaScript中原型的内存模型原型的重写方法即注意事项。在了解原型之后,我们就可以通过原型来创建JavaScript对象。基于原型的创建方式虽然可以有效的完成封装,但是依然会存在一些问题。

通过原型的方式来创建对象主要会产生2个问题:

  • 1、无法通过构造函数来设置对象的属性值。
  • 2、当属性中有引用类型变量时,可能会存在变量值的重复。

我们来看下面的例子:

function Person(){}
Person.prototype = {
  constructor:Person,
  name:  "Leon",
  age:22,
  friends:[  "Ada"  ,  "Chris"  ],
    say:  function  (){
    console.info( this .name+ "[" + this .friends+ "]" );
   }
}

在上面的代码中,我们创建了一个Person类,并通过原型重写的方式为它设置了一些属性和方法,其中有一个friends属性,是一个引用类型的数组。接下来我们通过Person类来创建对象,代码如下:

var  p1 =  new Person();
p1.name =  "John"  ;
p1.say();//控制台输出:Jhon[Ada,Chris]                             `

我们通过Person类创建了对象p1,可以看到使用原型方式创建对象时没有办法为对象p1设置属性,我们只有在对象创建之后才设置p1的name属性。接着调用p1的say()方法,控制台会输出Jhon[Ada,Chris],到这里一切都还是正常的。

如果我们接着为对象p1添加一个新的朋友,问题就会出现了。代码如下:

p1.friends.push("Mike");//为p1增加一个朋友(注意这里是在原型中添加)
p1.say();                          `

我们通过数组的push方法为p1添加一个新的朋友“Mike”,此时,在对象p1自己的空间中是没有friends属性的,所以“Mike”会被添加到Person的原型中,如下图所示:

使用原型方法创建对象的问题-1

由于新加入的数组元素是放置在原型中的,所以后面创建的所有对象都会共享这个属性,这时我们创建对象p2的话,他的朋友中也会有一个“Mike”。这是我们不希望看到的结果。

var  p2 =  new  Person();
//如果p1 push之后,原型中就多了一个人,p2也多了一个朋友`
p2.say();                              `
基于组合原型和构造函数的方式创建对象

为了解决上面的问题,我们可以使用基于组合原型和构造函数的方式来创建对象。也就是将属性在构造函数中定义,将方法在原型中定义。这种方式有效的集合了两者的优点,是我们在JavaScript中最常用的一种创建对象的方式。

function  Person(name,age,friends){
//属性在构造函数中定义`
  this.name = name;
  this.age = age;
  this.friends = friends;
}
Person.prototype = {`
    //方法在原型中定义
    constructor:Person,
    say:  function  (){
    console.info(this.name+"["+ this.friends+"]");
  }
}                             `

通过这种方式创建的对象,所有的属性都是保存在对象自己的空间中的。此时,在创建对象的时候,我们就可以为对象设置它自己的属性。

var p1 =  new  Person( "Leon",22,["Ada" , "Chris"]);
p1.name ="John";
p1.say();//控制台输出: John[Ada,Chris]                           `

完成上面的代码后,Person类及p1对象的内存模型如下图所示:

使用原型方法创建对象的问题-2

此时,我们再为对象p1添加一个新的朋友时,会在p1对象自己的内存空间中的friends属性中添加。这样,每个对象的属性都是独立的,不会互相干扰。

p1.friends.push(``"Mike"``);` `//为p1增加一个朋友(注意这里是在p1自己的空间中添加)`
p1.say();//控制台输出: John[Ada,Chris,Mike]
var  p2 =  new  Person();
p2.say();  //控制台输出: John[Ada,Chris]                             `

因此,现在再创建对象p2时,p2对象的friends只会是“Ada”和“Chris”,而没有“Mike”。

动态原型方式创建对象

虽然基于组合原型和构造函数的方式创建对象已经非常完美了,但是它和纯正的面向对象语言创建对象的方式还是有一些差别:类的方法被定义在类之外。为了让定义对象更加符合面向对象规范的需求,我们可以把定义方法的原型代码放置到Person构造函数中。这种方式我们称为动态原型方式创建对象。

function  Person(name,age,friends){
//属性在构造函数中定义`
  this.name = name;
  this.age = age;
  this.friends = friends;
  Person.prototype.say = {`
        console.info(this.name+"["+ this.friends+"]");
  } 
}

注意在使用动态原型方式创建对象的时候,我们在定义方法的时候不能够使用原型重写的方式,例如下面的代码是错误的:

// 错误的动态原型方式`
function  Person(name,age,friends){
//属性在构造函数中定义`
  this.name = name;
  this.age = age;
  this.friends = friends;
}

//不能使用原型重写的方式来设置方法`
Person.prototype = {
      constructor:Person,
      say:function(){
      console.info(this.name+"["+this.friends+"]");
    }
  }
}                             `

使用动态原型方式创建对象同样会存在问题,因为类中的方法是通过Person.prototype.say的方式创建的,这样每次创建对象的时候,都会在内存中创建一个新的say()方法。解决这个问题的方法是我们可以先做一个判断,看Person.prototype.say方法是否存在,不存在时才创建,否则就不创建。

// 动态原型方式`
function  Person(name,age,friends){
//属性在构造函数中定义`
  this.name = name;
  this.age = age;
  this.friends = friends;
}
//判断Person.prototype.say是否存在,不存在就创建`
if (!Person.prototype.say){
      alert("创建say方法" );
      Person.prototype.say = function(){
      console.info(  this.name+"["+this.friends+"]");
    }
}                           `

为了验证判断条件是否起作用,我们在代码中的判断分支中添加了一个弹出对话框语句。我们可以创建2个对象,然后2个对象分别调用say()方法,在结果中,第一个对象在调用say()方法时会弹出对话框,而第二个对象在调用say()方法时就不会在弹出对话框了,也就是说创建第二个对象时不会再添加say()方法。

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