原型与原型链是面试的常考点之一,所以颇有必要理解并掌握,本文尝试去弄清原型与原型链的关系,并经过图解的方式去帮助自身创建起原型与原型链的知识体系,使本身能在面试中能与面试官侃侃而谈,嘻嘻~面试
JavaScript常被描述为一种基于原型的语言(prototype-based language)---每一个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。所以若是往原型对象上添加属性和方法,那么全部由该对象实例化的实例对象均可以共享该原型上的属性和方法。bash
// 代码块1
function SuperType() {}; // 构造函数
SuperType.prototype; // 原型对象
SuperType.prototype.sayHello = function() {
console.log('Hello World!');
}
const s1 = new SuperType(); // 实例对象1
const s2 = new SuperType(); // 实例对象2
// 实例s1和实例s2均可以共享该原型上的方法sayHello()
s1.sayHello();
s2.sayHello();
复制代码
每一个构造函数都有一个指向原型对象的指针,经过.prototype属性访问,而原型对象都包含一个指向构造函数的指针,经过.constructor访问,以下图所示,其实就是一个循环引用。函数
// 接上述代码块1
SuperType.prototype.constructor === SuperType; // true
s1.__proto__ === SuperType.prototype; // true
复制代码
// 接上述代码块1
s1.constructor === SuperType; // true
复制代码
从上述代码咱们能够猜测:经过构造函数new出来的实例对象,是否也有一个指向实例化自身的构造函数的指针,可经过.constructor访问呢? 咱们尝试把实例对象s1打印出来看看!this
从打印结果咱们能够看出,实例对象s1自己并无constructor属性,而是经过原型向上查找__proto__,共享原型上的constructor属性,该属性最终指向SuperType。spa
所以构造函数、实例对象、原型对象三者的关系以下:prototype
咱们来回顾一下构造函数、原型和实例的关系:指针
每一个构造函数都有一个原型对象,而原型对象都包含一个指向构造函数的指针(constructor),而实例都包含一个指向原型对象的内部指针(proto)。code
而什么是原型链呢? 咱们知道,每一个对象都拥有一个原型对象,经过__proto__指针指向上一个原型,并从中继承方法和属性,同时原型对象也有可能拥有原型,这样一层一层,最终指向null。这就是原型链(prototype chain),经过原型链一个对象会拥有定义在其余对象中的属性和方法。cdn
接下来简单展现下原型链的运做机制:对象
function Person(name) {
this.name = name;
}
const p = new Person('jiaxin');
p; // Person {name: 'jiaxin'}
p.__proto__ === Person.prototype; // true
p.__proto__.__proto__ === Object.prototype; // true
p.__proto__.__proto__.__proto__ === null; // true
复制代码
上面只是简单地介绍了原型链的概念以及运做机制,而完整的原型链远远没有那么简单,来看一张图:
const f = new Function();
f; // 函数对象
f.__proto__ === Function.prototype; // true
f.__proto__.__proto__ === Object.prototype; // true
f.__proto__.__proto__.__proto__ === null; // true
复制代码
看下函数对象f的打印结果
function Person(name) {
this.name = name;
}
function Girl(name) {
Person.call(this, name);
}
const girl = new Girl('jiaxin');
girl.call(); // Uncaught TypeError: girl.call is not a function
复制代码
如上述代码所示,Person函数里面没有定义call()方法,为何Person能够访问call()方法呢?咱们把Person打印出来看一下,发现原来是在Person的原型上即Function.prototype上有call()方法能够访问。
一、每一个构造函数都有一个原型对象,而原型对象都包含一个指向构造函数的指针(constructor),而实例都包含一个指向原型对象的内部指针(proto)。
二、每一个对象拥有一个原型对象,经过 proto 指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null,这种关系被称为原型链。
三、当访问一个对象的属性 / 方法时,它不单单在该对象上查找,还会查找该对象的原型,以及该对象的原型的原型,一层一层向上查找,直到找到一个名字匹配的属性 / 方法或到达原型链的末尾(null)。