内容简介:JavaScript 内建的继承方法被称为 原型对象链(又叫原型对象继承)。原型对象的属性可经由对象实例访问,这就是继承的一种形式。对象实例继承了原型对象的属性,而原型对象也是一个对象,它也有自己的原型对象并继承其属性,以此类推。这就是原型对象链。
JavaScript 内建的继承方法被称为 原型对象链(又叫原型对象继承)。
原型对象的属性可经由对象实例访问,这就是继承的一种形式。
对象实例继承了原型对象的属性,而原型对象也是一个对象,它也有自己的原型对象并继承其属性,以此类推。这就是原型对象链。
所有对象(包括自义定的)都自动继承自 Object
,除非你另有指定。更确切地说,所有对象都继承自 Object.prototype
。任何以对象字面量形式定义的对象,其 [[Prototype]]
的值都被设为 Object.prototype
,这意味着它继承 Object.prototype
的属性。
5.1.1 继承自 Object.prototype 的方法
Object.prototype 一般有以下几个方法:
- hasOwnProperty() 检测是否存在一个给定名字的自有属性
- propertyIsemumerable() 检查一个自有属性是否可枚举
- isPrototypeOf 检查一个对象是否是另一个对象的原型对象
- valueOf() 返回一个对象的值表达
- toString() 返回一个对象的字符串表达
这 5 种方法经由继承出现在所有对象中。
因为所有对象都默认继承自 Object.prototype
,所以改变它就会影响所有的对象。所以不建议。
5.2 继承
对象继承是最简单的继承类型。你唯需要做的是指定哪个对象是新对象的 [[Prototype]]
。对象字面量形式会隐式指定 Object.prototype
为其 [[Protoype]]
。当然我们可以用 ES5 的 Object.create()
方法显式指定。该方法接受两个参数,第一个是新对象的 [[Prototype]]
所指向的对象。第二个参数是可选的一个属性描述对象,其格式与 Object.definePrototies()
一样。
var obj = { name: "Ljc" }; // 等同于 var obj = Object.create(Object.prototype, { name: { value: "Ljc", configurable: true, enumberable: true, writable: true } }); 复制代码
下面是继承其它对象:
var person = { name: "Jack", sayName: function(){ console.log(this.name); } } var student = Object.create(person, { name:{ value: "Ljc" }, grade: { value: "fourth year of university", enumerable: true, configurable: true, writable: true } }); person.sayName(); // "Jack" student.sayName(); // "Ljc" console.log(person.hasOwnProperty("sayName")); // true console.log(person.isPrototypeOf(student)); // true console.log(student.hasOwnProperty("sayName")); // false console.log("sayName" in student); // true 复制代码
当访问一个对象属性时,JavaScript 引擎会执行一个搜索过程。如果在对象实例存在该自有属性,则返回,否则,根据其私有属性 [[Protoype]]
所指向的原型对象进行搜索,找到返回,否则继承上述操作,知道继承链末端。末端通常是 Object.prototype
,其 [[Prototype]]
是 null
。
当然,也可以用 Object.create()
常见一个 [[Prototype]]
为 null
的对象。
var obj = Object.create(null); console.log("toString" in obj); // false 复制代码
该对象是一个没有原型对象链的对象,即是一个没有预定义属性的白板。
5.3 构造函数继承
JavaScript 中的对象继承也是构造函数继承的基础。
第四章提到,几乎所有函数都有 prototype
属性,它可被修改或替换。该 prototype
属性被自动设置为一个新的继承自 Object.prototype
的泛用对象,该对象(原型对象)有一个自有属性 constructor
。
实际上,JavaScript 引擎为你做了下面的事情。
// 你写成这样 function YourConstructor(){ // initialization } // JavaScript引擎在背后为你做了这些处理 YourConstructor.prototype = Object.create(Object.prototype, { constructor: { configurable: true, enumerable: true, value: YourConstructor, writable: true } }) 复制代码
你不需要做额外的工作,这段代码帮你把构造函数的 prototype
属性设置为一个继承自 Object.prototype
的对象。这意味着 YourConstructor
创建出来的任何对象都继承自 Object.prototype
。
由于 prototype 可写,你可以通过改变它来改变原型对象链。
instanceof 运算符可以用来判断某个构造函数的 prototype 属性是否存在另外一个要检测对象的原型链上。
function Rectangle(length, width){ this.length = length; this.width = width } Rectangle.prototype.getArea = function(){ return this.length * this.width } Rectangle.prototype.toString = function(){ return "[Rectangle " + this.length + "x" + this.width + "]"; } // inherits from Rectangle function Square(size){ this.length = size; this.width = size; } Square.prototype = new Rectangle(); // 尽管是 Square.prototype是指向了 Rectangle的对象实例,即Square的实例对象也能访问该实例的属性(如果你提前声明了该对象,且给该对象新增属性)。 // Square.prototype = Rectangle.prototype; // 这种实现没有上面这种好,因为Square.prototype 指向了 Rectangle.prototype,导致修改Square.prototype时,实际就是修改Rectangle.prototype。 console.log(Square.prototype.constructor); // 输出 Rectangle 构造函数 Square.prototype.constructor = Square; // 重置回 Square 构造函数 console.log(Square.prototype.constructor); // 输出 Square 构造函数 Square.prototype.toString = function(){ return "[Square " + this.length + "x" + this.width + "]"; } var rect = new Rectangle(5, 10); var square = new Square(6); console.log(rect.getArea()); // 50 console.log(square.getArea()); // 36 console.log(rect.toString()); // "[Rectangle 5 * 10]", 但如果是Square.prototype = Rectangle.prototype,则这里会"[Square 5 * 10]" console.log(square.toString()); // "[Square 6 * 6]" console.log(square instanceof Square); // true console.log(square instanceof Rectangle); // true console.log(square instanceof Object); // true 复制代码
Square.prototype
并不真的需要被改成为一个 Rectangle
对象。事实上,是 Square.prototype
需要指向 Rectangle.prototype
使得继承得以实现。这意味着可以用 Object.create()
简化例子。
// inherits from Rectangle function Square(size){ this.length = size; this.width = size; } Square.prototype= Object.create(Rectangle.prototype, { constructor: { configurable: true, enumerable: true, value: Square, writable: true } }) 复制代码
在对原型对象添加属性前要确保你已经改成了原型对象,否则在改写时会丢失之前添加的方法(因为继承是将被继承对象赋值给需要继承的原型对象,相当于重写了需要继承的原型对象)。
5.4 构造函数窃取
由于 JavaScript 中的继承是通过原型对象链来实现的,因此不需要调用对象的父类的构造函数。如果确实需要在子类构造函数中调用父类构造函数,那就可以在子类的构造函数中利用 call
、 apply
方法调用父类的构造函数。
// 在上面的代码基础上作出修改 // inherits from Rectangle function Square(size){ Rectangle.call(this, size, size); // optional: add new properties or override existing ones here } 复制代码
一般来说,需要修改 prototype
来继承方法并用构造函数窃取来设置属性,由于这种做法模仿了那些基于类的语言的类继承,所以这通常被称为伪类继承。
5.5 访问父类方法
其实也是通过指定 call
或 apply
的子对象调用父类方法。
以上所述就是小编给大家介绍的《《JavaScript面向对象精要》之五:继承》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 内存管理设计精要
- 网络爬虫精要
- 调度系统设计精要
- 《JavaScript面向对象精要》之二:函数
- 《JavaScript面向对象精要》之六:对象模式
- 《JavaScript面向对象精要》之三:理解对象
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Hello World
Hannah Fry / W. W. Norton Company / 2018-9 / GBP 17.99
A look inside the algorithms that are shaping our lives and the dilemmas they bring with them. If you were accused of a crime, who would you rather decide your sentence—a mathematically consistent ......一起来看看 《Hello World》 这本书的介绍吧!