JavaScript 进阶 – 1. 原型和原型链的概念
我们好多经常会被问道 JavaScript 原型和原型链的概念,还有关于继承,new 操作符相关的概念。本文就专门整理了原型和原型链的概念,关于对象继承我们后边进行介绍。本文包含对应的示例代码和脑图。如有奇异,欢迎指正!
目录讲解
- 1. 构造函数创建对象
-
2. 相关的名词介绍
- 2.1 prototype
- 2.2 proto
- 2.3 constructor
- 3. 实例和原型 原型和原型
- 4. 原型链是怎么产生的(附有相关的关系图说明)
1. 构造函数创建对象
我们先使用构造函数的方式声明一个对象:
function Person() {}
let person = new Person()
person.name = '小红'
console.log(person.name) // 小红
在上面的代码中。Person 是构造函数,person 是通过 new 方式创建的实例对象。
现在开始进入一个环节
2. 相关的名词介绍
prototype,constructor,__proto__是我们经常见到的几个概念,但是他们之间的关系具体是什么样的呢,让我们逐步开始了解。
2.1 prototype
每个函数都有 prototype 属性,除了 Function.prototype.bind(),该属性指向原型, prototype: 指向实例对象的原型对象
function Person() {}
Person.prototype.name = '小红'
let person = new Person()
console.log(person.name) // 小红
Person 这个函数有声明 prototype 属性,那么这个值指向的到底是哪儿,是原型对象吗?
其实 prototype 指向的是,调用当前构造函数创建实例对象的原型,也就是 person 的原型。
那么原型到底是什么呢?其实原型可以理解为,一个 JavaScript 对象 (null 除外) 在创建的时候会关联另外一个对象,这个被关联的对象就是我们说的原型对象,实例对象会在创建的时候,从原型对象继承一些属性或者方法。
对应的关系图如下:
2.2 proto
prototype 讲解了构造函数和原型对象之间的关系,那么实例对象和原型对象之间的关系又是怎么样的呢?下面讲解。每个 JavaScript 对象(null 除外),都会有个__proto__的属性,这个属性指向的就是原型对象
function Person() {}
let person = new Person()
console.log(person.__proto__ === Person.prototype) // true
// ES5 通过实例对象获取原型对象的方法
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
由此我们上面的管理系图谱可以补充为:
2.3 constructor
上面讲解了原型对象和构造函数,实例对象之间的关系,那么反过来获取原型对象和实例对象的构造函数是不是能获取到呢??constructor 就能做到这一点。constructor:指向该原型对象对应的构造函数
function Person() {}
let person = new Person()
// 原型对象的构造函数
console.log(Person.prototype.constructor === Person) // true
// 实例对象的构造函数
console.log(person.constructor === Person) // true
// 实例对象本身是否含有 constructor 属性
console.log(person.hasOwnProperty('constructor')) // false
注意:person.constructor 中实际上是没有 constructor 属性的,这是从 person 的原型中获取到的 constructor 属性才有了 person.constructor === Person。通过
关系图可以补充为:
3. 实例和原型 原型和原型
3.1 实例和原型
当我们读取一个对象的属性是,如果实例对象中能找到,就会返回实例对象对应的 value,如果没有找到,就会站到实例对象的原型对象中,查看有无此值,有则返回,没有的话,继续查找原型的原型对象上有无此值。一直会查到顶层为止。
function Person() {}
Person.prototype.name = '小红'
let person1 = new Person()
let person2 = new Person()
person2.name = '小明'
console.log(person1.name) // 小红
console.log(person2.name) // 小明
那么原型的原型执行的是什么呢?
3.2 原型和原型
实际上,原型的原型也是一个实例对象,是通过 Object 的构造函数生成的,因此,原型的原型也能通过__proto__获取其对应的原型对象的属性。
function Person() {}
Object.prototype.name = 'sunny'
let person1 = new Person()
let person2 = new Person()
console.log(person1.name) //sunny
console.log(person2.name) //sunny
对应的关系图如下:
4. 原型链是怎么产生的(附有相关的关系图说明)
console.log(Object.prototype.__proto__ === null) // true
Object.prototype 的原型为 null,null 代表的意思是没有对象,为空。换句话的意思就是说,没有原型对象了,这也就是查找的顶层对象了。
整个的关系图我们梳理为:
关系图中,红色的线其实就是我们平时说的 原型链
了
扩展内容
- 我们知道函数其实也是一个对象,任何函数都能看成是 Function()通过 new 实例化后的结果。因此,如果把 Person 和 Object 当做是实例对象的话,他们的原型指向的是构造函数 Function()的实例对象 Function.prototype。
console.log(Person.__proto__ === Function.prototype) //true
console.log(Object.__proto__ === Function.prototype) //true
- 原型对象 Function.prototype 的 constructor 指向的是构造函数 Function,Person 和 Object 本身没有 constructor 属性,但是其原型对象有该属性,因此也能获取到构造函数。
console.log(Function.prototype.constructor === Function) // true
console.log(Person.constructor === Function) //true
console.log(Person.hasOwnProperty('constructor')) //false
console.log(Object.constructor === Function) //true
console.log(Object.hasOwnProperty('constructor')) //false
- 所有的函数对象都可以看成是 Function 通过 new 之后生成的实例对象,那么 Function 可以看成是自己调用自己实例化的结果产生的。因此有 Function 的实例对象指向 Function.prototype
console.log(Function.prototype === Function.__proto__) // true
console.log(Function.prototype === Function.prototype) //true
- 如果此时的 Function.prototype 作为实例,那么他自己的实例对象执行的又是谁呢?所有的对象都可以看作是 Object 实例化的结果。所以,Function.prototype 的原型对象是 Object.prototype,其原型函数是 Object()。
console.log(Function.prototype.__proto__ === Object.prototype); //true
把这些全部总结完,之后我们的关系图,就变成了下面这个样子:
参考文章
JavaScript 深入之从原型到原型链
一张图理解 JS 的原型(prototype、_proto_、constructor 的三角关系)