于javascript原型链的层层递进查找规则,以及原型对象(prototype)的共享特性,实现继承是非常简单的事情
一、把父类的实例对象赋给子类的原型对象(prototype),可以实现继承
function Person(){ this.userName = 'ghostwu'; } Person.prototype.showUserName = function(){ return this.userName; } function Teacher (){} Teacher.prototype = new Person(); var oT = new Teacher(); console.log( oT.userName ); //ghostwu console.log( oT.showUserName() ); //ghostwu
通过把父类(Person)的一个实例赋给子类Teacher的原型对象,就可以实现继承,子类的实例就可以访问到父类的属性和方法
如果你不会画这个图,你需要去看下我的这篇文章:
[js高手之路]一步步图解javascript的原型(prototype)对象,原型链
第11行,执行oT.userName, 首先去oT对象上查找,很明显oT对象上没有任何属性,所以就顺着oT的隐式原型__proto__的指向查找到Teacher.prototype,
发现还是没有userName这个属性,继续沿着Teacher.prototype.__proto__向上查找,找到了new Person() 这个实例上面有个userName,值为ghostwu
所以停止查找,输出ghostwu.
第12行,执行oT.showUserName前面的过程同上,但是在new Person()这个实例上还是没有查找到showUserName这个方法,继续沿着new Person()的
隐式原型__proto__的指向( Person.prototype )查找,在Person.prototype上找到了showUserName这个方法,停止查找,输出ghostwu.
二、把父类的原型对象(prototype)赋给子类的原型对象(prototype),可以继承到父类的方法,但是继承不到父类的属性
function Person(){ this.userName = 'ghostwu'; } Person.prototype.showUserName = function(){ return 'Person::showUserName方法'; } function Teacher (){} Teacher.prototype = Person.prototype; var oT = new Teacher(); console.log( oT.showUserName() ); //ghostwu console.log( oT.userName ); //undefined, 没有继承到父类的userName
因为Teacher.prototype的隐式原型(__proto__)只指向Person.prototype,所以获取不到Person实例的属性
三、发生继承关系后,实例与构造函数(类)的关系判断
还是通过instanceof和isPrototypeOf判断
function Person(){ this.userName = 'ghostwu'; } Person.prototype.showUserName = function(){ return this.userName; } function Teacher (){} Teacher.prototype = new Person(); var oT = new Teacher(); console.log( oT instanceof Teacher ); //true console.log( oT instanceof Person ); //true console.log( oT instanceof Object ); //true console.log( Teacher.prototype.isPrototypeOf( oT ) ); //true console.log( Person.prototype.isPrototypeOf( oT ) ); //true console.log( Object.prototype.isPrototypeOf( oT ) ); //true
四,父类存在的方法和属性,子类可以覆盖(重写),子类没有的方法和属性,可以扩展
function Person() {} Person.prototype.showUserName = function () { console.log('Person::showUserName'); } function Teacher() { } Teacher.prototype = new Person(); Teacher.prototype.showUserName = function(){ console.log('Teacher::showUserName'); } Teacher.prototype.showAge = function(){ console.log( 22 ); } var oT = new Teacher(); oT.showUserName(); //Teacher::showUserName oT.showAge(); //22
五、重写原型对象之后,其实就是把原型对象的__proto__的指向发生了改变
原型对象prototype的__proto__的指向发生了改变,会把原本的继承关系覆盖(切断)
function Person() {} Person.prototype.showUserName = function () { console.log('Person::showUserName'); } function Teacher() {} Teacher.prototype = new Person(); Teacher.prototype = { showAge : function(){ console.log( 22 ); } } var oT = new Teacher(); oT.showAge(); //22 oT.showUserName();
上例,第7行,Teacher.prototype重写了Teacher的原型对象(prototype),原来第6行的原型对象的隐式原型(__proto__)指向就没有作用了
所以在第14行,oT.showUserName() 就会发生调用错误,因为Teacher的原型对象(prototype)的隐式原型(__proto__)不再指向父类(Person)的实例,继承关系被破坏了.
六、在继承过程中,小心处理实例的属性上引用类型的数据
function Person(){ this.skills = [ 'php', 'javascript' ]; } function Teacher (){} Teacher.prototype = new Person(); var oT1 = new Teacher(); var oT2 = new Teacher(); oT1.skills.push( 'linux' ); console.log( oT2.skills ); //php, java, linux
oT1的skills添加了一项linux数据,其他的实例都能访问到,因为其他实例中共享了skills数据,skills是一个引用类型
七、借用构造函数
为了消除引用类型影响不同的实例,可以借用构造函数,把引用类型的数据复制到每个对象上,就不会相互影响了
function Person( uName ){ this.skills = [ 'php', 'javascript' ]; this.userName = uName; } Person.prototype.showUserName = function(){ return this.userName; } function Teacher ( uName ){ Person.call( this, uName ); } var oT1 = new Teacher(); oT1.skills.push( 'linux' ); var oT2 = new Teacher(); console.log( oT2.skills ); //php,javascript console.log( oT2.showUserName() );
虽然oT1.skills添加了一项Linux,但是不会影响oT2.skills的数据,通过子类构造函数中call的方式,去借用父类的构造函数,把父类的属性复制过来,而且还能
传递参数,如第8行,但是第15行,方法调用错误,因为在构造中只复制了属性,不会复制到父类原型对象上的方法
八、组合继承(原型对象+借用构造函数)
经过以上的分析, 单一的原型继承的缺点有:
1、不能传递参数,如,
Teacher.prototype = new Person();
有些人说,小括号后面可以跟参数啊,没错,但是只要跟了参数,子类所有的实例属性,都是跟这个一样,说白了,还是传递不了参数
2、把引用类型放在原型对象上,会在不同实例上产生相互影响
单一的借用构造函数的缺点:
1、不能复制到父类的方法
刚好原型对象方式的缺点,借用构造函数可以弥补,借用构造函数的缺点,原型对象方式可以弥补,于是,就产生了一种组合继承方法:
function Person( uName ){ this.skills = [ 'php', 'javascript' ]; this.userName = uName; } Person.prototype.showUserName = function(){ return this.userName; } function Teacher ( uName ){ Person.call( this, uName ); } Teacher.prototype = new Person(); var oT1 = new Teacher( 'ghostwu' ); oT1.skills.push( 'linux' ); var oT2 = new Teacher( 'ghostwu' ); console.log( oT2.skills ); //php,javascript console.log( oT2.showUserName() ); //ghostwu
子类实例oT2的skills不会受到oT1的影响,子类的实例也能调用到父类的方法。
以上这篇[js高手之路]从原型链开始图解继承到组合继承的产生详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
js原型链继承
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新动态
- 群星《前途海量 电影原声专辑》[FLAC/分轨][227.78MB]
- 张信哲.1992-知道新曲与精丫巨石】【WAV+CUE】
- 王翠玲.1995-ANGEL【新艺宝】【WAV+CUE】
- 景冈山.1996-我的眼里只有你【大地唱片】【WAV+CUE】
- 群星《八戒 电影原声带》[320K/MP3][188.97MB]
- 群星《我的阿勒泰 影视原声带》[320K/MP3][139.47MB]
- 纪钧瀚《胎教古典音乐 钢琴与大提琴的沉浸时光》[320K/MP3][148.91MB]
- 刘雅丽.2001-丽花皇后·EMI精选王【EMI百代】【FLAC分轨】
- 齐秦.1994-黄金十年1981-1990CHINA.TOUR.LIVE精丫上华】【WAV+CUE】
- 群星.2008-本色·百代音乐人创作专辑【EMI百代】【WAV+CUE】
- 群星.2001-同步过冬AVCD【环球】【WAV+CUE】
- 群星.2020-同步过冬2020冀待晴空【环球】【WAV+CUE】
- 沈雁.1986-四季(2012梦田复刻版)【白云唱片】【WAV+CUE】
- 纪钧瀚《胎教古典音乐 钢琴与大提琴的沉浸时光》[FLAC/分轨][257.88MB]
- 《国语老歌 怀旧篇 3CD》[WAV/分轨][1.6GB]