学习Js也有很长的一段时间了。但是学习过程中也是走走停停。对很多概念的感觉也一直是模模糊糊。今天花了一点时间。重新研究了一下js里面的类和继承的机制。顺便总结下。
1.Js中的类式继承。
先看代码:
1 | function Person(name){ |
这是用js实现的最简单的一个Person类,其中包含了一个showNmae的方法,能够打印出name,然后实例化了一个叫xiaoming的对象,打印并且调用showName方法打印出name(也就是”Xiao Ming”)。
那么我们可能还会想,小明是一个人,而且他同样也是一个学生呀,这才是小明扮演的更加重要的角色。于是我们可以这样做。
1 | /*Person类*/ |
Student类继承了Person类,并拥有Person类的showName方法。在Student类中,我们还加入了Student所特有的属性和方法(grade和sayGrade方法)。
回过头来看看Student类。它和之前的Person类一样,先创建它的构造方法,这里我们看到了Person.call(this,name)这个语句。它的作用是调用父类Person的构造方法,并用this处于作用域链的最顶端,然后name作为参数被传入。
之后便是原型链的建立。
Javascript中的每个函数对象都有prototype属性,这也是Javascript不同与其他语言的重要因素之一。这个属性要么指向另外一个对象,要么就为null。当访问对象的某个成员时,如xiaoming.showName()。如果这个成员未见于当前对象,那么js会在当前prototype属性所指的对象中去查找它。如果在那个对象中也没有找到,那么js会沿着原型链向上逐一访问每一个原型对象,直到找到这个成员。——《Javascript设计模式》
而且我们还需要在原型链中将Student这个类的赋给Student.prototype.constructor函数。Student在设置为Person的实例时,其constructor属性已经被抹去(但是在这个地方,就算我们不写”Student.prototype.constructor = Student;”这句话整个程序还是照样能运行,但是是存在问题的,因为此时的Student.prototype.constructor的值是指向Person的,我们必须要将其指回他自己来)。
这里关于Student.prototype.constructor的问题,我们可以用下面的代码来测试
1 | /*Person类*/ |
这就是js中一个最简单的类式继承了。虽然和常规的oop式的继承还是有很大的不同,还是能够接受。但是还是感觉很不舒服,习惯了c#或者java的人看到js继承中出现的一大堆乱七八糟的prototype,估计头都大了。要是能用像extends这样的关键字来继承该多好。(作为一个小彩蛋,我把js里面稍微简化的继承放在文字后面。)
2.Js中的原型式继承。
接下来我们要看到的就是js中最酷的继承方式——原型继承。
原型继承与类式继承截然不同,我们发现在谈论他的时候,最好忘掉类和实例的一切知识,只从对象的角度来思考。——《Javascript设计模式》
还是先看代码,同样是实现之前的功能:
1 |
|
可以看到,这里我们首先是创建了一个Person的对象,其中,他有name的属性,还有showName这样一个方法。name属性首先赋值为null,如果在我们后面的xiaoMing对象中,如果我们不对xiaoMing.name进行赋值,那么调用showName方法所打印出来的值就是Person初始化的null。
这里我们使用了一个叫Object.create(someObject)的方法。这个方法的作用我们可以简单的理解,就是创建了一个对象,并将这个创建的新对象的原型链指向someObject。
原型继承的方式比之前的类式继承要清晰简明多了。我们接下来在实现之前代码中的Student类,然后用看看我么的结果是否和之前相同。
1 |
|
最后的结果和我们所期待的是完全相同。但是对比之前的类式继承,两者所用的方法是完全不同,首先,原型继承并没有涉及到constructor方法,而且在实例化(其实也就是创建一个新对象)的时候,也没有使用new关键字来开辟一个新的空间。这里面就有一个关于原型链的概念了,简单来说,就是:
1 | graph LR |
那照这样看来Person就是原型链的顶端了。其实不是这样,在Peroson上面还有Object,这才是原型链的顶端。所以说,完整的原型继承图应该是这样
1 | graph LR |
关于原型链的话题我们还能学习的东西还有很多,这里就先不展开讨论了。以上就是我对js的两种不同的继承方式的简单理解,至于在什么情况下用哪种方式来进行js开发完全遵循个人的喜好。喜欢传统oop的可能中意类式继承一点,觉得用原型更方便直白的,那就用后者。没什么要求。两者都可以。
小彩蛋
js中的简化类式继承的方式,因为大家可能会觉得,类式继承在初始化构建原型链的时候,实在是太太太麻烦了,这里给介绍一个十分简单的小代码,可以稍微简化下。
1 | function extend(subClass, superClass) { |
就是这个extend函数。接下来我们用第一个Person的案例来说明:
1 | function extend(subClass, superClass) { |
用extend的函数来实现原型链的建立,这样就稍微比之前的代码简洁了一点。