通过前段日子遇到的一道题, 分析一下, 用自己的想法进行解读, 加深自己对原型和原型链的理解. 如有错误的地方, 请大家在评论区多多指正
一, 题目
下面程序运行结果是什么?
- function Animal() {
- this.name = 'Animal';
- }
- Animal.prototype.changeName = function (name) {
- this.name = name;
- }
- function Cat() {
- this.name = 'Cat';
- }
- var animal = new Animal();
- Cat.prototype = animal;
- Cat.prototype.constructor = Cat;
- var cat = new Cat();
- animal.changeName('Tiger');
- console.log(cat.name)
- A. Animal
- B. Cat
- C. Tiger
D. 都不是
答案是 B Cat
xml 代码行上显示
二, 解读
1. 原型对象
无论什么时候, 只要创建了一个新函数, 就会根据一组特定的规则为该函数创建一个 prototype 属性, 这个属性指向函数的原型对象. 在默认情况下, 所有的原型对象都会自动获得一个 constructor(构造函数) 属性, 这个属性是一个指向 prototype 属性所在函数的指针.
下面用图来说明
- function Animal() {
- this.name = 'Animal';
- }
- Animal.prototype.changeName = function (name) {
- this.name = name;
- }
首先创建了一个 Animal 函数, Animal 中含有一个 prototype 属性, 指向 Animal Prototype, 而 Animal.prototype.constructor 指向 Animal. 这个时候由于 name 属性是在函数中定义的, 所以不在 Animal Prototype 中, 而 changeName 函数是通过 Animal.prototype.changeName 定义的, 所以我们可以通过这种方式, 在实例化多个对象时, 共享原型所保存的方法.
同理, 当创建了 Cat 函数时, 也是一样.
- function Cat() {
- this.name = 'Cat';
- }
2. 创建实例
当调用构造函数创建一个新实例后, 该实例的内部将包含一个指针 (内部属性), 指向构造函数的原型对象. 在 ECMA-262 第 5 版中管这个指针叫 [[Prototype]]. 虽然在脚本中没有标准的方式访问 [[Prototype]], 但 Firefox,Safari 和 Chrome 在每个对象上都支持一个属性 proto. 明确重要的一点, 这个连接存在于实例与构造函数的原型对象之间, 而不是存在于实例与构造函数之间.
- // 将 Cat 的原型对象指向 animal 实例, 获得 animal 中的属性, 原有的属性丢失
- Cat.prototype = animal;
这一部分相当于是把 Cat 的原型对象的指针指向了 animal 实例, 所以原来 Cat 原型对象中的 constructor 属性丢失, 替换成了 animal 实例中的属性, 包括 name 属性以及 proto 内部属性, 同时 proto 属性也指向 Animal.prototype, 因此 Cat 也可以通过原型链查找调用到 Animal 中的属性和方法.
- // 相当于重新创建了 constructor, 指向 Cat 构造函数
- Cat.prototype.constructor = Cat;
来源: http://www.qdfuns.com/article/51117/abdb601ffbc4c45235a1f45e62b4fc0b.html