把js的原型和原型链重新梳理了一遍,然后动手绘制了一张流程图,原型和原型链的秘密就藏在这张图上。绘制流程图的好处就是在绘制的过程中,既检验自己对这个知识点的掌握状况,同时在绘制过程中会对这个知识点印象更深刻,理解更透彻,建议每个感兴趣的小伙都来身体力行一次。
为了更清晰的了解原型链的走向,先创建三个构造函数,建立多重继承关系,分别为person、chinaperson、provinceperson,它们之间的继承关系为:provinceperson 继承了 chinaperson, chinaperson继承了person。接下来,我们
先贴上代码,每个子构造函数会在继承父级的基础上,分别在构造函数里面和原型里面,自定义添加自己的属性和方法;另外在person原型上写上和构造函数里面同名的属性和方法,用来验证同名方法名时,构造函数里面的方法和原型上的方法哪个优先执行;在provinceperson上会重写从父级继承的方法,侧面粗略展示下面向对象的多态特性。
/** * javascript的多级继承和多态、原型和原型链的体现 * */ //1.1 构造函数:人(person) function person(name){ this.name = name ? name : "人类"; this.methodperson = function(){ console.log("person构造函数里面的方法methodperson-->我的标签:" + this.name); } console.log("********person 构造函数 初始化********"); } //给person的原型上添加属性和方法 person.prototype.age = 18; person.prototype.run = function(){ console.log("person原型方法run-->name: " + this.name + ", age: " + this.age + ", 欢快的run"); } //问题:如果构造函数的原型里面的属性和方法,和构造函数的属性和方法同名,实例对象调用属性和方法时执行哪个? //person原型的name和person构造函数的name,实例对象调用哪个? person.prototype.name = "炎黄子孙"; person.prototype.methodperson = function(){ console.log("person原型的methodperson方法-->标签:" + this.name + ", age: " + this.age); } //1.2 构造函数:中国人(chinaperson),继承于person function chinaperson(name, skin){ person.call(this, name); //调用父级person构造函数 this.skin = skin ? skin : "黄色"; this.methodchinaperson = function(){ console.log("chinaperson构造函数里面的方法methodchinaperson-->肤色:" + this.skin + ", 标签: " + this.name); } console.log("********chinaperson 构造函数 初始化********"); } //设置chinaperson原型指向person原型,相当于chinaperson继承person chinaperson.prototype = object.create(person.prototype); //设置新原型的构造函数指向自己 chinaperson.prototype.constructor = chinaperson; //给chinaperson的原型自定义添加属性和方法 chinaperson.prototype.hero = "谭嗣同"; chinaperson.prototype.write = function(){ console.log("chinaperson原型里面的方法write-->我自横刀向天笑,去留肝胆两昆仑!is who? " + this.hero + ", 标签: " + this.name + ", skin: " + this.skin); } //1.3 构造函数:provinceperson, 继承于chinaperson function provinceperson(name, skin, count){ chinaperson.call(this, name, skin); //调用父级chinaperson构造函数 this.count = count ? count : 8000; this.methodprovinceperson = function(){ console.log("provinceperson构造函数里面的方法methodprovinceperson-->数量:" + this.count + "万, 肤色:"+ this.skin + ", 标签:" + this.name); } //重写从父级继承下来的方法methodchinaperson this.methodchinaperson = function(){ console.log("provinceperson构造函数里面重写父级方法methodchinaperson...."); } console.log("********provinceperson 构造函数 初始化********") } //设置provinceperson原型指向chinaperson原型,相当于构造函数provinceperson继承于chinaperson provinceperson.prototype = object.create(chinaperson.prototype); //设置新原型的构造函数指向自己 provinceperson.prototype.constructor = provinceperson; //给provinceperson的原型上自定义添加属性和方法 provinceperson.prototype.feature = "长沙臭豆腐"; provinceperson.prototype.eat = function(){ console.log("provinceperson原型里面的方法eat-->标签:" + this.name + ", 地方小吃是:" + this.feature + ", hero: " + this.hero + ", skin: " + this.skin); } //重写从父级原型继承下来的方法 provinceperson.prototype.write = function(){ console.log("provinceperson原型里面重写从父级原型继承的write方法-->。。。。^_^"); }
结合以上代码,绘制构造函数原型链关系,如下图:
对上图进行说明,和原型链知识点进行归纳:
大概知识点就是这些,在上面代码的基础上,再来一些测试代码,验证一下。先上一份测试原型链关系的代码:
//测试一下 var pro1 = person.prototype, pro2 = chinaperson.prototype, pro3 = provinceperson.prototype; //provinceperson原型的原型 === chinaperson的原型 var pro3_china = object.getprototypeof(pro3); //provinceperson原型的原型的原型 === person的原型 var pro3_person = object.getprototypeof(pro3_china); //provinceperson原型的原型的原型的原型 === object的原型 var pro3_object = object.getprototypeof(pro3_person); //function和object作为普通函数时是构造函数function的实例对象,获取这两个实例对象的原型 var pro_function = object.getprototypeof(function), pro_object = object.getprototypeof(object); console.log("************* 原型链测试 start **********") console.log("构造函数provinceperson继承自chinaperson, 构造函数chinaperson继承自person, person继承自object") console.log("object --> person --> chinaperson --> provinceperson") console.log("person.原型:", pro1); console.log("chinaperson.原型:", pro2); console.log("provinceperson.原型:", pro3); console.log("provinceperson.原型.原型: ", pro3_china); console.log("provinceperson.原型.原型.原型: ", pro3_person); console.log("provinceperson.原型.原型.原型.原型:", pro3_object); console.log("provinceperson.原型.原型 === chinaperson.原型 --> ", pro3_china === pro2); console.log("provinceperson.原型.原型.原型 === person.原型 --> ", pro3_person === pro1); console.log("provinceperson.原型.原型.原型.原型 === object.原型 --> ", pro3_object === object.prototype); console.log("function.prototype === function.__proto__ --> ", function.prototype === pro_function); console.log("object.__proto__ === function.prototype --> ", pro_object === function.prototype); console.log("************ 测试 end ************\n") /* 测试结果: ************* 原型链测试 start ********** 构造函数provinceperson继承自chinaperson, 构造函数chinaperson继承自person, person继承自object object --> person --> chinaperson --> provinceperson person.原型: {age: 18, run: ƒ, name: "炎黄子孙", methodperson: ƒ, constructor: ƒ} chinaperson.原型: person {constructor: ƒ, hero: "谭嗣同", write: ƒ} provinceperson.原型: chinaperson {constructor: ƒ, feature: "长沙臭豆腐", eat: ƒ, write: ƒ} provinceperson.原型.原型: person {constructor: ƒ, hero: "谭嗣同", write: ƒ} provinceperson.原型.原型.原型: {age: 18, run: ƒ, name: "炎黄子孙", methodperson: ƒ, constructor: ƒ} provinceperson.原型.原型.原型.原型: {constructor: ƒ, __definegetter__: ƒ, __definesetter__: ƒ, hasownproperty: ƒ, __lookupgetter__: ƒ, …} provinceperson.原型.原型 === chinaperson.原型 --> true provinceperson.原型.原型.原型 === person.原型 --> true provinceperson.原型.原型.原型.原型 === object.原型 --> true function.prototype === function.__proto__ --> true object.__proto__ === function.prototype --> true ************ 测试 end ************ */
测试结果截图:
再来一份对于多级继承和重写展示的测试代码:
//第二波测试,测试构造函数的继承 和 多态(重写从父级继承下来的属性或方法) console.log("\n************* 继承和重写 start ************"); console.log(">>>>>>准备创建一个person实例对象>>>>>"); var per = new person("王大锤"); per.methodperson(); per.run(); console.log("*****person实例对象测试结论:构造函数和原型有同名属性或方法,实例对象优先调用构造函数的属性或方法*****\n"); console.log("\n>>>>>准备创建一个chinaperson实例对象,chinaperson继承了person >>>>>"); var chobj = new chinaperson("中国人", "黄色"); chobj.methodchinaperson(); chobj.write(); chobj.methodperson(); chobj.run(); console.log("*****chinaperson实例对象测试结论:继承自父类person, 拥有父类所有对外的构造函数里面和原型里面的属性和方法\n"); console.log("\n>>>>>准备创建一个provinceperson实例对象,provinceperson继承了chinaperson>>>>>"); var proobj = new provinceperson("湖南人", "黄色", 888); proobj.methodprovinceperson(); proobj.eat(); proobj.methodchinaperson(); proobj.write(); proobj.methodperson(); proobj.run(); console.log("*****provinceperson实例对象测试结论:拥有父级和父级的父级的所有对外的,包括构造函数里面和原型里面的属性和方法;另外也可以对父级属性或方法进行重写"); console.log("************ 测试 end ************\n"); /* 测试结果打印日志: ************* 继承和重写 start ************ >>>>>>准备创建一个person实例对象>>>>> ********person 构造函数 初始化******** person构造函数里面的方法methodperson-->我的标签:王大锤 person原型方法run-->name: 王大锤, age: 18, 欢快的run *****person实例对象测试结论:构造函数和原型有同名属性或方法,实例对象优先调用构造函数的属性或方法***** >>>>>准备创建一个chinaperson实例对象,chinaperson继承了person >>>>> ********person 构造函数 初始化******** ********chinaperson 构造函数 初始化******** chinaperson构造函数里面的方法methodchinaperson-->肤色:黄色, 标签: 中国人 chinaperson原型里面的方法write-->我自横刀向天笑,去留肝胆两昆仑!is who? 谭嗣同, 标签: 中国人, skin: 黄色 person构造函数里面的方法methodperson-->我的标签:中国人 person原型方法run-->name: 中国人, age: 18, 欢快的run *****chinaperson实例对象测试结论:继承自父类person, 拥有父类所有对外的构造函数里面和原型里面的属性和方法 >>>>>准备创建一个provinceperson实例对象,provinceperson继承了chinaperson>>>>> ********person 构造函数 初始化******** ********chinaperson 构造函数 初始化******** ********provinceperson 构造函数 初始化******** provinceperson构造函数里面的方法methodprovinceperson-->数量:888万, 肤色:黄色, 标签:湖南人 provinceperson原型里面的方法eat-->标签:湖南人, 地方小吃是:长沙臭豆腐, hero: 谭嗣同, skin: 黄色 provinceperson构造函数里面重写父级方法methodchinaperson.... provinceperson原型里面重写从父级原型继承的write方法-->。。。。^_^ person构造函数里面的方法methodperson-->我的标签:湖南人 person原型方法run-->name: 湖南人, age: 18, 欢快的run *****provinceperson实例对象测试结论:拥有父级和父级的父级的所有对外的,包括构造函数里面和原型里面的属性和方法;另外也可以对父级属性或方法进行重写 ************ 测试 end ************ */
如对本文有疑问, 点击进行留言回复!!
同事牛逼啊,写了个隐藏 bug,我排查了 3 天才解决问题!
【JavaScript笔记(一)】万丈高楼平地起 - 基本概念篇
轻松解决 org.apache.taglibs.standard.tlv.JstlCoreTLV 困惑
网友评论