原型与 原型继承

javascript不支持传统类的继承的模式,是基于原型的继承,也就是通过prototype设置来实现继承

下面我们考虑下面的这个问题

    function Person() {
            this.name = "haha";

            this.sayName = function(){
                console.log(this.name);
            }
        }

        var p1 = new Person();
        var p2 = new Person();
        p1.sayName();
        console.log(p1.sayName === p2.sayName); //false

当我们使用构造函数模式(通过new调用函数)的时候 ,发现构造函数内的方法在每一个实例中都有创建一遍,也就是实现同样的一个功能要创建两个函数(函数也是对象,也就是当我们创建更多实例的时候,要不可避免的创建更多的相同功能的对象,这并不符合复用的思路)

为了实现对一个函数(对象)引用的问题 我们可以将上面的sayName方法移动到Person构造的外部,在通过this在实例化的时候绑定到特定的对象上面,这就实现了不同的对象引用的同一个函数(对象)

        function Person() {
			this.name = "haha";

			this.sayName = sayName;
		}

		function sayName() {
			console.log(this.name);
		}

		var p1 = new Person();
		var p2 = new Person();

		p1.sayName()//haha
		console.log(p1.sayName === p2.sayName);//true

这样解决问题的方式也存在着很大的问题 ,就是1)全局作用的函数仅仅被某个对象所调用,就应该考虑这个全局的函数位置有些不合理 (位置有点大)2)当我们要为Person定义很多类似的函数的时候,那这个person也就不存在我们所说的封装性,它定义的方法大部分都暴露了在外边

这样也就引出了原型模式

原型:每个函数都有一个prototype(原型)属性,这个属性是一个指针,它指向一个对象,这个对象的用途包含可以由特定的类型的所有实例共享的属性和方法 也就是prototype就是你创建实例的原型对象

原型模式

下面是一个简单的例子

              var animal = function(){
			this.type = "animal";
			this.testtype = "haha";

		}

		animal.prototype.sayType = function(){
			console.log(this.type);
		};//为animal的原型添加方法

		var person = function() {
			this.type = "person";//在person中添加新的type,屏蔽了animal中的type

		}//为Person添加属性

		person.prototype = new animal();//设置person的原型指向(同一个)animal实例

		var a = new person();
		a.sayType();
		console.log(a.testtype);                     a[person实例]        {type:"person"}          person.prototype[animal实例]          {type:"animal" testtype:"haah"}            animal.prototype            {function:sayType}              object.prototype//默认的原型               {}                     

 a对象的原型是animal的一个实例,所以a能访问animal实例中的属性或方法,并且通过animal的实例访问到animal原型中的属性或方法,我们可以通过对象实例访问保存在原型中的值,但是却不能通过对象实例重写原型的值,如果我们在实例中添加了一个与原型中一样的属性或者方法,那我们就是在这个实例中创建了这个属性或方法,这个属性或方法屏蔽了原型中的属性或方法

这是因为属性查找的过程是从实例开始沿着原型链一步一步向上查找的,当找到相应的属性,就返回,如果查找到object.prototype都没有,就返回undefined

所以当我们只想获得实例中的属性,而不是从原型的获得属性的时候,就要通过hasOwnProperty()这个方法了

缺点: 上面通过将实例的原型属性指向另一个实例的模式,会导致原型中的所有属性被实例所共享(指向同一个实例)并且我们也不能向原型中传递参数(也会导致所有实例同样属性的问题)   当这个实例中出现引用类型的属性的时候,就有可能出现我们意想不到的结果

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

        function SubType() {

        }

        SubType.prototype = new SuperType();

        var a = new SubType();
        var b = new SubType();

        a.colors.push("black");

        console.log(b.colors);//red green black         

      console.log(a instanceof SubType);//true
      console.log(a instanceof SuperType);//true
      console.log(a instanceof Object);//true  反映了实例与原型的关系 由于原型链的关系,可以说a实例是原型链中出现的类型的实例

 

上面的例子就反映出当原型对对象中存在引用类型的时候,修改一个实例中的值,第二个实例也会受到影响(是因为这个属性是存在原型链中通过查找获得,并不存在自己的实例中,所以会产生这样的结果)

基于上面的原因,我们很少使用单一的原型链实现继承 (继承的实现机制就是重写原型对象

为了解决超类原型中存在引用类型的问题,提出了一种借用构造函数的模式(经典继承)实现的思路是在子类型的环境中调用超类型的构造函数,这样每个子类型都有自己本身的一个超类型的副本,同时这种模式还有一个优点是可以向超类型的构造函数传递参数(也就是实现不同的子类型的定义)

        function SuperType(name) {
            this.name = name;
            this.colors = ["red","green"];
        }
        function SubType(name) {
            SuperType.call(this,name);
            //可以在这里添加自己的属性或者方法
            //call apply的区别是apply传递参数的形式必须是一个数组 call以多个参数的形式传递
        }
        var a = new SubType("haha");
        var b = new SubType("hao");

        console.log(a.name);//haha
        console.log(b.name);//hao

        a.colors.push("black");
        console.log(a.colors);//["red","green","black"]
        console.log(b.colors);//["red","green"]
    

但是借用构造函数模式也存在着一定的问题 1)在构造函数中定义方法,存在复用的问题 2)超类型原型中定义的方法对子类型是不可见的

             SuperType.prototype.sayHi = function() {
            console.log("hi");
        }

当我们通过生成的a实例去调用sayHi方法的时候,会报sayHi undefined (因为我们没有通过prototype去继承,所以无法通过原型链访问SuperType的属性)

这就提出了另外一种模式,也就是组合继承(伪经典继承),它是将借用构造模式与原型链技术整合到一起的一种模式,也就是通过借用构造模式实现对属性的继承,原型链实现对原型的属性和方法的继承 

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

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

        function SubType(name) {
            SuperType.call(this,name);

        }

        SubType.prototype = new SuperType();

        var a = new SubType("haha");
        var b = new SuperType("hao");

        a.sayName();//haha
        b.sayName();//hao

        a.colors.push("black");
        console.log(a.colors);//["red","green","black"]
        console.log(b.colors);//["red","green",]

这样的模式下 实例既有自己本身原型属性的副本,同时还能通过原型链访问到从原型继承来的属性 也是最常用的一种继承的模式

参考 javascript高级程序设计

汤姆大叔的blog 强大的原型与原型链 :http://www.cnblogs.com/TomXu/archive/2012/01/05/2305453.html

时间: 2024-10-05 15:31:27

原型与 原型继承的相关文章

面向对象、原型链、继承知识梳理

单例模式:就是一个对象咯 var person={ name:'xuwen', age:17 }; var person2={ name:'xiaoxu', age:25 } 工厂模式:就是一个函数,解决批量生产问题 1 function fn(name,age){ 2 var obj={}; 3 obj.name=name, 4 obj.age=age, 5 obj.write=function(){ 6 console.log(obj.name); 7 } 8 return obj; 9 }

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

继承是OO语言中一个最为人津津乐道的概念,也是初接触Javascript的初学者难理解的概念=.=继承主要分为两种:一种是接口继承,另一种是实现继承.而在ECMAScript中只支持实现继承,所以我们今天来讨论讨论实现继承.实现继承就是继承实际的方法,主要依靠原型链来实现.讲到这里我们就需要讨论讨论什么是原型链. 1.什么是原型 要理解原型链我们首先要知道什么是原型.我们知道每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,这个对象包含所有实例共享的属性和方法.所以我个人

JavaScript-原型&原型链&原型继承&组合函数

小小的芝麻之旅: 今天学习了js的原型,要说原型,我们先简单说一下函数创建过程. 原型 每个函数在创建的时候js都自动添加了prototype属性,这就是函数的原型,原型就是函数的一个属性,类似一个指针.原型在函数的创建过程中由js编译器自动添加. <script type="text/javascript"> function Flower(name,area) { this.name=name; this.area=area; this.showName=myName;

javascript 原型实现的继承

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en"> <head> <meta http-equiv="Content-Type"

原型、原型链、闭包、继承

一.原型.原型链 原型对象    在JavaScript 中,每当定义一个对象(函数)时候,对象中都会包含一些预定义的属性.其中函数对象的一个属性就是原型对象 prototype.注:普通对象没有prototype,但有__proto__属性. 原型对象其实就是普通对象(Function.prototype除外,它是函数对象,但它很特殊,他没有prototype属性(前面说道函数对象都有prototype属性)).看下面的例子:  function f1(){};  console.log(f1

原型、原型对象的理解 及其原型链继承

在 ES5 中,有的人可能对原型,原型对象,及其原型链不是很清楚,今天我就说说对这些的深入认识下.(如果有什么不懂得欢迎留言探讨,当然如果有什么写的不恰当的也希望大家留言备注.) 首先,再说原型与原型对象之前,当然有必要清楚构造函数,实例,原型与原型对象之间的关系.其实他们的关系也很简单. 构造函数,实例,原型与原型对象之间的关系: 构造函数有它自己的属性及其方法,其中包括自己定义的属性和方法外,还有两个特殊属性(prototype.constructor):而每个他的实例都会拥有它的所有属性和

原型,原型对象,原型链,构造函数,继承(一)

前言:javascript中 万物皆对象 , 但是对象是有区别的 分为普通对象(object)和函数对象(function): ①由以下三种形式创建的对象为函数对象: function fun1(){} var fun2 = function(){} var fun3 = new Function(); console.log(typeof fun1);//function console.log(typeof fun2);//function console.log(typeof fun3);

1、AJAX里面status的值代表什么 2、get post 的区别 3、怎样把对象转化成字符串 4、闭包、继承、原型、原型链 5 、http传输协议 6、arguments是什么

1.AJAX里面status的值代表什么     在JavaScript里面写AJax的时,最关键的一步是对XMLHttpRequest对象建立监听,即使用"onreadystatechange"方法.监听的时候,要对XMLHttpRequest对象的请求状态进行判断,通常是判断readyState的值为4且status的值为200或者304时执行我们需要的操作.以下记录了一些常用readState以及status的值及其含义 readyState 属性表示Ajax请求的当前状态.它的

原型链的继承都发生在构造函数上

网上的一道题目:var M = function() { this.person = "life" }, N = function() { this.gate = "ok" }; var k = new M(), l = new N(); k.__proto__ = l; console.log(k.gate); 这么写是有问题的. _proto_和prototype的区别. 首先应该是 k.prototype ...原型链的继承都是基本都是发生在构造函数上的,所以

深入理解JS原型链与继承

我 觉得阅读精彩的文章是提升自己最快的方法,而且我发现人在不同阶段看待同样的东西都会有不同的收获,有一天你看到一本好书或者好的文章,请记得收藏起来, 隔断时间再去看看,我想应该会有很大的收获.其实今天要讨论的主题,有许多人写过许多精彩的文章,但是今天我还是想把自己的理解的知识记录下来.我在学习 掌握JS原型链和继承的时候,就是看得@阮一峰老师的写的文章,觉得他写的技术类的文章都容易让理解,简明概要,又好理解.他是我学习JS路程里面一个比较佩服的导师,昨天重新看了他写的<Javascript 面向