js中的继承2--原型继承

一. 原型与构造函数

  Js所有的函数都有一个prototype属性,这个属性引用了一个对象,即原型对象,也简称原型。这个函数包括构造函数和普通函数,我们讲的更多是构造函数的原型,但是也不能否定普通函数也有原型。譬如普通函数:

function F(){
  ;
}
alert(F.prototype instanceof Object) //true

  构造函数,也即构造对象。首先了解下通过构造函数实例化对象的过程。

function A(x){
  this.x=x;
}
var obj=new A(1);

实例化obj对象有三步:

  1. 创建obj对象:obj=new Object();

  2. 将obj的内部__proto__指向构造他的函数A的prototype,同时,obj.constructor===A.prototype.constructor(这个是永远成立的,即使A.prototype不再指向原来的A原型,也就是说:类的实例对象的constructor属性永远指向"构造函数"的prototype.constructor),从而使得obj.constructor.prototype指向A.prototype(obj.constructor.prototype===A.prototype,当A.prototype改变时则不成立,下文有遇到)。obj.constructor.prototype与的内部_proto_是两码事,实例化对象时用的是_proto_,obj是没有prototype属性的,但是有内部的__proto__,通过__proto__来取得原型链上的原型属性和原型方法,FireFox公开了__proto__,可以在FireFox中alert(obj.__proto__);

  3. 将obj作为this去调用构造函数A,从而设置成员(即对象属性和对象方法)并初始化。

  当这3步完成,这个obj对象就与构造函数A再无联系,这个时候即使构造函数A再加任何成员,都不再影响已经实例化的obj对象了。此时,obj对象具有了x属性,同时具有了构造函数A的原型对象的所有成员,当然,此时该原型对象是没有成员的。

  原型对象初始是空的,也就是没有一个成员(即原型属性和原型方法)。可以通过如下方法验证原型对象具有多少成员。

var num=0;
for(o in A.prototype) {
  alert(o);//alert出原型属性名字
  num++;
}
alert("member: " + num);//alert出原型所有成员个数。

  但是,一旦定义了原型属性或原型方法,则所有通过该构造函数实例化出来的所有对象,都继承了这些原型属性和原型方法,这是通过内部的_proto_链来实现的。

  譬如

  A.prototype.say=function(){alert("Hi")};

  那所有的A的对象都具有了say方法,这个原型对象的say方法是唯一的副本给大家共享的,而不是每一个对象都有关于say方法的一个副本。

二. 原型与继承

  首先,看个简单的继承实现。

1 function A(x){
2   this.x=x;
3 }

4  function B(x,y){
5   this.tmpObj=A;
6   this.tmpObj(x);
7   delete this.tmpObj;
8   this.y=y;
9 }

  第5、6、7行:创建临时属性tmpObj引用构造函数A,然后在B内部执行,执行完后删除。当在B内部执行了this.x=x后(这里的this是B的对象),B当然就拥有了x属性,当然B的x属性和A的x属性两者是独立,所以并不能算严格的继承。第5、6、7行有更简单的实现,就是通过call(apply)方法:A.call(this,x);

这两种方法都有将this传递到A的执行里,this指向的是B的对象,这就是为什么不直接A(x)的原因。这种继承方式即是类继承(js没有类,这里只是指构造函数),虽然继承了A构造对象的所有属性方法,但是不能继承A的原型对象的成员。而要实现这个目的,就是在此基础上再添加原型继承。

  通过下面的例子,就能很深入地了解原型,以及原型参与实现的完美继承。(本文核心在此^_^)

1 function A(x){
2   this.x = x;
3 }
4 A.prototype.a = "a";
5 function B(x,y){
6   this.y = y;
7   A.call(this,x);
8 }
9 B.prototype.b1 = function(){
10   alert("b1");
11 }
12 B.prototype = new A();
13 B.prototype.b2 = function(){
14   alert("b2");
15 }
16 B.prototype.constructor = B;
17 var obj = new B(1,3);

  这个例子讲的就是B继承A。第7行类继承:A.call(this.x);上面已讲过。实现原型继承的是第12行:B.prototype = new A();

  就是说把B的原型指向了A的1个实例对象,这个实例对象具有x属性,为undefined,还具有a属性,值为"a"。所以B原型也具有了这2个属性(或者说,B和A建立了原型链,B是A的下级)。而因为方才的类继承,B的实例对象也具有了x属性,也就是说obj对象有2个同名的x属性,此时原型属性x要让位于实例对象属性x,所以obj.x是1,而非undefined。第13行又定义了原型方法b2,所以B原型也具有了b2。虽然第9~11行设置了原型方法b1,但是你会发现第12行执行后,B原型不再具有b1方法,也就是obj.b1是undefined。因为第12行使得B原型指向改变,原来具有b1的原型对象被抛弃,自然就没有b1了。

  第12行执行完后,B原型(B.prototype)指向了A的实例对象,而A的实例对象的构造器是构造函数A,所以B.prototype.constructor就是构造对象A了(换句话说,A构造了B的原型)。

alert(B.prototype.constructor)出来后就是"function A(x){...}" 。同样地,obj.constructor也是A构造对象,alert(obj.constructor)出来后就是"function A(x){...}" ,也就是说B.prototype.constructor===obj.constructor(true),但是B.prototype===obj.constructor.prototype(false),因为前者是B的原型,具有成员:x,a,b2,后者是A的原型,具有成员:a。如何修正这个问题呢,就在第16行,将B原型的构造器重新指向了B构造函数,那么B.prototype===obj.constructor.prototype(true),都具有成员:x,a,b2。

  如果没有第16行,那是不是obj = new B(1,3)会去调用A构造函数实例化呢?答案是否定的,你会发现obj.y=3,所以仍然是调用的B构造函数实例化的。虽然obj.constructor===A(true),但是对于new B()的行为来说,执行了上面所说的通过构造函数创建实例对象的3个步骤,第一步,创建空对象;第二步,obj.__proto__ === B.prototype,B.prototype是具有x,a,b2成员的,obj.constructor指向了B.prototype.constructor,即构造函数A;第三步,调用的构造函数B去设置和初始化成员,具有了属性x,y。虽然不加16行不影响obj的属性,但如上一段说,却影响obj.constructor和obj.constructor.prototype。所以在使用了原型继承后,要进行修正的操作。

  关于第12、16行,总言之,第12行使得B原型继承了A的原型对象的所有成员,但是也使得B的实例对象的构造器的原型指向了A原型,所以要通过第16行修正这个缺陷。

http://www.cnblogs.com/ljchow/archive/2010/06/08/1753526.html

时间: 2024-11-10 05:11:12

js中的继承2--原型继承的相关文章

JS中“类”继承和原型继承

类继承: 先用函数构造器创建了一个“类”Student,然后在Student原型上定义了一个方法sayHello,然后创建了一个"类“PrimaryStudent,用apply()初始化PrimaryStudent. 然后让PrimaryStudent的原型等于Student创建的对象,并把PrimaryStudent原型上的构造器指向当前”类“,即PrimaryStudent,注意红色加粗部分. 1 function Student(name){ 2 this.name = name; 3 }

JS中通过call方法实现继承

讲解都写在注释里面了,有不对的地方请拍砖,谢谢! <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>JS通过call方法实现继承</title> </head> <body> <script type="text/javascript"> /* js中的继承有多种实现方式,今天我们讨论下通过call方法实现的继承. 此

javascript继承,原型继承,借用构造函数继承,混合继承

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getS

JS继承之原型继承

许多OO语言都支持两种继承方式:接口继承和实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法.如前所述,由于函数没有签名,在ECMAScript中无法实现接口继承.ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的. --摘自<JavaScript高级程序设计> 原型继承 原型链是实现原型继承的主要方法,基本思想就是利用原型让一个引用类型继承另一个引用类型的属性和方法. 实现原型链的基本模式: 1 2 3 4 5 6 7 8 9 10 11 12 13 fun

JS面向对象组件 -- 继承的其他方式(类式继承、原型继承)

继承的其他形式: •类式继承:利用构造函数(类)继承的方式 •原型继承:借助原型来实现对象继承对象 类 : JS是没有类的概念的 , 把JS中的构造函数看做的类 要做属性和方法继承的时候,要分开继承. function Aaa(){ //父类 this.name = "小明"; } Aaa.prototype.showName = function(){ alert( this.name ); }; function Bbb(){ //子类 } Bbb.prototype = new

js组合继承(原型继承+借用构造函数继承)

组合继承就是将原型链和构造函数结合起来发挥二者优势的一种模式.继承:是类型与类型之间的继承继承目的:把子类型相同成员提取到父类型,实现代码重用 大体思路是:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承,很好地解决了原型链继承的缺点,是较为常用的一种继承方式.//一:借用构造函数(借用父类型extend) 缺点:无法继承父类型的方法//父类型 function Person(name,age,sex){ this.anme=name;this.age=age;th

js中的对象、原型链机制、构造函数

一.在js中创建对象的方式 //一.字面量或直接量创建对象 var obj1 = { name:"zs", age:12 }; //二.通过new来创建对象 var obj2 = new Object(); obj2.name = "zs"; obj2.age = 16; //三.通过工厂函数来创建 function creatObj() { return {}; } //四.通过new 构造函数来创建 function Obj() { } //测试 var obj

js之类式继承,原型继承

类式继承 //extend function function extend(subClass,superClass){ var F=function(){}; F.prototype=superClass.prototype; subClass.prototype=new F(); subClass.prototype.constructor=subClass; subClass.superclass=superClass.prototype; if(superClass.prototype.

浅谈JS中的构造函数、原型对象(prototype)、实例中的属性/方法之间的关系

原文链接:https://segmentfault.com/a/1190000016951069 构造函数:函数中的一种,通过关键字new可以创建其实例.为了便于区分,通常首字母大写:原型对象:一种特殊的对象,构造函数创建时自动生成:与构造函数形成一一对应,如同人和影子般的关系:实例:通过构造函数实例出来的对象: 在定义构造函数时,在其内部(“{“和”}”)进行定义属性和方法.当我们通过关键字new,对构造函数进行实例化的时候.实例会对构造函数的这些属性进行拷贝出一份副本,然后将其归属为当前实例

js中的对象 函数 原型

// 关于 Function Object 和 proto prototype // 1.每一个对象实例都有一个 proto 属性,这个属性就是指向 对象构造函数的原型. let b = new Function(); console.log(b.__proto__ === Function.prototype); // true console.log(Function.prototype); // [Function] console.log(Function.prototype.proto