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

所谓的对象,就是抽象化的数据自己

一个面向对象转向面向原型的困惑

我发现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();

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

clipboard.png

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

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

可是你们都知道高级语言都是要吸引别人来用的,这个方式实在是和其余语言不太同样,怎么吸引其余人来用呢?本着不行就封装一层的原则因而语言提供了构造函数,可是这个构造函数到这里为止仍是和其余语言有彻底不一样的意义(固然使用上区别不大)。spa

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这个对象上动态添加方法。prototype

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

clipboard.png
对咱们来讲只须要关注横着一排的原型链就好了,至于Person构造函数是一个函数天然也是一个对象(函数也是对象),一样天然有本身的原型链可是这里和主体无关就不体如今图上了。3d

若是原型链的知识都差很少了的话我以为就能够放出下面这张广为人知的图来记忆一番了:

clipboard.png
而后写了这么多,你就发现其实继承在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);

使用一张图展现上面的代码作了什么:
clipboard.png

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当中的封装

相关文章
相关标签/搜索