美文网首页
JavaScript 面向对象之原型链继承实现

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

作者: 1204号技师 | 来源:发表于2018-01-22 11:13 被阅读32次

在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

相关文章

  • js6种继承模式

    继承是面向对象编程中又一非常重要的概念,JavaScript支持实现继承,不支持接口继承,实现继承主要依靠原型链来...

  • javascript面向对象中继承实现?

    面向对象的基本特征有:封闭、继承、多态。 在JavaScript中实现继承的方法: 1. 原型链(prototyp...

  • JS 中的一些概念问题

    Q:描述 JavaScript 中的继承和原型链,并举例子。 JavaScript 是基于原型的面向对象语言,并无...

  • Javascript继承的原理

    JavaScript的继承是通过原型链继承,与传统面向对象的继承方式不同。 prototype与原型 我们从头开始...

  • 2018-06-27

    JavaScript(面向对象+原型理解+继承+作用域链和闭包+this使用总结) 一、面向对象 1、什么是面向对...

  • 【javascript】继承

    javascript只支持实现继承,而且继承主要是依靠原型链来实现的。 原型链 javascript将原型链作为实...

  • ES5和ES6中对于继承的实现方法

    在ES5继承的实现非常有趣的,由于没有传统面向对象类的概念,Javascript利用原型链的特性来实现继承,这其中...

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

    在ES6下可以通过class 关键字创建一个类, 也可以通过class .. extends 实现继承, 但只是...

  • JavaScript面向对象程序设计

    JavaScript面向对象程序设计本文会碰到的知识点:原型、原型链、函数对象、普通对象、继承 读完本文,可以学到...

  • 全方位理解JavaScript面向对象

    JavaScript面向对象程序设计 本文会碰到的知识点:原型、原型链、函数对象、普通对象、继承 读完本文,可以学...

网友评论

      本文标题:JavaScript 面向对象之原型链继承实现

      本文链接:https://www.haomeiwen.com/subject/wcpxaxtx.html