通过 JavaScript.prototype 和 继承 - 01 这篇博客我们知道了两个规则.
- 在
javascript
中, 函数也是对象. 函数作为对象又和普通的对象有点差别.这个差别就是规则一里说的:所有的函数对象都会有一个prototype属性. prototype属性本身也是一个对象. - 所有通过
new 函数()
方式创建出来的对象,都是继承自这个函数对象的 prototype对象的.
非常别扭的写代码.
我平时很少使用到 new 关键字来创建对象(而是字面量) -> 虽然是字面量,实际上还是调用了 new Object()
但是为了弄清楚 原型和继承 也只能强行自己先这么写了.
一个很简单的例子
function Person (name, age) {
this.name = name
this.age = age
}
Person.prototype.showInfo = function () {
console.log(`${this.name} - ${this.age}`)
}
const p = new Person('张三', 22)
p.showInfo()
结果
张三 - 22
根据第一篇博客的规则一和规则二来说,这没什么毛病.
-
Person.prototype
有showInfo
方法 -
p
又是new Person
出来的. -
p
继承自Person.prototype
现在调用在调用 ..
p.toString()
结果
[object Object]
Person.prototype
上面压根就没有定义 toString()
方法啊.
p
里面更加没有定义.
单纯的根据规则一和规则二,无法解释这种现象.
规则三 --- 原型链
在 JavaScript
中,有一条隐蔽的链条 -----> 原型链.
它是原型对象之间的继承关系.
如果画一张图来理解的话如下:
当调用 p.toString()
方法时.
-
p
首先会在自己身上找,看有没有定义这和方法.如果没有. -
p
会顺着原型链找到Person.prototype
看有没有.如果没有 -
p
会继续往上找,找到Object.prototype
看没有有此方法的定义.如果有就执行,没有就会报toString is not function
的错误了.
流程图里的 proto 属性是个什么鬼?
在js中,所有对象其实都有一个__proto__
的属性.
(当然,函数对象比较特殊,它还有一个 prototype
对象)
这个属性指向的是当前对象继承的原型对象.
// 对象的 .__proto__ 指向了函数的 prototype 属性
console.log(`p.__proto__ === Person.prototype:${p.__proto__ === Person.prototype}`)
// 对象的 .__proto__.__protype__ 指向了 Object.prototype
console.log(`p.__proto__.__proto__ === Object.prototype:${p.__proto__.__proto__ === Object.prototype}`)
// 对象的 .proto__.__proto__.__proto 指向了原型链的顶层 ,也就是 null
console.log(`p.__proto__.__proto__.__proto__ === null:${p.__proto__.__proto__.__proto__ === null}`)
所以,利用 __proto__
可以证明上述调用 toString()
方法,原型追溯的过程是正确的.
补充:
通过对象的 __proto__
属性的追溯过程,顶部是null
. 可以很方便的知道当前对象的继承层数到底有几层.
function extendsCount (o) {
if (typeof o === "object") {
let count = 0
let proto = o.__proto__
while (proto) {
count++
proto = proto.__proto__
}
return count
}
return 0
}
console.log(extendsCount(p)) // 2