温馨提示:作者的爬坑记录,对你等大神完全没有价值,别在我这浪费生命
在上一篇博文javascript对象不完全探索记录03:小伙子,你对象咋来的?上篇,中大概说了说在js中,比较好理解的对象创建方式,分别是直接定义/字面量,和调用构造函数
你对象还有原型?
在一众博文及书中,有一个高级/不好看明白的方式,比上面这两种更收到推崇,那就是大名鼎鼎的原型方式,看到这个词,我表示不是我谦虚,是真懵逼,啥原型,什么原型,谁的原型?现看看别人给的例子
function Car() {
}
Car.prototype.color = "blue";
Car.prototype.doors = 4;
Car.prototype.mpg = 25;
Car.prototype.showColor = function() {
alert(this.color);
};
var oCar1 = new Car();
var oCar2 = new Car();
源自ECMAScript 定义类或对象 - W3school
别说,还真有那么点眼熟,这种对象名称,后面跟一个prototype
的写法,一直以来是我一个重要懵逼来源,话说这词,不就是原型的意思吗?
prototype
英 [ˈprəʊtətaɪp] 美 [ˈproʊtətaɪp]
n.
原型,雏形,蓝本
哈,在这等着我呢,其实认真一看这句Car.prototype.color = "blue";
的语法意思是给Car
的prototype
的color
赋值,翻译一下就是给Car
的原型中的color
属性赋值,所以说是不是能理解为原型也就是prototype
是对象的一个属性呢?还是从头了解吧
所以你原型是啥?
提到原型,就不能不提到javascript中的一个重要的懵逼概念 - 原型链
每个对象都有一个私有属性(称之为 [[Prototype]]),它持有一个连接到另一个称为其 prototype 对象(原型对象)的链接。该 prototype 对象又具有一个自己的原型,层层向上直到一个对象的原型为 null。(译者注:Object.getPrototypeOf(Object.prototype) === null; // true)根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
这段话我注意到的有几个关键词:每个对象,私有属性,链接,层层向上
用直白的话描述一下,在javascript中任何一个对象都有一个叫做原型对象的对象,这个原型对象就是传说中的prototype,而指向原型对象的链接/指针/箭头/->/都存在对象内部的一个私有属性中[[Prototype]]中(*见注1)
也就是说对象的[[Prototype]]中并不是直接存了原型对象,而是存着一个指向原型对象的链接//这也就使得其是动态的-待研究
由此可以想到的,既然每个对象都有原型对象,每个对象也都可以作为其他对象的原型对象,那么就会形成一个由[[Prototype]]属性组成的链,这就是传说中的原型链了,而利用原型链,对象可以访问其原型对象的属性及方法
*注1
[[prototype]]是一个隐藏属性,但很多浏览器都给每一个对象提供.__proto__这一属性,这个属性就是上文反复提到的该对象的[[prototype]]。由于这个属性不标准,因此一般不提倡使用。ES5中用Object.getPrototypeOf函数获得一个对象的[[prototype]]。ES6中,使用Object.setPrototypeOf可以直接修改一个对象的[[prototype]]
换句话说,任何一个对象,都是在另一个被叫做原型对象的基础之上被创建出来的,这也就是所谓的原型了
整这么麻烦干嘛?
就像我们知道的,在学园都市里有好多少女们/对象,她们各自有不同名字,头发颜色,以及超能力,她们可以展现自己的超能力,我们建立一个名叫GirlFriend()
的构造函数,来记录记录
function GirlFriend(name,hairColor,power){
this.name = name;
this.hairColor = hairColor;
this.showPower = function(){
console.log(power)
}
}
记录/实例化炮姐和黑子
var mikoto = new GirlFriend("Mikoto","brown","BiliBili");
var kuroko = new GirlFriend("Kuroko","black","Telesport");
mikoto.showPower();//BiliBili
kuroko.showPower();//Telesport
直到这里一切都很正常,但是却发现炮姐不是一个人!有人处于某种原因克隆了好多炮姐,如何记录炮姐的妹妹们呢,我们创建一个构造函数Sister()
用于记录炮姐的妹妹们
function Sister(level,number){
this.level = level;
this.number = number;
this.showLevel = function(){
console.log(this.level);
}
}
但是妹妹们也是由炮姐克隆而来的啊,炮姐有的属性她们也都应该有啊,怎么办,直接在Sister()
里新增属性吗?太麻烦了而且这就跟炮姐没关系了,炮姐哪天要是在GrilFriend()
里多录入一个新的属性,在Sister()
也还得继续添加。
就没有什么更好的方式吗,答案是肯定的
于是我们就用炮姐这个实例对象作为原型对象
Sister.prototype = mikoto;
在这里Sister.prototype
指的是由构造函数Sister()
生成的实例对象所对应的原型对象
说白了,上面这行代码的的作用就是让所有由Sister()
生成的实例对象的原型对象都是mikoto
,我们来试试结果
var sister = new Sister(3,'0001');
sister.showPower();//BiliBili
sister.showLevel();//3
到此为止一个拥有3级BiliBili能力的妹妹就诞生了
而且其整个的执行过程也与我们对链的理解一样,是从内到外,从这儿到那儿的
GirlFriend.prototype.age = 14;
console.log(sister);//见截图
console.log(sister.age)//14
从结果中可以看出,sister
对象内部并没有age
属性,在sister
对象的原型对象mikoto
中也没有age
属性,但是在mikoto
的原型对象中包含age
属性并且有值,所以sister
对象就顺着原型链一路找到了第一个age
属性
但其实sister的因为是被克隆出来的所以只有1岁而已
sister.age = 1;
console.log(sister);//见截图
console.log(sister.age)//1
从运行结果可以看出,sister
对象内部有age
属性,这个是sister
原型链上第一个age
属性,所以sister.age
的值就取1
为由GirlFriend()
实例化对象的原型对象增加属性age并赋值,看看sister.age
能在说细点吗
在上文代码和截图中出现了两个和prototype相关的词,prototype和_proto_,这俩货是干啥的?
其实上文提到了,构造函数Foo()的prototype属性指的就是这个构造函数所对应的原型对象,其实就是通过Foo()创建的对象的原型对象,所以prototype是构造函数所具有的一个属性
而_proto_属性是对应对象所说的,见上文注1所说,举个例子
sister.__proto__.age = 1
console.log(mikoto.age);//1
console.log(kuroko.age);//14
sister.__proto__.__proto__.age = 1
console.log(kuroko.age);//1
正如例子中表现的,对象可以通过_proto_
属性获得自己的原型对象,以及原型链上每一个对象
在截图中的原型对象中,还存在一个constructor
的属性,这个属性指向的就是这个原型对象所对应的构造函数,也就是那个构造出原型对象为该对象的函数,一句话概括就是构造函数和其对应的对象互相拥有彼此,构造函数将对象放在prototype
属性中,对象将构造函数放在constructor
属性中我想这就是爱情吧
这里再放一张图,就能更清除解释他们之间的关系了
源自知乎问题 - js中__proto__和prototype的区别和关系? - doris的回答
话说回来
绕了这么大一圈,还没忘我们为什么要研究原型吧,通过原型的方式创建对象的属性和方法,就可以利用同种对象类型的不同实例拥有想用的原型对象这一特性,避免重复创建,并且在修改原型对象的某个属性后,也可以通过原型链影响到其他所有相关对象上。
举个例子
GirlFriend.prototype.sayHello = function(){
console.log("Ohayo!")
}
kuroko.sayHello();//Ohayo!
sister.sayHello();//Ohayo!
并且说到底她们执行的都是同一个sayHello()
关于原型这块概念相对复杂,还设计嵌套,相互引用等等深坑,还是得先捋清楚再自己多做联系来理解和熟练运用啦
能看到这的估计都懵逼了
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。