JavaScript 面向对象之原型链继承实现

在ES6下可以通过class 关键字创建一个类, 也可以通过class .. extends 实现继承, 但只是一个语法糖, 最终还是通过原型链实现继承

JavaScript里每个function都有一个Prototype对象, 使用new 操作符去操作function时会产生一个新对象, 这个新对象会继承Prototype所有的属性和方法
代码如下

function Person (){};  //相当定义一个类
Person.say2 = function(){ console.log("Hi2") }; //在方法增加一个方法
Person.prototype.say = function () {console.log("hi!"); } //增加一个方法
Person.prototype.name = "小明"; //增加一个属性
Person ();  //undefined; 如果直接调用相当一个方法调用, 没有任何特别之处
var man = new Person();  //加上new之后可以当作一个构造函数
console.log(man.constructor === Person); //true,  构造函数
man.say(); // hi!, 实例继承了say方法
console.log(man.name) //小明,实例继承了name属性  
man.say2(); //报错,没找到该方法, 因为实例化时只会继承prototype

对象实例化出来只会继承prototype本身的属性和方法, 并不会继承function的属性和方法。 上面的代码看上去感觉像混合或复制prototype的方法和属性, 并没有什么神奇之处, 可以看下代码体会下神奇之处

function Person (){};  //相当定义一个类
Person.prototype.say = function () {console.log("hi!"); } //增加一个方法
var man = new Person(); 
var woman = new Person();
man.say = function(){console.log("Hi, man")};
man.say();  //Hi, man ,重写say方法
woman.say(); // hi!, 方法没有受到影响
    
Person.prototype.show = function(){ console.log("HAHA") }
man.show ();  //HAHA ,动态继承了show方法
woman.show (); //HAHA, 动态继承了show方法

Person.prototype.say = function () {console.log("hi!  Person"); } //增加一个方法
man.say();  //Hi, man ,say方法不受影响
woman.say(); // hi!  Person!, 方法已改变

从上面代码可以发现到几个规律, 实例重写方法并不会影响到prototype, 更不会影响到其它实例, prototype重写方法会影响到没有重写方法的实例, 并且prototype增加方法所有实例也会动态继承下来。 原理其实很简单, 可以通过console.dir(man),console.dir(woman), console.dir(Person.prototype) 打印结构分析

image.png

man和woman实例都有一个__proto __属性(谷歌浏览器会显示出来), __proto __属性指向Person的prototype, 它们是相等的, Person的prototype也有一个__proto __属性, 这个属性指向Object的Prototype, 而Object.Prototype.__proto __指向null, null处于最顶端, 这些__proto __连接在一起就组成了原型链。当试图访问一个对象的属性时, 首先会在对象的属性中查找, 找不到再沿__proto __这条原型链一直找, 直到找到或到达顶端才结束

通过以上的结论可以解释man.say方法为什么不受影响了, 当执行man.say = function(){console.log("Hi, man")}代码时, man相当于定义一个方法, 执行man.say()时, 首先就在man对象方法里找到了, 不用沿着原型链找。 所有不管Person.prototype.say怎么变都不会影响到man.say方法。而show方法在对象没有定义 , 所有要沿原型链查找

有了以上的知识,该考虑如何延续原型链, 达到类与类之间的继承。 仔细分析Person类就可以发现它是继承Object, man和woman实例都有Object的prototype的方法, 那是因为Person.prototype.__proto __属性指向Object.prototype, 所以延续原型链关键点在于__proto __属性,只要__proto __指向父类的prototype就算完成了继承, 但是__proto __是怎么来的? 由上面的例子得知是通过new function实例化才会有, 所以子类的prototype实际就是父类的实例
接上面的例子, 我们需要增加一个学生的子类, 学生子类比多一个报学号的方法。

function Person (){};  //相当定义一个类
Person.prototype.say = function () {console.log("hi!"); } //增加一个方法
function Student(){}; //定义一个学生类
Student.prototype = new Person();  //继承Person类
Student.prototype.sayNo = function(){console.log("1号")}; //定义一个报学号的方法
var xiaoming = new Student(); 
xiaoming.say();//hi!,  成功继承下来了
xiaoming.sayNo(); //1号, 方法正常
   
Person.prototype.show = function(){console.log("大家好")}; 
xiaoming.show(); //大家好,  动态继承也没问题

   //再增加一个子类, 三好学生
function GoodsStudent (){};
GoodsStudent.prototype = new Student();
GoodsStudent.prototype.prize = function(){console.log("领奖~")}; //定义一个领奖的方法
var xiaohong = new GoodsStudent();
xiaohong.say();//hi!,  成功继承下来了
xiaohong.sayNo();//1号, 成功继承下来了
xiaohong.prize();//领奖~, 正常 

console.log(xiaohong instanceof GoodsStudent && xiaohong instanceof Student && xiaohong instanceof Person && xiaohong instanceof Object);//true,  instanceof也没问题

一个简单的继承已经完成了, 但不完整, Student.prototype = new Person(); //继承Person类 这段代码有问题, 如果Person是有参的构造函数就行不通了。 问题还有很多, 面向对象中当子类正在实例化时,总是链式调用父类构造方法初始化父类空间, prototype中constructor必须指向当前构造函数。
最终版代码如下:

function Person (name){this.name = name};  //相当定义一个类
Person.prototype.say = function () {console.log("hi!" + this.name); } //增加一个方法
function Student(name){Person.apply(this, arguments)}; //当子类正在实例化时,总是链式调用父类构造方法初始化父类空间
//定义一个原型的构造函数
var prototypeFn = function(){ this.constructor =  Student; };// prototype中constructor必须指向当前构造函数 
prototypeFn.prototype = Person.prototype;
Student.prototype = new prototypeFn();
var xiaoming = new Student("小明");
xiaoming.say(); //hi!, 小明,  已经继承下来了, 没问题
console.log(xiaoming instanceof Student && xiaoming instanceof Person &&  xiaoming instanceof  Object); //true, instanceof也没问题

console.dir(xiaoming) 打印结构看上去也没问题

image.png

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容