Javascript面向对象(三)——原型继承
2017-04-15 22:18
441 查看
Javascript面向对象(三)——原型继承
实际编程中,我们经常需要一些东西并扩展之。例如,我们有user对象,带有属性和方法,现在想要
admin和
guest,和其稍微有些变化,我们最好重用
user对象,但不是复制/重新实现它的方法,而是在其基础上构建。原型继承是Javascript重要特性,可以实现之。
[[Prototype]]
在Javascript中,对象有个特殊的隐藏属性
[[Prototype]](规范中的名称),其可以为null或引用其他对象,该对象称为原型:
这个
[[Prototype]]有个魔力的意思,当我们从对象中读取属性,如果没有找到,Javascript自动从其原型中查找。在编程中,这样机制被称为“原型继承”。很多酷的语言和编程技术是基于该机制。
属性
[[Prototype]]是内在的且隐藏的,但是有很多方式去设置它。
其一是使用
__proto__方式,代码如下:
let animal = { eats: true }; let rabbit = { jumps: true }; rabbit.__proto__ = animal;
请注意,
__proto__和
[[Prototype]]不同,后者是前者的getter/setter访问器,后面我们讨论其他方式,现在使用
__proto__够用。
如果我们在
rabbit中查找属性,没有发现,Javascript自动从
animal中查找。示例:
let animal = { eats: true }; let rabbit = { jumps: true };
rabbit.__proto__ = animal; // (*) // we can find both properties in rabbit now: alert( rabbit.eats ); // true (**) alert( rabbit.jumps ); // true
这里,我们说
animal是
rabbit的原型,或
rabbit原型继承自
animal对象。
所以如果
animal有许多有用的属性和方法,那么自动成为
rabbit对象的属性和方法,这些是继承的。
如果
animal有一个方法,可以在
rabbit中调用:
let animal = { eats: true, walk() { alert("Animal walk"); } }; let rabbit = { jumps: true, __proto__: animal }; // walk is taken from the prototype rabbit.walk(); // Animal walk
方法自动从原型中带来,如下图:
原型链可以更长:
let animal = { eats: true, walk() { alert("Animal walk"); } };
let rabbit = { jumps: true, __proto__: animal }; let longEar = { earLength: 10, __proto__: rabbit } // walk is taken from the prototype chain longEar.walk(); // Animal walk alert(longEar.jumps); // true (from rabbit)
实际上有两个限制:
1、不能循环引用。Javascript抛出错误,如果
__proto__循环引用。
2、
__proto__的值,只能赋值为对象或null,所有其他值(原始值)被忽略。
另外显而易见,只有有一个
[[Prototype]],不支持多继承。
读/写规则
原型仅用于reading属性。
对于数据属性(不是getter/setter访问器),写/删除操作直接通过对象实现。下面的例子,我们给
rabbit自己的
walk方法赋值:
let animal = { eats: true, walk() { /* this method won't be used by rabbit */ } };
let rabbit = { __proto__: animal } rabbit.walk = function() { alert("Rabbit! Bounce-bounce!"); }; rabbit.walk(); // Rabbit! Bounce-bounce!
现在,
rabbit.walk()在自己内部查找方法并立刻调用,没有使用原型方法。
对于getter/setter访问器,如果我们读写属性,他们在原型中查找并执行。示例,留意代码中的
admin.fullName属性。
let user = { name: "John", surname: "Smith", set fullName(value) { [this.name, this.surname] = value.split(" "); } get fullName() { return `${this.name} ${this.surname}`; } }; let admin = { __proto__: user, isAdmin: true }; alert(admin.fullName); // John Smith (*) // setter triggers! admin.fullName = "Alice Cooper"; // (**)
星号()行属性
admin.fullName,在原型
user中有getter访问器,所以他可以调用,(*)行属性在原型中有setter访问器,所以也可以调用。
this的值
上面的示例可能提出有趣的问题,在setfullName(value)内部this的值是什么?
this.name和
this.surname是那个对象的属性,
user或
admin?
答案是简单的:this根本不受原型影响。
无论方法出现在哪里,对象或原型。调用方法时,this总是“.”号前面的那个对象。
所以,setter是有admin调用,this是admin,不是user。
这实际是超级重要的事情,因为我们可能有一个大对象,带有很多方法,从它继承。那么我们能调用它的方法在子对象上,并修改子对象,而不是那个大对象。举例,这里
animal代表方法库,
rabbit使用他们。
调用
rabbit.sleep()在rabbit对象上,通过设置了
this.isSleeping:
// animal has methods let animal = { walk() { if (!this.isSleeping) { alert(`I walk`); } }, sleep() { this.isSleeping = true; } }; let rabbit = { name: "White Rabbit", __proto__: animal }; // modifies rabbit.isSleeping rabbit.sleep(); alert(rabbit.isSleeping); // true alert(animal.isSleeping); // undefined (no such property in the prototype)
结果图示如下:
如果我们有其他对象
bird,
snake等继承自
animal,他们也获得animal的方法。但this在每个方法中和调用其对象一致,是运行时确定(.前面的对象),不是animal。所以当我们写数据至
this,它实际存在在那些调用的子对象中。
结论是:方法是共享的,但对象状态不是。
总结
在 JavaScript, 所有对象有个隐藏[[Prototype]]属性,其值只能是其他对象或null.
我们能通过 obj.proto 访问它 (也有其他方法,后继续说明).
被[[Prototype]]引用的对象称为原型.
如果我们想对
obj的属性或调用方法,它不存在,那么JavaScript尝试去原型中查找. Write/delete 属性直接在对象上运行, 他们不使用原型 (除非属性确实是setter访问器).
如果我们调用obj.method(), 并且方法来自原型, this仍然代表当前调用obj.
相关文章推荐
- 深入javascript面向对象,js的原型链、继承
- JavaScript面向对象(二)--前端必须知道的原型和继承
- JavaScript高级程序设计之面向对象的程序设计之继承之原型链 第6.3.1讲笔记
- js面向对象、原型及继承(javaScript高级程序设计第3版)
- JavaScript面向对象(3)——原型与基于构造函数的继承模式(原型链)
- 【读书笔记】读《编写高质量代码—Web前端开发修炼之道》 - JavaScript原型继承与面向对象
- JavaScript面向对象-基于原型链和函数伪装组合的方式实现继承
- JavaScript面向对象——深入理解原型继承
- JavaScript 面向对象与原型、继承
- 面向对象的Javascript - 通过原型(Prototype)实现继承
- JavaScript-形象理解面向对象、原型和继承
- JavaScript面向对象的程序设计2(组合构造和原型 继承)
- 面向对象的JavaScript 五 ----- Javascript实现继承的方式(2)
- javascript 基于原型的知识--如何实现继承
- 浅析Javascript原型继承
- 面向对象的JavaScript(一) — 类及类的继承
- JavaScript的相关继承笔记以及使用外部库实现JavaScript的面向对象特性
- JavaScript 学习_4_原型_对象_继承
- JavaScript面向对象------继承
- Javascript 原型和继承(Prototypes and Inheritance)