1、基于类的继承
下面看下面的代码:<script type="text/javascript"> function Person(name, age) { this.name = name; this.age = age; } Person.prototype.say = function () { console.log(this.name + " , " + this.age); } function Student(no) { this.no = no; } <span style="white-space:pre"> /** * Student的prototype指向Person的对象 */</span> Student.prototype = new Person(); var stu1 = new Student("0001"); stu1.name = '张三'; stu1.age = '11'; console.log(stu1.no); stu1.say(); </script>
输出结果:
0001 张三 , 11
可以看到Student成功集成了Person,并且拥有了Person的say方法,核心代码其实就是一句 Student.prototype = new Person();,下面通过图解来说明原理:
将Student.prototype指向new Person() , new Person的_proto_又指向Person Prototype;这样完成了整个继承。
但是这种方式存在问题:
问题1:当父类存在引用类型变量时,造成数据不一致,下面我们给Person添加一个hobbies属性,类型为数组。
<script type="text/javascript"> /** * 存在问题 * 1、无法在Student的构造方法中传递参数用于父类的构造方法 * 2、对于引用类型变量,造成数据不一致 */ function Person(name, age) { this.name = name; this.age = age; this.hobbies = [] ; } Person.prototype.say = function () { console.log(this.name + " , " + this.age +" , " +this.hobbies); } function Student(no) { this.no = no; } Student.prototype = new Person(); var stu1 = new Student("0001"); stu1.name = '张三'; stu1.age = '11'; stu1.hobbies.push("soccer"); stu1.say(); var stu2 = new Student("0002"); stu2.name = '李四'; stu2.age = '12'; stu2.hobbies.push("girl"); stu2.say(); </script>
输出结果:
张三 , 11 , soccer 李四 , 12 , soccer,girl可以看出,李四的hobbies应该只有girl,但是上面的代码让所有对象共享了hobbies属性。
上述的继承方式还存在一个问题:
问题2:在Student的构造方法中,无法使用new Student("00001" , "张三" , 12) ;创建对象,并初始化name和age属性,必须stu.name, stu.age进行赋值
为了解决上述问题,对上述代码进行修改:
<script type="text/javascript"> function Person(name, age) { this.name = name; this.age = age; this.hobbies = []; } Person.prototype.say = function () { console.log(this.name + " , " + this.age +" , " + this.hobbies); } function Student(name, age, no) { /** * 使用call方法,第一个参数为上下文; * 有点类似Java中的super(name,age)的感觉 */ Person.call(this, name, age); this.no = no; } Student.prototype = new Person(); var stu1 = new Student("0001","张三",11); stu1.hobbies.push("soccer"); stu1.say(); var stu2 = new Student("0002","李四",12); stu2.hobbies.push("cangjin"); stu2.hobbies.push("basketball"); stu2.say(); </script>
输出:
0001 , 张三 , soccer 0002 , 李四 , cangjin,basketball
在Student的构造方法中使用了Person.call(this,name,age)感觉就像super(name,age)【call的第一个参数为上下文】;并且成功解决了对引用属性的共享问题,完美解决。
2、基于原型链的继承
<script type="text/javascript"> /** * 基于原型链的集成中都是对象 * 存在问题: * 1、对于引用类型变量,造成数据不一致 */ var Person = { name: "人", age: 0, hobbies: [], say: function () { console.log(this.name + " , " + this.age + " , " + this.hobbies); } } ; var Student = clone(Person); Student.no =""; Student.sayHello = function() { console.log(this.name +"hello ") ; } var stu1 = clone(Student); stu1.name = "zhangsan"; stu1.age = 12; stu1.hobbies.push("Java"); stu1.say(); var stu2 = clone(Student); stu2.name = "lisi"; stu2.age = 13; stu2.hobbies.push("Javascript"); stu2.say(); /** * 返回一个prototype执行obj的一个对象 * @param obj * @returns {F} */ function clone(obj) { var F = function () { }; F.prototype = obj; return new F(); } </script>
输出:
zhangsan , 12 , Java lisi , 13 , Java,Javascript
可以看出同样存在引用属性不一致的问题,并且整个操作全部基于对象,给人的感觉不是很好,下面通过图解解释下原理:
对象间通过一个clone函数,不断的返回一个新的对象,且prototype执行传入的对象,整个继承过程其实就是_proto_不断的指向,形成一个链,所以叫做原型链。
好了,已经介绍完了,js的两种集成的方式,最好使用的还是通过类的继承(上述第一种方案,解决存在问题的)。
如果代码或者讲解存在任何问题,欢迎留言指出。