javascript 面向对象精要 第五章 继承

 第五章 继承

引言: 理解面向对象编程 第一步 创建对象  第二步 理解继承

5.1 原型对象链和Object.prototype

Javascript内建的继承方法被称为原型对象链,又可称为原型对象继承。

原型对象的属性可经由对象实例访问,这就是继承的一种形式。原型对象也是一个对象,它也有自己的原型对象并继承其属性,这就是原型对象链

所有的对象包括你自己定义的对象,都自动继承自Object,除非你另有指定。任何以对象字面形式定义的对象,其[[prototype]]属性的值都被设为Object.prototype.如下面例子所示:

var book={
     title:"tonghua"
};
var prototype=Object.getPrototypeOf(book);
console.log(prototype===Object.prototype);

运行结果如下:

5.1.1 继承自Object.prototype的属性

hasOwnProperty()检查是否存在一个给定名字的自有属性
propertyIsEnumerable()

检查一个自有属性是否可枚举

isPrototypeOf()检查一个对象是否是另一个对象的原型对象
valueOf()

返回一个对象的表达式值

toString()返回一个对象的字符串表达式

5.1.2 修改Object.prototype

修改Object.prototype会影响所有的对象

Object.prototype.add=function(value){
     return this+value;
}
var book={
title:"tonghua"
};
console.log(book.add(5));
console.log("title".add("end"));
console.log(docunment.add(true));
console.log(window.add(5));
运行结果如下:

添加Object.prototype.add()会导致所有的对象都有了一个add()方法。另外,Object.prototype.add()是一个可枚举属性,意味着它会出现在for in循环中,一个空对象仍然会输出“add"作为其属性,因为它存在与原型对象中并且可枚举。

var empty={};
for (var property in empty){
console.log(property);
}

因为这个原因,推荐在for in 循环中始终使用hasOwnProperty(),如下:

var empty={};
for (var property in empty){
  if(empty.hasOwnProperty(property)){

      console.log(property);
}
}

这个方法虽然可以过滤那些不想要的原型对象的属性,但同时也限制了for in 循环。所以,最好不要修改原型属性/

5.2 对象继承

最简单的一种继承类型。唯一要做的就是指定哪个对象是新对象的[[prototype]].

隐式:字面形式

显式:Object.create()    接收2个参数 第一个参数:需要被设置为新对象的[[prototype]].的对象 第二个参数:可选 属性描述对象

var person1={
   name:"Nicholas",
   sayName: function(){
     console.log(this.name);
   }
};
var person2=Object.create(person1,{
   name:{
    configurable:true,
    enumerable:true,
    value:"Greg",
    writable:true
}
});
   person1.sayName();
   person2.sayName();

console.log(person1.isPrototypeOf(person2));
console.log(person1.hasOwnProperty("sayName"));
console.log(person2.hasOwnProperty("sayName"));

运行结果如下:


分析:对象person2继承了person1,也继承了name He sayName().然而person2在通过Object.create()创建时还定义 一个自有属性name,该自有属性隐藏并代替了原型对象的同名属性。

但是sayName()仍然只存在于person1并被person2继承。


当访问一个对象的属性时,javascript引擎会执行一个搜索过程。如果在该对象实例上发现该属性,,该属性值就会被利用,如果没有,则搜索[[prototype]],如果还是没有发现,则继续搜索该原型对象的[[prototype]],直到继承链末端。末端一般是一个object.prototype,其[[prototype]]被设置为null。

5.3 构造函数继承

几乎所有函数都有 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 属性可写,你可以通过改写他来改变原型对象链。

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的一个对象实例。

 rect作为Rectangle的实例对象被创建。square作为Square的实例对象被创建.两个对象都有getArea()方法,因为那继承自Rectangle.prototype.instanceOf操作符,认为变量square同时是Square\Rectangle、Object的对象实例。

唯一相关的部分就是,Square.prototype需要指向Rectangle.prototype,使得继承得以实现。


5.4 构造函数窃取

如果需要在子类构造函数中调用父类的构造函数。用call()或者apply().

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+"]";
};

function Square(size){
     Rectangle.call(this,size,size);
}
Square.prototype=Object.create(Rectangle.prototype,{
   constructor:{
    configurable:true,
    enumerable:true,
    value:Square,
    writable:true
        }
});
 Square.prototype.toString=function(){
   return "[Square "+this.length+"x"+this.width+"]";
 };
 var Square=new Square(6);
 console.log(Square.length);
console.log(Square.width);
console.log(Square.getArea());

运行结果如下:


分析:Square 构造函数调用了Rectangle构造函数,并传入了this 和size 两次,一次为length一次为width ,你可以在调用完父类的构造函数后继续添加新的属性或者覆盖已有的属性。Square类型有自己的toString()方法隐藏了其原型对象的toString()方法。

5.5 访问父类方法

上例中,Square类型有自己的toString()方法隐藏了其原型对象的toString()方法。如果还是想访问父类的方法该怎么办?

通过call()和apply()方法调用父类的原型对象的方法时传入一个子类的对象

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+"]";
};

function Square(size){
     Rectangle.call(this,size,size);
}
Square.prototype=Object.create(Rectangle.prototype,{
   constructor:{
    configurable:true,
    enumerable:true,
    value:Square,
    writable:true
        }
});
 Square.prototype.toString=function(){
 var text= Rectangle.prototype.toString.call(this)
  return text.replace("Rectangle","Square");
 };
 var Square=new Square(6);
 console.log(Square.length);
console.log(Square.width);
console.log(Square.getArea());
console.log(Square.toString());

运行结果如下:




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值