JavaScript 原型继承原型链
Posted 白瑕
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript 原型继承原型链相关的知识,希望对你有一定的参考价值。
文章目录
前言
部分理论参考《javascript权威指南》
每一个对象都有与之相关的原型(prototype), 原型是在实例创建之初就设定好的, 作用是继承属性.
使用new调用构造函数会自动创建一个新对象, 因此构造函数本身只需要初始化这个新对象的状态即可. 调用构造函数的一个重要特征是构造函数的prototype
属性将会作为新对象的原型__proto__
.
通过new表达式创建的对象通常会继承一个constructor属性, 这个属性指代创建这个对象的构造函数, 因此一个对象的原型
应该为它的constructor.prototype
, 即其构造函数的原型(原型属性), 但对于由Object.create()创建的对象往往不是如此.
一、何时出现继承情况?
在JavaScript中, 只有在查询属性
时才能体会到继承的存在, 而设置属性(赋值和加入新属性)和继承无关, 这是JavaScript的一个重要特性, 该特性让编程者得以有选择性的覆盖继承来的属性.
举例一个设置属性的操作, 说明一下设置值过程中无法体现继承:
- 向对象内设置属性和赋值, 也总是只在原始对象里创建一个属性或者赋值已有的属性, 而不会对原型链进行操作.
function A()
this.do = function() return 'foo';
A.prototype = function()
this.do = function() return 'bar';
var x = new A().do();
new A(), 构造出原型指向function A的对象, 对象上有个do
方法;
"调用时优先去自身实例上寻找方法, 如果找到将不再去原型上寻找."按照这个规则来, 不需要看A.prototype
赋值这步即可得出foo
的答案.
算上A.prototype赋值这步的话, A.prototype
赋值只是改变A.prototype
的值为一个表达式, 这并没有向原型上绑定任何方法(绑的话一般A.prototype.do=xxx
格式).
但是哪怕只是在赋值, 这步也算在重写原型
了, 重写原型后, 新原型与已存在的实例的联系遭到切断, 而已存在的实例仍旧会选择引用以前的原型(而以前的原型又不在了, 所以会找不到).
放在这里, 如果"优先去实例上寻找"这条规则已经无法解决问题, 实例上已经无法找到, 直接去原型A.prototyp
e寻找do方法
的话, 会发现A.prototype
已经被赋值为函数, 无法找到而导致报错.
所以调用do()
方法调用的实际上还是第一步在原型上的do
方法, 那么结果就为foo
;
二、继承的体现&原型链的形成
对比一个查找属性的操作:
- 现在要在对象中查找属性x, 如果对象中不存在x, 那么将会去这个对象的原型对象中继续寻找x;
- 如果原型对象里也没有x, 但是这个原型对象还有它自己的原型, 那么就再去这个原型对象的原型上寻找x;
- 就这样一级一级的往上找, 直到找到x或者查找到一个原型是null的原型为止;
- 由这一条也可以看出原型对象一级一级构成了一种链状结构, 即常说的
原型链
, 属性的继承
即是通过原型链来实现的.
如果两个对象继承自同一个原型, 往往意味着他们是由同一个构造函数创建并初始化的.
一个简单的原型链, 从grandpa到son, son寻找num沿着原型链一路向上找, 最后在grandpa的原型里找到, 作为自己原型中的num.
function grandpa()
this.num = 935;
//此时new grandpa()会构造出对象" num: 935 "
var vardad = function dad()
vardad.prototype = new grandpa(); //第一段链
var varson = function son() ;
varson.prototype = new vardad(); //第二段链
console.log(varson.prototype.num);
三、例-求以下代码的输出
function father()
this.num = 935;
this.work = [ 'read', 'write', 'listen' ];
function son()
son.prototype = new father();
let son1 = new son();
let son2 = new son();
son1.num = 117;
son1.work.pop();
console.log(son2.num);
console.log(son2.work);
son.prototype本该为空对象, 现在被赋值为father构造的对象num: 935, work: [ 'read', 'write', 'listen' ]
;
let son1和son2以function son()为原型构造的仍旧都是空对象, 此时son1被加入num属性赋值为117(对象是可以直接这样加属性的, 所以不用去实例或者原型上找num);
但是work加入后没有值直接被执行了方法, 对于work如果依旧在实例上直接找那么完全不行, son1实例上本来就是空的, 现在只有个num, 那么必须从初始赋值了father构造对象的son原型上寻找work, 得到work为[ 'read', 'write', 'listen' ]
, 执行pop(), 直接把function son()原型的work改成了[ ‘read’, ‘write’ ];
然后做完发现和son1没有半毛钱关系, 他输出的son2.
那就只能num, work全去原型上找了, 得到结果935, 和被son1 pop()
过的work
: [ ‘read’, ‘write’ ]
总结
复习点理论.
以上是关于JavaScript 原型继承原型链的主要内容,如果未能解决你的问题,请参考以下文章
JavaScript高级原型和继承相关:原型对象函数原型原型链和继承继承的优化对象判断相关方法