对Javascript的原型,原型链和继承的个人理解

继承是OO语言中一个最为人津津乐道的概念,也是初接触Javascript的初学者难理解的概念=。=继承主要分为两种:一种是接口继承,另一种是实现继承。而在ECMAScript中只支持实现继承,所以我们今天来讨论讨论实现继承。实现继承就是继承实际的方法,主要依靠原型链来实现。讲到这里我们就需要讨论讨论什么是原型链。

1、什么是原型

  要理解原型链我们首先要知道什么是原型。我们知道每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,这个对象包含所有实例共享的属性和方法。所以我个人觉得可以这么简单的理解:原型就是prototype属性。

  同时prototype属性有自己的prototype对象,而pototype对象肯定也有自己的constuct属性,construct属性有自己的constuctor对象,神奇的事情要发生了,这最后一个constructor对象就是我们构造出来的function函数本身!

2、关于prototype和_proto_

  (1).每个对象都具有一个名为__proto__的属性;

  (2).每个构造函数(构造函数标准为大写开头,如Function(),Object()等等JS中自带的构造函数,以及自己创建的)都具有一个名为prototype的方法(注意:既然是方法,那么就是一个对象(JS中函数同样是对象),所以prototype同样带有__proto__属性);

  (3).每个对象的__proto__属性指向自身构造函数的prototype;

  所以在大多数情况下我们可以这么认为:_proto_===constructor.prototype(某些情况除外,比如:通过Object.create()创建的对象不适用此等式)

3、原型链

  讲到这,到底什么是原型链呢?其实已经很明了了。由于每个对象都有_proto_属性,而在Javascript中万物皆对象,所以最终会形成一条由_proto_连起来的链条,递归访问最终会到头,最终的值为NULL。那么这条链条就是原型链。

  当Javascript引擎查找对象的属性时,先查找对象本身是否存在该属性,如果不存在,那么会在原型链上逐级查找。(但不会查找自身的prototype)

4、继承

  一、原型链式继承

    实现原型链继承有一种基础模式,代码如下:

function SuperType(){
    this.property=ture;
}SuperType.prototype.getSuperValue=function(){  return this.prototype;};function SubType(){  this.subproperty=false;}SubType.prototype=new SubType();//继承了SuperTypeSubType.prototype.getSubValue=function(){  return this.subproperty;};var instance=new SubType();alert(instance.getSuperValue());//true

 基础原型链继承虽然简单但是它有两个问题:1、在通过原型链继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性了。2、在创建子类型的实例时,不能向超类型的构造函数中传递函数。

  二、借用构造函数继承

  在子类型构造函数的内部调用超类构造函数,通过使用call()和apply()方法可以在新创建的对象上执行构造函数。

function SuperType() {
this.colors = ["red","blue","green"];
}
function SubType() {
SuperType.call(this);//继承了SuperType
}
var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors);//"red","blue","green","black"
var instance2 = new SubType();
console.log(instance2.colors);//"red","blue","green"

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

  三、组合继承

function Super(){
    // 只在此处声明基本属性和引用属性
    this.val = 1;
    this.arr = [1];
}
//  在此处声明函数
Super.prototype.fun1 = function(){};
Super.prototype.fun2 = function(){};
//Super.prototype.fun3...
function Sub(){
    Super.call(this);   // 核心
    // ...
}
Sub.prototype = new Super();    // 核心

var sub1 = new Sub(1);
var sub2 = new Sub(2);
alert(sub1.fun === sub2.fun);   // true

  为了解决借用构造函数继承的问题,组合继承应运而生。

  组合继承把实例函数都放在原型对象上,以实现函数复用。同时还要保留借用构造函数方式的优点,通过Super.call(this);继承父类的基本属性和引用属性并保留能传参的优点;通过Sub.prototype = new Super();继承父类函数,实现函数复用。

  组合继承避免了原型链继承和借用构造函数继承的缺点,融合了他们的优点:不存在引用属性共享问题,可传参,函数可复用。因此成为Javascript中最常见的继承模式。

  缺点:组合集成的最大问题是在无论什么情况下都会调用两次超类型构造函数。

function SuperType(name) {
    this.name=name;
    this.colors = ["red","blue","green"];
}
SuperType.prototype.sayname=function(){
    alert(this.name);
};
function SubType(name,age) {
    SuperType.call(this,name);//第一次调用SuperType()
    this.age=age
}
SubType.prototype=new SuperType();//第二次调用SuperType()
SubType.prototype.constructor=function(){
    alert(this.age);
};

  

  四、寄生式组合继承

    既然组合继承也有不足之处,那当然就需要弥补,于是就出现了寄生式组合继承。

寄生式组合继承的基本模式如下:

function inheritPrototype(subType,superType){
    var prototype=Object(superType.prototype);//创建对象
    prototypr.constructor=subType;//增强对象  弥补因重写原型失去的默认的constructor属性  subType.prototype=prototype;//指定对象 将新创建的对象赋值给子类型的原型}

这个示例中的inheritPrototype()函数实现了组合继承的最简单形式。我们可以调用inheritPrototype()函数区替换前面例子中的子类型原型赋值函数:

function SuperType(name) {
    this.name=name;
    this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName=function(){
    alert(this.name);
};
function SubType(name,age) {
    SuperType.call(this,name);//第一次调用SuperType()
    this.age=age
}
inheritPrototype(Subtype,Supertype);
SubType.prototype.sayAge=function(){
    alert(this.age);
};

  这个例子的搞笑体现在只调用了一次Supertype构造函数,避免了在SubType.prototype上创建的不必要、多余的属性。与此同时还能保持原型链的不变;因此,还能正常使用instanceof和isPrototypeOf()。所以现在寄生式组合继承是最理想的继承方式。

PS:关于原型式继承和寄生式继承在这里就不多做解释了。大家可以自己去查相关的资料,不太推荐使用(个人观点)。

时间: 2024-12-22 15:53:26

对Javascript的原型,原型链和继承的个人理解的相关文章

对Javascript 类、原型链、继承的理解

一.序言 ??和其他面向对象的语言(如Java)不同,Javascript语言对类的实现和继承的实现没有标准的定义,而是将这些交给了程序员,让程序员更加灵活地(当然刚开始也更加头疼)去定义类,实现继承.(以下不讨论ES6中利用class.extends关键字来实现类和继承:实质上,ES6中的class.extends关键字是利用语法糖实现的) Javascript灵活到甚至可以实现接口的封装(类似Java中的Interface和implements). 二.类的实现 1.我对类的理解 ??首先,

JavaScript原型链和继承

1.概念 JavaScript并不提供一个class的实现,在ES6中提供class关键字,但是这个只是一个语法糖,JavaScript仍然是基于原型的.JavaScript只有一种结构:对象.每个对象都有一个私有属性:_proto_,这个属性指向它构造函数的原型对象(property).它的原型对象也有一个属于自己的原型对象,这样层层向上知道一个对象的属性为null.根据定义null没有原型,它是这个原型链中的最后一个环节. 几乎所有的JavaScript中的对象都是位于原型链顶端的Objec

浅谈Javascript中的原型、原型链、继承

构造函数,原型,实例三者的关系 构造函数: 构造函数是创建对象的一种常用方式, 其他创建对象的方式还包括工厂模式, 原型模式, 对象字面量等.我们来看一个简单的构造函数: function Product (name, role){ //构造函数的命名约定第一个字母使用大写的形式! this.name = name; this.role = role; } ( 1 ) 每一个构造函数都有一个prototype属性,我们可以在Chorme控制台中打印出Product.prototype属性. (

JavaScript ES5类 原型 原型链 组合、原型、寄生式继承

ES5类 原型  原型链 继承 JavaScript中,原型是相对于构造函数(类)的叫法(或者说概念),原型链是相对于构造函数(类)的实例对象的叫法. 对于JavaScript对象,如果在对象自身上找不到该属性,那么就会向上沿着原型链继续查找该属性 创建一个ES5类 在ES5中,类是由函数名首字母大写的函数,通过关键字new创建的. 类的构造函数就是函数自身 一般情况下,ES5类的原型对象prototype是自身构造函数,该类的实例化对象的原型链对象__proto__也是该构造函数,这二者指向同

深入javascript面向对象,js的原型链、继承

进阶面向对象----------------------– 在JS源码中,系统对象也是基于原型的程序, 尽量不要去添加和修改系统对象的方法 包装对象----------------------– 基本类型都有自己对应的包装对象 比如String Number Boolean 基本类型会找到对应的包装对象类型,然后包装对象把所有的属性方法给了 基本类型,然后包装对象消失 例如 var str = 'abc'; str.num = 10; //创建一个包装对象,给包装对象加num属性,然后立刻消失.

JavaScript难点系列(六):原型链与继承

类和构造函数 JS中使用构造函数来定义类: function Range(from, to) { this.from = from this.to = to } Range.prototype.includes = function(x) { return this,from <= x && x <= this.to } Range.prototype.toString = function() { return this.from + '...' + this.to } va

菜鸟快飞之JavaScript对象、原型、继承(三)

正文之前需要声明的一点是,菜鸟系列博文全是基于ES5的,不考虑ES6甚至更高版本. 继承 由于我个人不是学计算机的,所以对于很多东西只是知其然,不知其所以然.就像这个继承,刚开始学JavaScript就听人说了JavaScript几大核心,但是自己平时似乎都没怎么用到,所以一直不明白为什么需要这些东西,面试还总是问这些. 但是随着一点点学习,也有去看过jQuery源码,虽然到现在也没怎么看懂(主要也是有些懒),但慢慢还是对这些东西有了更深的了解. 为什么需要继承 举个很简单的例子,我在平时学习写

javascript中的原型与原型链

javascript是一门面向对象的语言,但它却没有像其他面向对象的语言(如java,C++)有类的概念,也没有基于类的继承体系.但是它有自己独特的继承方式,那就是基于原型和原型链的继承. 当我们创建一个对象时,每个对象在生成之后都有一个隐式的属性__proto__(非规范,暂且这么叫吧),该属性指向该实例的原型对象,并共享其原型对象上的属性和方法. 下面分析下不同创建对象的方式所对应的__proto__属性指向的不同. 1.用对象字面量创建对象. var a={x:1}; console.lo

JavaScript中的原型继承原理

在JavaScript当中,对象A如果要继承对象B的属性和方法,那么只要将对象B放到对象A的原型链上即可.而某个对象的原型链,就是由该对象开始,通过__proto__属性连接起来的一串对象.__proto__属性是JavaScript对象中的内部属性,任何JavaScript对象,包括我们自己构建的对象,JavaScript的built-in对象,任何函数(在JavaScript当中,函数也是对象)都具有这个属性.如下图就是一个原型链的例子: 上图中,A,B,C分别代表3个对象,蓝色箭头串接起来