原型到底是个什么?

通过上一章节<面向对象入门>相信大家对于面向对象都有了一些很直白的了解,那么面向对象中一个很重要的概念"原型"又是个什么玩意呢?可以说,如果把"原型"给吃透了,那么你对于面向对象的知识点以及掌握50%了(当然,只是对于我们这些新手入门而已...).

我们知道,一切皆是对象,当然函数也不例外.那么既然是个对象,就一定有它的属性,只是很多隐藏的属性我们以前不知道而已.这里我就先说说函数的第一个比较重要的属性--prototype.

当我们创建了一个函数A之后(也就是申明),这个函数A就有了它默认的一个属性prototype,这个属性是内置好了.这时浏览器就会在内存中创建一个"对象B",而前面函数A的prototype的属性的值指向的就是这个"对象B",此时我们就称"对象B"为函数A的原型对象.他们之间的这种联系并不是简单的prototype的值指向"对象B",其实在"对象B"中也有一个默认的属性constructor,它的值指向了这个函数A!(注:其实任意函数中都有prototype,只不过不是构造函数的时候prototype我们不关注而已)

OK,说到这里,小伙们可能有点绕,那么我直接上图吧!(博主还是有一定美术功底的)

img1.png

这是一张简易的原型解析图,就先看最上面俩个框吧.函数A创建完毕后,它的默认属性prototype指向的是浏览器自动生成的对象B,而对象B的内置属性constructor指向的是这个函数A,此时,对象B就是函数A的原型对象!

好的,相信大家在博主生动形象的解析下对原型对象应该有了一个基本概念,那么这个原型对象它有什么用吗?诶,你们还别小看它,它的用处还真挺大的.

这里要小伙们回忆一下了,在上节中,我们知道了创建一个对象可以通过构造函数的方式来进行创建.当我们用上面的函数A作为构造函数来创建一个对象A1时,也就是var A1 = new A(); new一个对象出来.这时,对象A1其实也会有一个默认的属性值[proto].就像上面描述的,构造函数A它的默认属性prototype指向的是原型对象B,[[proto]]属性指向的也是原型对象B. 同一个构造函数能用于创建不同的对象,再次利用构造函数A来创建一个对象A2,它的[[proto]]指向的当然也是原型对象B.(现在大家可以回头看看我的img1.png了)

下面还是来看一个例子吧.

例1:
function Person(name,age){
  this.name = name;
  this.age = age;
}
var person1 = new Person("王先生",22);
var person2 = new Person("张先生",23);

person1 和 person2都是通过构造函数Person创建出的对象,所以他俩的proto指向的都是Person的对象原型.

hasOwnProperty( )方法

用于判断一个对象中的属性是否来自对象本身,也就是能判断它的来源,它是来自对象本身,还是来自这个对象的[[proto]]属性指向的原型.

若是来自于对象本身,则返回true, 来自于原型和不存在都返回fasle;

将例1稍微改动一下,在Person的原型对象中添加一个eat函数.

例2:
function Person(name,age){
    this.name = name;
    this.age = age;
}
Person.prototype.eat=function(){
    console.log('a');
}
var person1 = new Person("王先生",22);
var person2 = new Person("张先生",23);
//给person1对象添加属性sex
person1.sex = "男";
console.log(person1.hasOwnProperty('sex'));
console.log(person1.hasOwnProperty('eat'));
=>true
=>false

可以看到不管是name,age属性还是eat函数都是在构造函数时就写入了的,也就是都存在于Person的原型对象中,所以第二个console.log输出的就是false,而sex这个属性是我们在创建完对象person1之后添加的属性,所以可以理解为是person1的私有属性,是存在于person1对象本身,所以第一个console.log返回的就是true.

instanceof操作符和isPrototypeOf( )方法

俩个方法非常相似,都是用于检测一个对象是否来自于一个构造函数

//使用instanceof操作符
function A(){ }
var a1 = new A();
console.log(a1 instanceof A);
=>true

function A(){ }
var a1 = new A();
console.log(A.prototype.isPrototypeOf(a1));
=>true

isPrototypeOf()函数用于指示对象是否存在于另一个对象的原型链中。如果存在,返回true,否则返回false。

可以简单理解为一个对象是否是通过这个构造函数来创建的.

和instanceof相似,但instanceof是操作符,而isPrototypeOf( )是方法

使用组合模型和动态模型

组合模型

简单来说,就是属性在构造函数中创建,而方法在构造函数的原型中创建,如:

function Person(name,age){
  this.name = name;                     //直接在构造函数中封装属性;
  this.age = age;
}
Person.prototype.eat=function(food){    //在构造函数的原型(Person.prototype)中封装方法;
  alert(this.name+"like eat"+food);
}
Person.prototype.play=function(playName){
  alert(this.name+"like play"+playName);
}

var p1 = new Person("王先生",22);
var p2 = new Person("张先生",23);

p1.eat("拨娜娜");
p2.play("皮革");

动态模型

优点:封装性好

function Person(name,age){
  this.name = name;
  this.age = age;
  if(!Person.prototype.eat){                            //判断原型中是否有eat函数
    Person.prototype.eat=function(food){                //若没有的话则添加
      console.log(this.name+"like eat"+food)
    }
  }
  if(!Person.prototype.play){
    Person.prototype.play=funciton(playName){
      console.log(this.name+"like play"+playName)
    }
  }
}
//在此可以理解为每调用一次构造函数就执行构造函数,所以每执行一次就会把原先在原型中的函数舍弃,更改为和它一样的函数,则造成了有废弃的函数产生;
var p1 = new Person("王先生",22);      
var p2 = new Person("张先生",23);

以上俩种模型都有其各自的优点和缺点,那么有没有好点的模型来完善这俩种模型呢,下面来看看这个动态组合模型:

function Person(name,age){
  this.name = name;
  this.age = age;
}
Person.prototype = {
  eat:function(food){               
      console.log(this.name+"like eat"+food)
    }
  play:funciton(playName){
      console.log(this.name+"like play"+playName)
    }
}
//在此可以理解为每调用一次构造函数就执行构造函数,所以每执行一次就会把原先在原型中的函数舍弃,更改为和它一样的函数,则造成了有废弃的函数产生;
var p1 = new Person("王先生",22);      
var p2 = new Person("张先生",23);

终极动态组合模型

function Person(ldy){
  this._init(ldy);
}
Person.prototype = {
  _init:function(ldy){
    this.name = ldy.name;
    this.age = ldy.age;
  }
  eat:function(food){               
      console.log(this.name+"like eat"+food)
    }
  play:funciton(playName){
      console.log(this.name+"like play"+playName)
    }
}
//通过向构造函数中传递一个对象opt,这个对象中将要添加的属性添加进去
var p1 = new Person({
  name:"王先生",
  age:22,
})

最后这种终极动态组合模型摒弃了以往我们对于构造函数的看法,它在创建对象 p1的时候,传入进去的是一个对象,这样就可以传入不同数量的属性.

并且将要获取的属性全部直接封装到Person的原型对象中,这样构造函数Person中就只需要调用一下原型对象中的_init()函数就可以了(注:_init一般用于表示初始化),想要后续添加什么方法直接在Person的原型对象中添加.

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

推荐阅读更多精彩内容