javascript 原型与原型链的理解及实例分析
本文实例讲述了javascript原型与原型链的理解。分享给大家供大家参考,具体如下:
javascript中一切皆对象,但是由于没有Class类的概念,所以就无法很好的表达对象与对象之间的关系了。
比如对象A与对象B之间,它们两个是相对独立的个体,互不干扰,对象A修改自身的属性不会影响到对象B。
虽然这很好,但是有一个问题,如果对象A与对象B都有一个方法run(),并且代码也一样,那对象A与对象B各自都独立拥有一份run()方法的完整代码,这是需要资源去保存的。
一旦我们程序中应用的对象过多,那这种资源消耗会是巨大的。那有没有一种方法可以让对象A与对象B拥有一些公共的属性和方法,让它们之前有某种联系?
我们设想一下,会不会存在一个common对象(公共对象),common对象上保存着公共的属性和方法,而对象A与对象B里面有一个prototype属性指向这个common对象,
当然我们调用对象A或对象B的属性和方法时,如果在自身对象中没有找到,就去prototype这个属性指向的对象上面去找。
而common对象本身也有一个prototype属性指向更上一级的common对象,然后一直往上找啊找,直到为null,就停止。
这种不断的从下往上找的这种路径,就像链条一样,我们称它为原型链,而那个common对象,我们称它为原型对象。
我们来看一个构造函数
functionBase(name){ this.name=name; } letA=newBase('A'); letB=newBase('B'); //每一个函数都有一个prototype属性,指向该函数的原型对象 console.log(Base.prototype); //当然原型对象也是一个对象,它也有一个constructor,指向构造函数 console.log(Base.prototype.constructor===Base); //每一个实例对象的constructor都指向创建它们的构造函数 console.log(A.constructor===Base); console.log(B.constructor===Base); //每一个实例对象都有一个__proto__属性,该属性指向构造函数的原型对象 console.log(A.__proto__===Base.prototype); console.log(B.__proto__===Base.prototype);
1、每一个函数都有一个prototype属性,它指向该函数的原型对象。
2、原型对象也是对象,它也有自已的constructor,它指向构造函数Base()。换句话说,其实原型对象也是构造函数Base()的一个实例。只不过比较特殊,用来存放公共属性和方法的。
3、每一个通过构造函数Base()创建的实例对象,都有一个constructor,指向创建它们的构造函数。
4、每一个对象,都有一个__proto__属性,指向构造函数Base()的原型对象。换句话说,__proto__是将原型串联起来形成链条的关键。不然对象A与对象B都无法找到原型对象上的公共属性和方法。
functionBase(name){ this.name=name; } //我们在原型对象上添加公共属性 Base.prototype.status='开始'; //我们在原型对象上添加公共方法 Base.prototype.run=function(){ console.log(this.name+'run...'); }; letA=newBase('A'); letB=newBase('B'); A.run(); B.run(); console.log(A.status); console.log(B.status); //修改原型上的属性,则实例对象也会跟着改变 Base.prototype.status='停止'; console.log(A.status); console.log(B.status);
通过原型与原型链,让对象与对象之间有了关联关系。
那如何通过原型与原型链,让一个构造函数继承于另一个构造函数?
比如,我们要让构造函数Child继承于构造函数Base,只需要让Child的prototype指向Base的原型对象,不就可以了?
functionBase(name){ } Base.prototype.name='Base'; Base.prototype.run=function(){ console.log(this.name+'run...'); }; functionChild(){ } Child.prototype=Base.prototype; //注意这个时候,Child.prototype对象的constructor属性指向了Base //这就导致通过构造函数Child创建的实例对象,对象的constructor属性会指向Base,而不是Child,这会导致混乱。 //所以我们重新设置Child.prototype.constructor指向Child Child.prototype.constructor=Child; letc=newChild(); console.log(c.name); c.run();
这样有一个问题,Child.prototype与Base.prototype指向同一个原型对象,任何对Child.prototype的修改都会反应到Base.prototype上面。
这时,Base.prototype.constructor指向了Child,这显然是有问题。
我们只能通过一个中间的空构造函数,来完成原型的指向。
functionBase(name){ } Base.prototype.name='Base'; Base.prototype.run=function(){ console.log(this.name+'run...'); }; functionChild(){ } //创建一个中间的空构造函数 functionMid(){ } //让该空构造函数的prototype指向Base的原型对象 Mid.prototype=Base.prototype; //再让Child的prototype指向该空构造函数的一个实例 Child.prototype=newMid(); //这样,当修改Child.prototype.constructor时,Base.prototype就不会受影响了 Child.prototype.constructor=Child; letc=newChild(); console.log(c.name); c.run(); //Base.prototype的constructor仍然指向Base,没有受到影响 console.log(Base.prototype.constructor);
那怎么通过原型与原型链,让你一对象继承于另一个对象呢?
比如,我们要让对象B继承于对象A,无非就是想要拿到对象A的属性和方法,这么一想,那通过把对象B的__proto__ 指向对象A,不就可以实现了?
letA={ name:'A', run(){ console.log(this.name+'run...'); } }; console.log(A.name); A.run(); letB={}; //让对象B的__proto__指向对象A B.__proto__=A; //当对象B调用run()方法时会在自身上找,如果没找到,则通过__proto__向上找 //由于__proto__指向对象A,所以最终会在对象A中找到run()方法 B.run(); B.__proto__.name='B'; console.log(A.name); console.log(B.name);
这样有一个问题,当修改B.__proto__.name='B';时,对象A也会受到影响。
我们可以通过ES5提供的Object.create()来解决此问题,Object.create()可以通过指定的原型对象创建一个新对象。
letA={ name:'A', run(){ console.log(this.name+'run...'); } }; console.log(A.name); A.run(); letB={}; //通过Object.create()创建一个以对象A为原型对象的新对象 //让对象B的__proto__指向该新对象 //这样再操作B.__proto__中的属性就与对象A无关了。 B.__proto__=Object.create(A); B.run(); B.__proto__.name='B'; console.log(A.name); console.log(B.name);
感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码运行效果。
更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》
希望本文所述对大家JavaScript程序设计有所帮助。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。