4

温馨提示:作者的爬坑记录,对你等大神完全没有价值,别在我这浪费生命

在上一篇博文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";的语法意思是给Carprototypecolor赋值,翻译一下就是给Car的原型中的color属性赋值,所以说是不是能理解为原型也就是prototype是对象的一个属性呢?还是从头了解吧

所以你原型是啥?

提到原型,就不能不提到javascript中的一个重要的懵逼概念 - 原型链

每个对象都有一个私有属性(称之为 [[Prototype]]),它持有一个连接到另一个称为其 prototype 对象(原型对象)的链接。该 prototype 对象又具有一个自己的原型,层层向上直到一个对象的原型为 null。(译者注:Object.getPrototypeOf(Object.prototype) === null; // true)根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

源自MDN Web docs - Web技术文档/javascript/继承与原型链

这段话我注意到的有几个关键词:每个对象,私有属性,链接,层层向上

用直白的话描述一下,在javascript中任何一个对象都有一个叫做原型对象的对象,这个原型对象就是传说中的prototype,而指向原型对象的链接/指针/箭头/->/都存在对象内部的一个私有属性中[[Prototype]]中(*见注1)

也就是说对象的[[Prototype]]中并不是直接存了原型对象,而是存着一个指向原型对象的链接//这也就使得其是动态的-待研究

由此可以想到的,既然每个对象都有原型对象,每个对象也都可以作为其他对象的原型对象,那么就会形成一个由[[Prototype]]属性组成的链,这就是传说中的原型链了,而利用原型链,对象可以访问其原型对象的属性及方法

*注1
[[prototype]]是一个隐藏属性,但很多浏览器都给每一个对象提供.__proto__这一属性,这个属性就是上文反复提到的该对象的[[prototype]]。由于这个属性不标准,因此一般不提倡使用。ES5中用Object.getPrototypeOf函数获得一个对象的[[prototype]]。ES6中,使用Object.setPrototypeOf可以直接修改一个对象的[[prototype]]

源自知乎问题 - js中__proto__和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属性中我想这就是爱情吧

这里再放一张图,就能更清除解释他们之间的关系了
prototype,_proto_,construtor之间关系

源自知乎问题 - js中__proto__和prototype的区别和关系? - doris的回答

话说回来

绕了这么大一圈,还没忘我们为什么要研究原型吧,通过原型的方式创建对象的属性和方法,就可以利用同种对象类型的不同实例拥有想用的原型对象这一特性,避免重复创建,并且在修改原型对象的某个属性后,也可以通过原型链影响到其他所有相关对象上。

举个例子

GirlFriend.prototype.sayHello = function(){
    console.log("Ohayo!")
}
kuroko.sayHello();//Ohayo!
sister.sayHello();//Ohayo!

并且说到底她们执行的都是同一个sayHello()

关于原型这块概念相对复杂,还设计嵌套,相互引用等等深坑,还是得先捋清楚再自己多做联系来理解和熟练运用啦
能看到这的估计都懵逼了


lskrat
478 声望43 粉丝

能不能借我个谱靠靠