资讯专栏INFORMATION COLUMN

Javascript面向对象从入门到重新入门--关于继承

3fuyu / 913人阅读

摘要:这个构造函数的不管从调用方式还是内部写法就都很有的感觉,但是从用途上来说,它其实更靠近的概念是中的工厂方法。到这里,所有关于继承的东西讲完了,接下来准备准备说说当中的封装

所谓的对象,就是抽象化的数据本身
一个面向对象转向面向原型的困惑

我发现Javascript这门语言每次翻开都会带给人新感受,尤其是看完其他语言的面向对象再来看它,但是如果你也是过来人就一定记得教科书里面冗长乏味的面向对象,所有的书上都会跟你这么说:面向对象是对要解决的问题的一种抽象,比如很经典的Java或者C++,class就是根基,然后类实例化出对象balabala....初学者来看的话其实是很难接受的,但是挺过这个时期,就会产生一种理所应当的感觉,就会觉得:面向对象当然需要类啦当然需要实例化啦,不然怎么继承的之类的。当然基本上主流的面向对象语言都会提供基本相同的概念。但是有个异类就是JavaScript,如果你用其他语言的概念来理解这个世界里的对象可能会找不着北。因为这个世界里没有“类”这个东西,所有的东西都是对象。
朝下看的时候,我建议学过面向对象的人忘掉“类”这个东西,不然很容易就会搞混。

什么是对象?

那到底什么是对象呢?这个问题问浅了是个傻问题问深了又变成了一个哲学问题,每个语言甚至每个人都有不同的答案,但是如果你只有十天来设计一门语言的话,你肯定也是想把这个东西设计的越简单越好,所以对JavaScript来说,对象就是属性+方法,简单来说就是这样:

var Person={
    name :"XXX",
    age:18,
    address:"YYY",
    gender:0,
    eat:function(){
        console.log("食");
    },
    wear:function(){
        console.log("衣");
    },
    live:function(){
        console.log("住");
    },
    walk:function(){
        console.log("行");
    }
}
Person.eat();

这样一个对象就写好了,不仅如此,我们在运行时还可以动态的修改内部对象的属性,以及增加方法等等非常自由。第一眼看上去非常的直观,但是仔细想想看,问题其实很多,比如,属性这样无限制的访问一点安全性都没有,再比如我想再要生成一个人但是名字叫小红的,就很费劲,再再比如我怎么实现继承?问题很多我们一个一个来说

纯对象怎么完成继承?

其实继承说白了,要做的事情就是把两个毫不相干的人建立父子关系,但是怎么建立呢?Javascript语言的对象生来都有一个特殊属性叫__proto__,我们可以用这个属性来关联其他对象,就像这样:

var Teacher={
    //这里添加老师的属性和方法
    __proto__:Person
}
Teacher.eat();

这样,两个对象之间就建立了联系,人类(对象)是老师(对象)的原型,用图表示就是:

这样一条链条把对象之间联系起来,这样使用Teacher调用eat方法的时候找不到就顺着链子朝上找一直找到头如果没有就报错,看起来很完美。

怎么欺骗其他世界的程序员?

但是大家都知道高级语言都是要吸引别人来用的,这个方式实在是和其他语言不太一样,怎么吸引其他人来用呢?本着不行就封装一层的原则于是语言提供了构造函数,但是这个构造函数到这里为止还是和其他语言有完全不同的意义(当然使用上区别不大)。

function Person(){
    this.name=name;
    this.eat=function(){
        console.log(this.name+" eat food now");
    }
}

ming =new Student("xiaoming");
hong =new Student("xiaohong");

这个构造函数的不管从调用方式还是内部写法就都很有Java Class的感觉,但是从用途上来说,它其实更靠近的概念是Java中的工厂方法。而且使用的时候还使用了new这个关键字,同时解决了上面的那个不用生成一个对象就写一大串代码的尴尬。同时还很灵活,你还是可以在ming或者hong这个对象上动态添加方法。

但是上面的操作有个很操蛋的问题就是,因为这个语言没有类只有对象,所以你构造函数里写的方法会原封不动的出现在新生成的对象当中,这意味着每个新生成的对象都有相同的函数,这就很浪费而且对象多了还吃内存。设计者当然也想到了这个问题,他用纯对象的思考方式想了一个解决办法,举个例子:
现在有一个Person构造函数(上面那样的),通常使用它来生成新的对象(小红小明等等)来通过原型链来访问父对象的方法,基于这个模型那我索性就再生成一个对象挂在Person下面,用一个属性指向它(大家都知道我说的就是prototype这个属性啦),公共的方法完全可以都放在这个对象里面,但是怎么调用这些方法呢?其实大家既然都是对象,小明小红的__proto__里面写的只要是Person.prototype就完事了呀,说了这么多用一个图来标识一下:


对我们来说只需要关注横着一排的原型链就行了,至于Person构造函数是一个函数自然也是一个对象(函数也是对象),同样自然有自己的原型链但是这里和主体无关就不体现在图上了。

如果原型链的知识都差不多了的话我觉得就可以放出下面这张广为人知的图来记忆一番了:


然后写了这么多,你就发现其实继承在Javascript当中原来也是一个谎言--只要把新的对象挂上原型链就算是“继承”了。那问题就变成了怎么构建原型链,我们还是放上道爷发明的一种方式来实现继承(方法多种多样,我觉得道爷的这种桥接并且不污染上下文环境的方式相当好用)

//父类
function Student(props){
    this.name=props.name||"unnamed";
}
Student.prototype.hello=function(){
    console.log("Hello, " + this.name + "!");
}
//子类
function PrimaryStudent(props) {
    Student.call(this, props);
    this.grade = props.grade || 1;
}
//使用一个空构造函数来桥接
function F(){}

F.prototype=Student.prototype; //把原本指向Function.prototype的指针指向父类
PrimaryStudent.prototype=new F();//桥接子类对象到一个F的匿名对象上
PrimaryStudent.prototype.constructor=PrimaryStudent;//纠正构造函数指向

PrimaryStudent.prototype.getGrade=function(){
        return this.grade;
}

// 开始测试
var xiaoming=new PrimaryStudent({
    name:"xiaoming",
    grade:2
});
console.log(xiaoming.__proto__===PrimaryStudent.prototype);
console.log(xiaoming.__proto__.__proto__===Student.prototype);
console.log(xiaoming instanceof PrimaryStudent);
console.log(xiaoming instanceof Student);

使用一张图展示上面的代码做了什么:

done!关系就是这么桥接好的,我们如果再在外面包一层函数就完全可以做工具函数来用

ES6标准下的语法糖

很明显,上面的这个做法很费劲,或者说,不直观没法吸引别人来用,所以ES6标准里加了一个很Java的语法糖,就像下面这么写:

class Person {
    constructor(name){
        this.name=name;
    }
    hello(){
        console.log(`${this.name} say hello to you!`);
    }
}
class Teacher extends Person{
    constructor(name,gender){
        super(name);
        this.gender=gender;
    }
    myGender(){
        console.log(`${this.name}"s gender is ${this.gender}`);
    }
}

var x=new Teacher("niuguangzhe","nan");
x.myGender();

可以说是Java味道十足,但是别忘了,其实语言的内部仍然是原型链。

到这里,所有关于继承的东西讲完了,接下来准备准备说说Javascript当中的封装

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/89975.html

相关文章

  • 【连载】前端个人文章整理-基础入门

    摘要:个人前端文章整理从最开始萌生写文章的想法,到着手开始写,再到现在已经一年的时间了,由于工作比较忙,更新缓慢,后面还是会继更新,现将已经写好的文章整理一个目录,方便更多的小伙伴去学习。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 个人前端文章整理 从最开始萌生写文章的想法,到着手...

    madthumb 评论0 收藏0
  • 【连载】前端个人文章整理-基础入门

    摘要:个人前端文章整理从最开始萌生写文章的想法,到着手开始写,再到现在已经一年的时间了,由于工作比较忙,更新缓慢,后面还是会继更新,现将已经写好的文章整理一个目录,方便更多的小伙伴去学习。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 个人前端文章整理 从最开始萌生写文章的想法,到着手...

    Labradors 评论0 收藏0
  • D1.Nodejs 入门

    摘要:上下文切换上下文最直观的表现就是代码块中的,通常在面向对象的编程中用到,来指代当前类生成的对应实例,与其他语言的一致。咦,是干嘛的,有没有其他方式实现,请自行谷歌。 分享第一篇,关于 NodeJS —— Javascript 的常用知识以及如何从 Javascript 开发者过渡到 NodeJS 开发者(不会介绍具体的框架)。在读本文前,希望你对 javascript 有一些初步的认识...

    Rango 评论0 收藏0
  • SegmentFault 技术周刊 Vol.40 - 2018,来学习一门新的编程语言吧!

    摘要:入门,第一个这是一门很新的语言,年前后正式公布,算起来是比较年轻的编程语言了,更重要的是它是面向程序员的函数式编程语言,它的代码运行在之上。它通过编辑类工具,带来了先进的编辑体验,增强了语言服务。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不觉已经到来了,总结过去的 2017,相信小伙们一定有很多收获...

    caspar 评论0 收藏0
  • SegmentFault 技术周刊 Vol.40 - 2018,来学习一门新的编程语言吧!

    摘要:入门,第一个这是一门很新的语言,年前后正式公布,算起来是比较年轻的编程语言了,更重要的是它是面向程序员的函数式编程语言,它的代码运行在之上。它通过编辑类工具,带来了先进的编辑体验,增强了语言服务。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不觉已经到来了,总结过去的 2017,相信小伙们一定有很多收获...

    nihao 评论0 收藏0

发表评论

0条评论

3fuyu

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<