start
多年以前学过JS中的原型,但是并没有彻底理解。导致每次看到 prototype —> 头大。
深入学习JS的过程中才发现,原型无处不在。
时至今日,就让我再学习一次 JavaScript 中的原型,顺便做个总结。
1. prototype属性
我定义一个函数 Tomato,然后打印一下它。
function Tomato() {
}
// 为了展示更详细的信息,我这里使用 `console.dir`
console.dir(Tomato)
可以发现函数Tomato,除了基本的几个属性,它还有一个属性:prototype。
函数的prototype属性是什么?可以到上面的截图,就是一个对象。
结论
在JavaScript中,我们创建一个函数A(就是声明一个函数), 那么浏览器就会在内存中创建一个对象B,而且每个函数都默认会有一个属性 prototype 指向了这个对象B( 即:prototype的属性的值是这个对象 )。这个对象B就是函数A的原型对象,简称函数的原型。
2. [[Prototype]]
基于函数 Tomato
,我去new
一个对象 t1
,然后打印一下t1
function Tomato() {
}
var t1 = new Tomato
console.dir(t1)
new
出来的对象 t1
只有一个属性 [[Prototype]]
。
小提示:
- 双中括号的属性是系统内置属性。
如何打印它
-
可以通过
Object.getPrototypeOf(t1)
来获取t1
的属性[[Prototype]]
-
也可以通过
t1.__proto__
来获取t1的属性[[Prototype]]
(ps:不要觉得__proto__
看起来很复杂,简单理解它就是一个属性名。某些时候为了防止变量名重复,命名时会在名称前后加两个下划线。)
打印出来的效果
console.log(t1.__proto__)
console.log(Object.getPrototypeOf(t1))
结论
对象的__proto__
属性是什么?可以看到它的打印,也是一个对象。
每个实例对象都有一个属性
__proto__
,它也指向了一个对象。
3. prototype
和__proto__
之间的联系
我们去比较一下prototype
和__proto__
这两个属性的值。
function Tomato() {
}
var t1 = new Tomato()
// 直接全等于,发现返回的是true。
console.log(t1.__proto__ === Tomato.prototype) // true
得到它们的关系图
结论
对象的
__proto__
属性,默认值为构造函数的prototype
属性值
4. prototype
和 __proto__
区分
这个地方容易混淆,这里单独拿出来记录一下。
- 每个函数function都有一个
prototype
,即显式原型(属性) - 每个实例对象都有一个
__proto__
,即隐式原型(属性 ) - 对象的隐式原型的值为对应构造函数的显式原型的值.
5. 原型对象中的constructor
上面打印可以看到,原型对象中有一个constructor属性,它指向我们函数对象。
6. 原型链
既然我们new
出来的对象t1
有一个属性__proto__
,并且指向了一个原型对象。原型对象也是对象啊,那么原型对象的属性__proto__
指向什么?
打印一下
function Tomato() {
console.log('我是Tomato')
}
var t1 = new Tomato()
console.log(t1.__proto__)
console.log(t1.__proto__.__proto__)
console.log(t1.__proto__.__proto__.__proto__)
代码不清晰,我继续画图
这个__proto__
指向的链条就是我们的原型链。感觉就是对象和对象之间的隐式链条,它的终点是null
7. 知道这些东西有什么用?
废话不多说,直接上代码:
function Tomato() {
}
// 1. 首先既然 `Tomato.prototype` 是一个对象,那么现在给对象中添加一些属性
Tomato.prototype.a = '我是Tomato的原型对象上的属性a'
Tomato.prototype.b = '我是Tomato的原型对象上的属性b'
Tomato.prototype.c = function () {
console.log('我是Tomato的原型对象上的方法c')
}
Tomato.prototype.d = function () {
console.log('我是Tomato的原型对象上的方法d')
}
var t1 = new Tomato()
// 2. 其次 t1 本身是一个空对象。 我也给它添加一些属性
t1.a = '你好'
// 3. 打印一下t1
console.log(t1)
console.log(t1.a)
console.log(t1.b)
console.log(t1.c)
console.log(t1.d)
console.log(t1.e)
输出结果:
确认一下,是不是从原型对象上获取的。
console.log(t1.b===Tomato.prototype.b) // true
console.log(t1.c===Tomato.prototype.c) //true
执行完上方的代码会发现,t1
本身是只有一个a
属性的,但是我打印 t1.b
,t1.c
,
会将Tomato.prototype
上的同名属性打印出来了。
当我们访问一个对象的属性的时候
- 先在自身属性上寻找,找到了就返回。
- 如果没有 就沿着
__proto__
这条链向上查找,找到返回。- 如果最终没有找到,就返回 undefined。
第二条我这里没有演示,可以自行测试一下。
end
- 原型的基本知识点就这些啦。