JS Pro-深入面向对象的程序设计之继承的详解_javascript技巧

原型链(prototype chaining):

利用原型来继承属性和方法。回顾一下构造函数(constructor),原型对象(prototype)和实例(instance)的关系。每一个构造函数都有一个prototype属性,该属性指向一个prototype对象;prototype对象也有constructor属性,指向该函数;而实例也有一个内部指针(__proto__)指向这个prototype对象。如果这个prototype对象是另外一个对象的实例会是怎样的呢?这样该prototype对象就包含一个指向另一个类型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。

JS的继承很简单,就是把子类的prototype设为父类的一个(实例化)对象

复制代码 代码如下:

function SuperType(){
    this.property = true;
}

SuperType.prototype.getSuperValue = function(){
    return this.property;
};

function SubType(){
    this.subproperty = false;
}

//inherit from SuperType
SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function (){
    return this.subproperty;
};

var instance = new SubType();
alert(instance.getSuperValue());   //true

最终的结果:instance的__proto__指向SubType.prototype对象,而SubType.prototype对象的__proto__属性又指向SuperType.prototype对象。getSuperValue()是一个方法,所以仍然存在于原型中,而property是一个实例属性,所以现在存在于SubType.prototype这个实例中。  instance.constructor现在指向的是SuperType,这是由于SubType.prototype指向SuperType.prototype,而SuperType.prototype的constructor属性指向SuperType函数,所以instance.constructor指向SuperType。

默认情况下,所有引用类型都继承Object。这是因为所有函数的原型对象,默认都是Object的一个实例,所以内部prototype(__proto__)指向Object.Prototype。

原型和实例的关系:可以使用2种方法来确定原型与实例之间的关系。
- instancef操作符:使用该操作符来测试实例与原型链中出现过的构造函数,都会返回true

复制代码 代码如下:

alert(instance instanceof Object); //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType); //true

- isPrototypeOf()方法:只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。

复制代码 代码如下:

alert(Object.prototype.isPrototypeOf(instance)); //true
alert(SuperType.prototype.isPrototypeOf(instance)); //true
alert(SubType.prototype.isPrototypeOf(instance)); //true

给子类添加方法的注意点:我们有的时候会给子类添加方法,或者是重写父类的某些方法。这个时候就要注意,这些方法必须在继承后再定义。以下的例子里,SubType在继承SuperType后,我们给它添加了新的方法getSubValue(),而且重写了getSuperValue()方法。对于后者,只有SubType的实例才会使用重写的方法,SuperType的实例还是会使用原有的getSuperValue()方法。

复制代码 代码如下:

function SuperType(){
  this.property = true;
}
SuperType.prototype.getSuperValue = function(){
  return this.property;
};
function SubType(){
  t his.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
//new method
SubType.prototype.getSubValue = function (){
  return this.subproperty;
};
//override existing method
SubType.prototype.getSuperValue = function (){
  return false;
};
var instance = new SubType();
alert(instance.getSuperValue()); //false

另外一个需要注意的是,通过原型链实现继承时,不能使用对象字面量创建原型方法,因为这样会重写原型链。如下面的代码,在SubType继承SuperType以后,使用对象字面量给原型添加方法,但这样做,会重写SubType原型,重写后的SubType.prototype包含的是一个Object的实例,从而也切断了与SuperType的关系。

复制代码 代码如下:

function SuperType(){
  this.property = true;
}
SuperType.prototype.getSuperValue = function(){
  return this.property;
};
function SubType(){
  this.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
//try to add new methods - this nullifies the previous line
SubType.prototype = {
  getSubValue : function (){
    return this.subproperty;
  },
  someOtherMethod : function (){
    return false;
  }
};
var instance = new SubType();
alert(instance.getSuperValue()); //error!

原型链的问题:和原型一样,当使用引用类型值的时候,原型链就会出问题了。回顾一下之前的内容,包含一个引用类型值的原型属性会被所有实例共享,这就是为什么我们要把引用类型值在构造函数中定义,而不是在原型中定义。在通过原型链实现继承时,原型实际上会变成另一个类型的实例,于是,原先的实例属性也顺利成章的变成现在的原型属性了。

复制代码 代码如下:

function SuperType(){
  this.colors = [“red”, “blue”, “green”];
}
function SubType(){
}
//inherit from SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push(“black”);
alert(instance1.colors); //”red,blue,green,black”
var instance2 = new SubType();
alert(instance2.colors); //”red,blue,green,black”

在SuperType构造函数中,我们定义了一个colors数组,每一个SuperType实例都会拥有它自己的这个colors数组。但是当SubType使用原型链继承SuperType以后,SubType.prototype变成SuperType的一个实例,因此它拥有自己的colors属性,也就是说SubType.prototype.colors属性。所以,当创建SubType实例的时候,所有实例都共享这一属性了。如上面的代码所示。

第二个问题就是:在创建子类的实例时,不能向超类的构造函数中传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类的构造函数传递参数。由于这些问题,我们不会单独使用原型链。

--------------------------------------------------------------------------------

借用构造函数(Contructor stealing):
为了解决上述问题,开发人员发明了一种叫做借用构造函数的技术。这种技术的思路就是:在子类型构造函数的内部调用超类型构造函数。(函数,只不过是在特定环境中执行代码的对象?)我们可以通过使用apply()或call()方法在新创建的对象上执行构造函数。

复制代码 代码如下:

function SuperType(){
  this.colors = [“red”, “blue”, “green”];
}
function SubType(){
  //inherit from SuperType
  SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push(“black”);
alert(instance1.colors); //”red,blue,green,black”
var instance2 = new SubType();
alert(instance2.colors); //”red,blue,green”

我们在SubType里使用call()方法调用SuperType的构造函数,实际上就是在新的SubType对象上执行SuperType()函数中定义的所有对象初始化代码。结果就是每个SubType实例都具有自己的colors属性的副本。

传递参数:使用借用构造函数方法的一个很大的好处在于就是,我们可以从子类的构造函数传递参数到父类的构造函数中。

复制代码 代码如下:

function SuperType(name){
  this.name = name;
}
function SubType(){
  //inherit from SuperType passing in an argument
  SuperType.call(this, “Nicholas”);
  //instance property
  this.age = 29;
}
var instance = new SubType();
alert(instance.name); //”Nicholas”;
alert(instance.age); //29

新的SuperType构造函数新增了一个参数name,我们在call SuperType的同时,往SuperType传递参数"Nicholas"。为了不让超类型的构造函数重写子类型的属性,可以在调用超类型构造函数后再定义子类的属性。

借用构造函数的问题:方法都在构造函数中定义,无法复用。而且在超类型的原型中定义的方法,对子类型而言是不可见的。结果所有类型都只能使用构造函数模式。

--------------------------------------------------------------------------------

组合继承:
结合原型链及借用构造函数各自的优点的一种继承模式。使用原型链继承原型属性及方法,使用借用构造函数来继承实例属性。如下面例子,我们使用call()方法调用SuperType的构造函数(每个SubType实例都拥有自己的name和colors属性,以及SubType的age属性);然后再把SuperType实例赋值给SubType的原型,使其继承SuperType的sayName()方法(每个实例都共用这个方法)。

复制代码 代码如下:

function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function(){
    alert(this.name);
};

function SubType(name, age){
    //inherit properties
    SuperType.call(this, name);
    this.age = age;
}
//inherit methods
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
    alert(this.age);
};

var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27

原型式继承(Prototypal Inheritance):

复制代码 代码如下:

function object(o){
  function F(){}
  F.prototype = o;
  return new F();
}
--------------------------------------------------------------------------------

寄生式继承(Parasitic Inheritance):

缺点同构造函数

时间: 2024-10-10 23:06:28

JS Pro-深入面向对象的程序设计之继承的详解_javascript技巧的相关文章

在js里怎么实现Xcode里的callFuncN方法(详解)_javascript技巧

本人使用的WebStorm编辑器,里面没有callFuncN, 不记得Lua是否支持callFuncN,如果不支持相信应该能用同样的方法做到. 废话不多说,贴代码: loadDown : function () { var dis = this.left_move.getPositionY() - this.left.getPositionY(); // 得到一个距离 var act1 = new cc.moveBy(0.5,cc.p(0,-dis)); var act2 = cc.callFu

JS定时器使用,定时定点,固定时刻,循环执行详解_javascript技巧

本文概述:本文主要介绍通过JS实现定时定点执行,在某一个固定时刻执行某个函数的方法.比如说在下一个整点执行,在每一个整点执行,每隔10分钟定时执行的方法. JavaScript中有两个定时器方法:setTimeout()和setInterval(). 这两个方法都可以用来实现在一个固定时间段之后去执行JavaScript.实际上,setTimeout和setInterval的语法相同.它们都有两个参数,一个是将要执行的代码字符串,或者函数名,还有一个是以毫秒为单位的时间间隔,当过了那个时间段之后

js判断是否为空和typeof的用法(详解)_javascript技巧

(1)typeof用法 typeof的运算数未定义,返回的就是 "undefined". 运算数为数字 typeof(x) = "number" 字符串 typeof(x) = "string" 布尔值 typeof(x) = "boolean" 对象,数组和null typeof(x) = "object" 函数 typeof(x) = "function" (2)js判断是否为空 v

JavaScript中的继承方式详解_javascript技巧

js继承的概念 js里常用的如下两种继承方式: 原型链继承(对象间的继承) 类式继承(构造函数间的继承) 由于js不像java那样是真正面向对象的语言,js是基于对象的,它没有类的概念.所以,要想实现继承,可以用js的原型prototype机制或者用apply和call方法去实现 在面向对象的语言中,我们使用类来创建一个自定义对象.然而js中所有事物都是对象,那么用什么办法来创建自定义对象呢?这就需要用到js的原型: 我们可以简单的把prototype看做是一个模版,新创建的自定义对象都是这个模

JS实现焦点图轮播效果的方法详解_javascript技巧

本文实例讲述了JS实现焦点图轮播效果的方法.分享给大家供大家参考,具体如下: 效果图如下: 一.所用到的知识点 1.DOM操作 2.定时器 3.事件运用 4.Js动画 5.函数递归 6.无限滚动大法 二.结构和样式 <div id="banner" class="banner"> <ul id="list-banner" class="list-banner fn-clear" style="lef

js console.log打印对像与数组用法详解_javascript技巧

本文实例讲述了js console.log打印对像与数组用法.分享给大家供大家参考,具体如下: console.log是什么东西,其实就是一个打印js数组和对像的函数而已,就像是php的print_r,var_dump.console.log这个函数本身没什么好说的,这篇博客告诉大家怎么去用这个函数.在说这个函数之前,我想大家用的最多查看js输出,是alert吧,但是alert,只能弹string或者是int的 一.测试文件test.html <html xmlns="http://www

JS小游戏之仙剑翻牌源码详解_javascript技巧

本文实例讲述了JS小游戏的仙剑翻牌源码,是一款非常优秀的游戏源码.分享给大家供大家参考.具体如下: 一.游戏介绍: 这是一个翻牌配对游戏,共十关. 1.游戏随机从42张牌中抽取9张进行游戏,每组为2张相同的牌,共18张牌. 2.连续翻到两张相同的为胜利,当9组全部翻到则过关.如不是翻到连续两张相同的,则需要重新翻. 3.游戏共有10关,在规定时间内通过为挑战成功. 4.如果某关在规定时间内没有通过,则会从当前关继续游戏. 5.游戏中的卡牌图片与音乐均为大宇公司所有. 6.需要支持html5的浏览

js/jquery解析json和数组格式的方法详解_javascript技巧

在解析之前,我们必须弄清楚几个概念:数组,关联数组以及json之间有哪些区别和联系点? 一.概念介绍1.数组 语法: ECMAScript v3规定了数组直接量的语法,JavaScript 1.2和JScript 3.0实现了它.可以把-个用逗号分隔的表达式列表放在方括号中,创建并初始化-个数组.这些表达式的值将成为数组元素.例如: var a = [1, true, 'abc']; 具体操作查看API. ps:必须方括号隔开. 2.关联数组 1.语法:var myhash= {"key1″:&

JS小游戏之极速快跑源码详解_javascript技巧

本文实例讲述了JS小游戏的极速快跑源码,分享给大家供大家参考.具体如下: 游戏运行后如下图所示: Javascript部分代码如下: /** 极速快跑 * Author: fdipzone * Date: 2012-07-15 * Ver: 1.0 */ var gameimg = ['images/start.png', 'images/start_over.png', 'images/go.png', 'images/go_over.png', 'images/running.gif', '