# 原型和原型链
# 原型
无论什么时候,只要创建了一个函数,那么就会为之创建一个 prototype
属性,这个属性指向了函数的原型对象。在默认情况下,每个原型对象同样会创建一个constructor
属性,用于指向 prototype
属性所在函数的指针。使用构造函数创建的对象,会创建一个名为 [[prototype]]
的指针指向构造函数的原型对象。
# 三个属性
prototype
:每一个函数都有一个prototype
属性,指向原型对象。- 例外:
Function.prototype.bind()
没有prototype
属性
- 例外:
[[prototype]
:每一个实例对象都有一个[[prototype]]
内部属性,指向构造函数的原型对象。constructor
:每一个原型对象都有一个constructor
属性,指向关联的构造函数。
# 举个🌰
创建了一个函数后,默认情况下,其原型对象只获得一个 constructor
属性,该属性指向 Person
函数。
// 构造函数
function Person() {}
console.log(Person.prototype);
2
3
可以给原型对象添加属性和方法
// 构造函数
function Person() {}
Person.prototype.country = 'China';
Person.prototype.sex = 'man';
Person.prototype.age = 26;
Person.prototype.sayAge = function() {
console.log(this.age);
}
console.log(Person.prototype);
2
3
4
5
6
7
8
9
每个对象实例都有一个 __proto__
属性去访问原型对象,这里的 __proto__
属性是浏览器提供用于访问实例对象内部属性 [[prototype]]
。ECMAScript5中增加了一个 Object.getPrototypeOf()
方法,用于对象访问其原型对象。
// 构造函数
function Person() {}
Person.prototype.country = 'China';
Person.prototype.sex = 'man';
Person.prototype.age = 26;
Person.prototype.getAge = function() {
console.log(this.age);
}
var person1 = new Person();
person1.getAge();
console.log(person1);
var person2 = new Person();
person2.getAge();
console.log(person2);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
它们之间的关系如下图:
由于原型对象会共享自身的属性和方法,因此这里创建的2个Person对象实例能够直接访问 getAge()
- 当调用
person1.getAge()
时,会先在person1实例中搜索getAge()
,若找到就返回;若没有找到,则到person1的原型对象中去搜索。这里进行了2次搜索工作。 - 若在实例对象中添加了原型对象中同名的属性,那么该属性并不会重写原型对象中的值,只是作为实例对象自己的属性,不会被共享。例如:
// 构造函数
function Person() {}
Person.prototype.country = 'China';
Person.prototype.sex = 'man';
Person.prototype.age = 26;
Person.prototype.getAge = function() {
console.log(this.age);
}
var person1 = new Person();
person1.age = 28;
console.log(person1);
2
3
4
5
6
7
8
9
10
11
12
可以看出:只是为person1对象添加了一个 age
属性,而原型对象中的 age
属性的值不改变。
console.log(person1.age)
会输出28,而不是26。此时访问的是person1对象中的 age
属性。可以使用 delete person1.age
删除本对象上的属性,删除之后访问的就是原型对象上面的 age
属性了。
可以使用 hasOwnProperty()
来检测一个属性是存在于对象实例中,还是原型对象中。
# 原型链
当一个实例对象的原型对象是另一种类型的实例对象,且这个实例对象的原型对象同样又是另一种类型的实例对象,以此下去,最终会就形成了一个有限的原型链。原型链是JavaScript中一种能够实现继承的方式。
# 再举个🌰
function Animal() {}
Animal.prototype.category = 'Animal';
function Cat() {}
// Cat的原型对象赋值为Animal实例对象
Cat.prototype = new Animal();
Cat.prototype.category = 'Cat';
function Tiger() {}
// Tiger的原型对象赋值为Cat实例对象
Tiger.prototype = new Cat();
Tiger.prototype.category = 'Tiger';
var tiger = new Tiger();
console.log(tiger);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tiger 指向Tiger的原型,Tiger 的原型指向 Cat 的原型, Cat 的原型指向 Animal 的原型,Animal 的原型指向 Object 的原型。简单来说:__proto__
属性连接每个对象,形成了原型链。任何原型链的最底层都是 Object.prototype
。注意:Tiger 实例对象和 Cat 实例对象的原型对象都没有 constructor
属性,这是因为我们重写了原型对象,将其赋值为了使用 new
创建的新对象。
属性的描述符 →