前言
在JavaScript中,原型(Prototype)机制是一种核心特性,它支撑着面向对象编程的基本概念,如继承和对象属性的查找规则。理解原型、原型链以及隐式原型(
__proto__
)的概念对于深入掌握JavaScript的运行机制至关重要。
正文
下面,我将一步步的让你明白JavaScript中原型和原型链,不学会不要钱!
1. 原型(Prototype)
每个JavaScript函数都有一个特殊的属性叫做prototype
。
这个属性是一个对象,用于存储所有通过该函数作为构造函数创建的实例所共享的属性和方法。\
这样说的有点笼统,我们来看一下这个例子
js
Person.prototype.like='听歌'
function Person() {
this.name = '田总'
}
let p = new Person()
console.log(p.like)
我们现在往Person构造函数的原型上添加了like
属性为听歌,我们发现其对象能够输出为
我们再试验一下,给Person对象p的like
属性删除了,看看结果
js
Person.prototype.like='听歌'
function Person() {
this.name = '田总'
}
let p = new Person()
delete p.like
console.log(p.like)
依旧能够输出"听歌"
并且在我们修改p.like='撸铁'
时,输出变为了
简而言之,原型对象定义了一个特定类型的所有实例应该具有的属性和行为。
例如,当我们使用new
关键字基于某个构造函数创建新对象时,这个新对象会自动链接到构造函数的prototype
上,从而能够访问到原型上的属性和方法。
划重点!函数的原型(Prototype)我们称为显式原型!
为什么是显式原型,那是不是还有别的原型?
隐式原型
每个对象都有一个内部属性__proto__
,它指向该对象的原型。我们称为隐式原型
举个例子:我们写一个构造函数
js
function Son() {
this.name = 'Tom'
}
然后new一个新的对象:
js
let son = new Son()
那么在new这个对象的过程中,v8引擎会进行这么几项事情:
js
var this = {
name:'Tom'
}
this.__proto__ = Person.prototype
return this
生成一个新对象,然后把构造函数中的属性传给新对象,然后将构造函数的显式原型传给对象的隐式原型。最后返回这个对象给实例对象。
也就是说,实例对象的隐式原型其实就是其构造函数的显式原型
当一个实例对象尝试访问一个属性或方法时,如果该属性或方法不在实例本身上定义,JavaScript引擎会继续在其关联的原型对象上查找。这意味着,通过构造函数创建的所有对象都可以"继承"原型上的属性和方法。需要注意的是,实例对原型的这种访问是隐式的,并且原型属性是只读的,意味着实例不能直接修改原型上的属性,但可以通过覆盖实例自身的同名属性来达到类似效果。
这就是为什么在上面我们的p
实例对象可以访问到Person
的prototype
,就是因为p
对象继承了Person
的prototype
为__proto__
了解了显式原型和隐式原型,我们来了解一下原型链
原型链的概念源于JavaScript引擎在查找对象属性时的机制。如果在当前对象上找不到某个属性,引擎会继续在其__proto__
属性所指向的对象(即其原型对象)上查找,这个过程会递归进行,直到找到该属性或者遇到null
(标志着原型链的终点)。这种链式的查找方式确保了对象可以从其祖先那里继承属性和方法,构成了JavaScript中继承的核心逻辑。
我们在创造实例对象时,它具有一个隐式原型__proto__
它继承于其构造函数的显示原型prototype
。
那么在JavaScript中,万物皆为对象 ,所以其构造函数,也是一个对象,对象就有其隐式原型,指向其构造函数的构造函数的显式原型,原型也是对象,它也有隐式原型
是不是有点懵了
来看一下这一段代码
js
Grand.prototype.lastName = '张'
function Grand(){
this.name = '三'
}
Father.prototype = new Grand()
function Father() {
this.age = 40
}
Son.prototype = new Father()
function Son() {
this.like='coding'
}
let son = new Son()
console.log(son.lastName)
最后输出的是什么?
这个就是它的原型链,一层一层归属于上一级,最后搜索到底层Grand.prototype
找到lastName
输出
Grand
的显式原型prototype
就是最底层吗,并不是,prototype
是一个对象,那么它就有自己的__proto__
它指向创建其对象的Object.prototype
,而Object.prototype
也是一个对象,它也有隐式原型,Object
也是由一个function
创造出来的......
开始套娃
而Grand
函数也是一个对象,它也有其隐式原型,指向函数的显示原型Function.prototype
回到一开始继续套娃
最后,这么一张图诞生了
超级无敌套娃图
它会一直套娃下去吗,其实并不会,到了Object.prototype
的__proto__
其实就会指向null
了
原型链的魅力,你现在了解了吗
所有对象都有原型吗?
我们一直都说,JavaScript万物皆为对象,那么是不是所有对象都有原型呢?
不是
并非所有JavaScript对象都有原型。通过Object.create(null)
创建的对象是一个特例,它没有原型链------它的__proto__
为null
。这意味着该对象不继承自Object.prototype
,也不具备任何默认的原型属性和方法,如toString()
或hasOwnProperty()
。这样的对象常用于需要纯净、无污染对象的场景,比如构建某些特定的数据结构或实现特定的安全需求。
总结
JavaScript的原型机制提供了一种灵活而强大的方式来实现对象间的继承和属性共享。理解原型、原型链以及隐式原型的概念,是深入JavaScript语言内核、编写高效和可维护代码的基础。
求点赞评论收藏,有问题随时私信博主!