原型和原型链是JavaScript中很重要的一块知识点,因为实例、构造函数、原型函数之前关系,有时候会被绕晕,遂计划进行一波总结~
回顾
只要创建一个函数,就会按照特定的规则为这个函数创建一个prototype
属性(指向原型对象)
默认情况下,所有的原型的对象自动获得一个名为constructor
的属性,指回与之关联的构造函数
原型对象上包含由构造函数的实例共享的属性和方法
在自定义构造函数时,原型对象默认只会获得constructor
属性,其他的所有方法都继承自Object
每次调用构造函数创建一个新实例,这个实例内部[[prototype]]
指针就会被赋值为构造函数的原型对象,一般我们通过__proto__
进行访问对象的原型
实例 构造函数 原型 三者之间的关系
根据之前回顾的知识,若设Person
为构造函数,Person.prototype
即为原型对象,p=new Person()
为构造函数创建的实例,不难可以手绘此图~
根据上图,不难理解:实例与构造函数原型之间有直接的联系,而实例与构造函数之间没有直接联系。
1. 原型行为
下面根据一个例子来说明三者之间的关系
(1)构造函数和原型链循环引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function Person() {};
console.log(Person.prototype);
console.log(Person.prototype.constructor === Person);
|
(2)原型链终止于Object原型对象
1 2 3 4 5 6 7 8
|
console.log(Person.prototype.__proto__ === Object.prototype); console.log(Person.prototype.__proto__.constructor === Object); console.log(Person.prototype.__proto__.__proto__ === null);
|
(3)实例访问原型对象
1 2 3 4 5 6 7 8
|
let person1 = new Person(); console.log(person1.__proto__ === Person.prototype);
|
(4)原型操作方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
console.log(person1 instanceof Person); console.log(person1 instanceof Object);
console.log(Person.prototype.isPrototypeOf(person1));
console.log(Object.getPrototypeOf(person1) === Person.prototype);
let person = { name: 'Katrina', };
Object.setPrototypeOf(person1, person); console.log(Object.getPrototypeOf(person1) === person);
let person = { name: 'Katrina', };
let person2 = Object.create(person); console.log(Object.getPrototypeOf(person2) === person);
|
上述关系均可抽象为下图:
2. 原型层级
(1)对象属性的查找机制
对象属性的查找机制:在通过对象访问属性时,会按照这个属性的名称开始搜索。搜索开始于对象实例本身。如果在这个实例上发现了给定的名称,则返回该名称对应的值,如果没有找到这个属性,则搜索会沿着指针进入原型对象,然后在原型对象上找到属性后,再返回对应的值。如果直接原型中没有找到,就会去原型的原型进行寻找,直到找到Object.prototype
的原型,指向null
为止。
1 2 3 4 5 6 7 8 9 10 11 12 13
| function Person() {};
Person.prototype.name = 'Katrina'; Person.prototype.age = 18; Person.prototype.sayName = function() { console.log(`Hi, my name is ${this.name}.`); };
let p1 = new Person(); let p2 = new Person(); p1.name = 'y2d'; console.log(p1.name); console.log(p2.name);
|
从上面这个例子不难理解对象属性的查找机制,在p1
对象中含有name
属性,打印结果为y2d
,而p2
对象中不含name
属性,就沿着指针去对原型寻找name
属性并打印出Katrina
,同时也可以说明如果实例对象有和原型对象相同的属性,实例对象的属性会遮盖住原型对象的属性,会屏蔽对原型同名属性的访问。
(2)方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function Person() {};
Person.prototype.name = 'Katrina'; Person.prototype.age = 18; Person.prototype.sayName = function() { console.log(`Hi, my name is ${this.name}.`); };
let p1 = new Person(); let p2 = new Person(); p1.name = 'y2d'; console.log(p1.name); console.log(p2.name);
delete p1.name; console.log(p1.name);
|
- hasOwnProperty() 方法用于确定某个属性是实例的属性还是原型的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function Person() {};
Person.prototype.name = 'Katrina'; Person.prototype.age = 18; Person.prototype.sayName = function() { console.log(`Hi, my name is ${this.name}.`); };
let p1 = new Person();
p1.gender = 'female';
console.log(p1.hasOwnProperty('gender')); console.log(p1.hasOwnProperty('name'));
|
hasOwnProperty() 方法可用于改进for…in枚举对象属性时会枚举原型链上的可枚举属性的缺陷:循环语句 | for-in&&for-of的区别
知识补充
手写new
手写instanceof
手写Object.create
参考