前言:原型的理解在于你对于JavaScript中的对象和继承关系的认知,如果你并不是很清楚,那身为小白的你可以看看我写的原型,肯定会让你对原型有一个很好的理解
一、什么是JavaScript中的原型?
定义:是函数function对象的一个属性,它定义了构造函数制造的对象的公共祖先。通过构造函数产生对象,该对象可以继承原型的属性和方法,原型也是对象。
//Person.prototype --原型 数据结构: {}
Person.prototype.say = function(){
console.log('哈哈哈哈');
}
function Person(){
this.name = '红红'
}
let p = new Person() // 得到一个实例对象
p.say()
console.log(p)
vscode:js执行结果
浏览器:执行结果
以上代码发现:
p的显示对象中并没有发现有say()这个方法,但是在执行过程中依然可以访问,说明了p隐式继承了say()这个方法。通过浏览器的执行结果来看,我们可以发现Person下面有一个[[Prototype]]属性、且[[Prototype]]属性下面又有一个say()方法。由此我们可以说明,Person隐式继承了这个say()方法
二、原型的两种类型
函数原型:显示原型---prototype 函数天生具有的原型属性
Car.prototype ={
carName :'bgw',
height :1400,
lang :3900
}
//函数原型 显示原型
function Car(owner,color){
this.owner = owner
this.color = color
}
var a = new Car('snake','black')
console.log(a)
对象原型:隐式原型---[[Prototype]] __proto__对象天生具有的原型属性
<script>
Person.prototype.name = 'John';
function Person(){
this.a = 1
}
var person = new Person
console.log(person)//隐式原型 对象原型
</script>
三、原型特性
1. 利用原型的特点和概念,可以提取公有属性,不需要在每次创建对象时都执行,降低了代码的耦合度(代码的关联性)。
以下以一个创建车库的例子:
有一个宝马工厂生产了一车子,但是车子除了名字和颜色需要用户自己diy外,无需其他改变。
常规手法:代码一
function Car(owner,color){
this.carName = 'bgw'
this.height =1400
this.lang = 3900
this.owner = owner
this.color = color
}
var a = new Car('snake','black')
console.log(a)
用了原型特性的手法:代码二
Car.prototype ={
carName :'bgw',
height :1400,
lang :3900
}
function Car(owner,color){
this.owner = owner
this.color = color
}
var a = new Car('snake','black')
console.log(a)
对比两串代码的执行结果,我们发现,代码一中,每一条代码都重新执行了一遍,造成的结果是过于复杂,而代码二中提取出了公有属性,正好符合了我们的预期结果只打印出了,我们自定义的效果。因此,合理的利用原型,可以降低代码的耦合度。
2. 实例对象无法对原型做 增删改查
我们通一过一个修改的例子来展示,思考下现在的打印结果是什么
function Person(name){
this.name = name;
}
Person.prototype.lastName = 'yang'
var p = new Person('hong');
p.lastName = 'jiang'
console.log(p.lastName)
然后我们在修改一下代码,思考一下现在的打印结果是什么
function Person(name){
this.name = name;
}
Person.prototype.lastName = 'yang'
var p = new Person('hong');
var p2 = new Person('hong');
p.lastName = 'jiang'
console.log(p2.lastName)
下面来展示结果:
结果一
结果二
通过结果一和结果二对比,我们发现Person里面的隐式对象并没有发生改变。结果一里面只是又重新添加了一个name = jiang的属性,所以打印结果才有了,但是并没改变其中的原型对象,因此我们可以举一反三得出一个结论就是:实例对象无法对原型做 增删改查。(有不相信的码友可以自己去尝试尝试)
3 .实例对象的隐式原型 __ proto__ === 构造函数的显示原型(prototype)
<script>
Person.prototype.name = 'John';
function Person(){
this.a = 1
}
var person = new Person
console.log(person)//隐式原型 对象原型
</script>
我们打印出来发现对象身上也具有一个原型,函数自身身上也有一个原型,两者的值是相等的,因此,我们可以知道,为什么可以访问到原型身上的显示原型,正是因为,其对象身上的隐式原型等于函数身上的显示原型从而可以进行访问。
四、原型链
在原型上加一个原型,再加一个原型...把原型连成链状,这种原型的链状的访问顺序叫做原型链,直到找到null为止
我们来思考一个问题,实例对象的隐式原型等于什么?
很简单:等于构造函数的显示原型。
然后我们再思考,构造函数的显示原型是一个对象,那么构造函数的显示原型有没有隐式原型。
很显然:是对象就有隐式原型
<script>
function Init(){
}
var init = new Init();
console.log(init);//{__proto__:xxx}
</script>
即:[[Prototype]]:Object 也是一个对象
然后我们又可以发现:对象的隐式原型又等于构造函数的显示原型:即又是一个对象,那么对象又应该有一个原型。
我们发现这样子似乎好像可以无限套娃。但实际上,我们最终的结果只能到Object这一层。因为Object是最终的创建者,就到了底部了。即到了最终-- _ proto _:null 结束
了解了以上内容我们可以开始看看这个列子
<script>
Ground.prototype.lastName = '周'
function Ground(){
}
var ground = new Ground()
Father.prototype = ground
function Father(){
this.name = '子成'
this.fortune ={card:'visa'}
}
var father = new Father()
Son.prototype = father
function Son(){
this.hobit = 'readings'
}
var son = new Son()
console.log(son.lastName)
</script>
我们发从Son开始,往上加了一个Father的原型,然后Father的身上又加了一个Ground的原型,然后Ground自身身上又加了一个显示原型。因此当我们从儿子身上访问lastName属性时,我们会一路往上找。并且这种关系呈现一种链型的关系。因此我们称这种原型的链型关系,为原型链。
五、总结
JavaScript 中的对象,都有一个内置属性Prototype,指向这个对象的原型对象。当查找一个属性或方法时,如果在当前对象中找不到,会继续在当前对象的原型对象中查找;如果原型对象中依然没有找到,会继续在原型对象的原型中查找(原型也是对象,也有它自己的原型);直到找到为止,或者查找到最顶层的原型对象中也没有找到,就结束查找,返回 undefined。这个查找过程是一个链式的查找,每个对象都有一个到它自身原型对象的链接,这些链接组建的整个链条就是原型链。拥有相同原型的多个对象,他们的共同特征正是通过这种查找模式体现出来的。
读到这里如果对你有点作用的话,可以给一个小小的点赞吗,你的点赞是我持续写下去的动力。 然后有什么写的不好地方也欢迎指出不住。