原型
什么是原型?
原型是函数上的一个属性,它定义了构造函数制造构造函数的公共祖先
原型的作用:
1属性和方法的共享(函数的原型prototype)
下面,我们来看一段代码
function Car( color, owner)
{
this.name='su7'
this.lang=5000
this.height=1400
this.color =color
this.owner='宇哥'
}
let Car1= new Car('black','鹏哥')
let Car2 = new Car('white','牛哥')
在这里,我定义了一个Car方法,当我们想要创建一个实例对象的时候,需要new一个实例对象,那么,如果我们想要创建多个实例对象,100个,1000个,是不是要写100行,1000行这种代码,这样显的是不是很不优雅?
那么我们有没有别的方法呢?
Car.prototype.name='su7'
Car.prototype.lang = 5000
Car.prototype.height = 1400
function Car( color, owner)
{
this.color =color
this.owner=owner
}
let Car1= new Car('black','鹏哥')
let Car2 = new Car('white','牛哥')
console.log(Car1.name)
console.log(Car2.lang)
在这里,我们把公共的属性 name='su7',lang = 5000,height = 1400定义在了外面, 我们来看打印结果
Car1和Car2没有name,lang ,height这几个属性,那么是不是这两个实例对象不包含这几个属性呢?
接下来,我们来访问一下实例对象里的这几个属性
Car.prototype.name='su7'
Car.prototype.lang = 5000
Car.prototype.height = 1400
function Car( color, owner)
{
this.color =color
this.owner=owner
}
let Car1= new Car('black','鹏哥')
let Car2 = new Car('white','牛哥')
console.log(Car1.name)
console.log(Car2.lang)
在这里,我们访问Car1的 name属性,Car2的 lang属性 这个时候,我们在控制台发现有打印结果
是不是很奇怪? 上面打印了Car1对象,发现没有name这个属性,但是访问的时候却能访问到
解释:在这这里,我们把name , lang ,height,叫做Car1 ,Car2 隐士具有的属性,也叫继承。于是我们得到一句话: 构造函数new 出来的对象会隐式继承到 构造函数原型上的属性
实例对象可修改显示继承到到的属性,但是无法增加,删除,修改隐士继承的属性(原型上的),那是可以查询隐式继承的属性
对象的原型 p.___proto ___
那么,两者的之间有什么关系呢?
function Person(){
}
let p =new Person()
console.log(Person.prototype)
console.log(p.__proto__)
在这里,我们打印函数的原型和由这个构造函数创建的实例对象的原型
我们发现,两者是一样的,这个时候,我们得出一个结论:
对象的隐式原型==创建它的构造函数的显示原型
Person.prototype.say=function(){
console.log('hello')
}
function Person(){
this.name='Tom'
}
let p =new Person()
console.log(p.say())
打印结果:hello
我们都知道,p是构造函数Person创建的一个实例对象,当我们访问p的say()方法时,会打印hello
那么为什么会这样呢?我们来打印一下实例对象p
我们发现,实例对象p里面有一个显示属性name,还有一个隐式属性say
解释一下,这里的constructor指的是由谁创建这个对象,比如这个实例对象p是由Peron这个构造函数创建的
这里,我们得出一个结论
对象会隐式继承创建它的构造函数的属性,js引擎在查找属性时,会先查找显示具有的属性,找不到,再查找隐式具有的属性
p.___proto ___ == Person.prototype(对象的隐式原型==构造函数的显示原型)
构造函数的显示原型Person.prototype是一个对象,在js当中,只要是一个对象,就有隐式原型
xxx的隐式原型是创建xxx的构造函数的显示原型,Person是由Object创建的,所以有
Person.prototype. p.___proto ___ ==Object.prototype
接下来,我们来看一段代码,来对原型有一个更深的理解
grandFather.prototype.say = function () {
console.log('haha')
}
function grandFather() {
this.age = 60
this.like = 'drink'
}
Father.prototype = new grandFather()
function Father() {
this.age = 40
this.fortune = {
card: 'visa'
}
}
Son.prototype = new Father()
function Son() {
this.age = 18
}
let p =new Son()
console.log(p.say())
当v8执行这段代码时,
1.因为son的显示属性没有say()方法,所以会找实例对象p的隐式原型p.___proto ___
2实例对象son的隐式原型p.___proto ___ 等于Son()构造函数的显示原型Son.prototype
p.___proto ___ == Son.prototype
3.Son()构造函数的显示原型Son.prototype等于一个由构造函数Father()创建的实例对象,记为father
4.我们发现father 里面没有say()方法,
5.所以会找 father的隐式原型 father.___proto ___ ==构造函数的显示原型
father.___proto ___==Father.prototype
Father.prototype ==new grandFather
6.我们发现它等于一个由 grandFather()创建的一个实例对象,这个对象里面也没有say()方法,,由new Father创建的对象记为grandfather
7.所以会找 grandfather的隐式原型 grandfather.___proto ___ ==构造函数的显示原型
grandfather.___proto ___==grandFather.prototype ==say()
所以,最终打印haha
我们把这种关系称之为原型链
什么是原型链?
原型链(Prototype Chain)是用于实现继承和属性查找的一种机制。当你访问一个对象的某个属性或方法时,如果该对象自身没有这个属性或方法,JavaScript引擎会沿着原型链向上查找,直到找到这个属性或方法或者到达原型链的终点。
-
原型链的形成:
- 当一个对象被创建时,其
__proto__属性被初始化为其构造函数的prototype。 - 构造函数的
prototype对象也有自己的__proto__属性,这通常是Object.prototype。 Object.prototype的__proto__属性是null,这标志着原型链的结束。
- 当一个对象被创建时,其
-
属性查找过程:
- 当尝试访问一个对象的属性时,JavaScript引擎首先检查该对象本身是否具有这个属性。
- 如果没有找到,引擎会沿着
__proto__链向上查找。 - 查找过程会一直持续,直到找到该属性或到达原型链的末端(
null)。
总结一下
原型与原型链总结
-
原型(Prototype) :
- 是函数的一个属性,用于定义对象的公共祖先。
- 通过构造函数创建的对象会继承其构造函数的原型上的属性和方法。
-
共享属性和方法:
- 公共属性和方法可以定义在构造函数的原型上,使所有实例共享这些属性和方法。
- 例如,将
Car.prototype.name定义在原型上,所有Car的实例都能访问name属性。
-
对象的隐式原型:
- 每个对象都有一个隐式原型属性
__proto__,指向其构造函数的原型对象。 - 实例对象的隐式原型
p.__proto__等于其构造函数的显示原型Person.prototype。
- 每个对象都有一个隐式原型属性
-
原型链(Prototype Chain) :
- 当访问对象属性时,JavaScript 引擎会先查找对象本身的属性,找不到则沿着原型链向上查找,直到
Object.prototype或null。 - 原型链用于实现继承和属性查找,是多个对象通过原型连接形成的链条。
- 当访问对象属性时,JavaScript 引擎会先查找对象本身的属性,找不到则沿着原型链向上查找,直到