说到面向对象继承就要扯到原型链了,因为ECMAScript实现继承的主要方法就是原型链,JavaScript高级程序设计一书中这样写到:继承基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
1、原型链继承
1 function Person(name,sex){ 2 this.name = name; 3 this.sex = sex; 4 } 5 Person.prototype.play = function(){ 6 console.log("running"); 7 } 8 function Student(age){ 9 this.age = age; 10 } 11 Student.prototype = new Person("小花","女");
上面代码通过将实例化的Person赋给Student.prototype来实现继承,现在原来存在于Person实例中的所有属性和方法现在也在Student.prototype中了。现在来实例化一个Student对象来访问继承来的name属性,可以打印出来"小花"
var Dave = new Student(); console.log(Dave.name);//小花
2、call、play方法实现继承
之所以把这两种方法写在一块是因为他们区别就在于第二个参数的问题
上面的代码Person类不变,改变一下Student类
function Student(name,age,sex){ // call,apply方法的第一个参数都是要传入给当前对象的对象,后面的参数都是传递给当前对象的参数 //Person.call(this,name,sex); Person.apply(this,[name,sex]); this.age = age; } var Dave = new Student("Dave",19,"男"); console.log(Dave.name);//Dave
通过上面代码可以看到call方法第二个参数作为call的参数传入,而apply得第二个参数必须是数组或者arguments(有关于arguments的知识这里不做展开,附上链接可以查看)传入。
现在对Person.call(this,name,sex)进行分析:new Student("Dave",19,"男")之后,Student里面的this关键字指向了Student的实例Dave,而Person.call(this,name,sex)会执行Person方法,并传入name,sex作为参数。Person中的this会被call(this里的this->Student的实例Dave替换掉),所以Person里的this.name=name可以写成Dave.name=name.
若是打印Dave.play()则会报错,说明call和apply方法只能继承构造函数的属性和方法,不能继承构造函数的prototype属性添加的方法
3、组合继承
这种方法就是前面两种继承方法的组合体。
Person类还是保持不变,第二种方法的Student类也保持不变
1 Student.prototype = new Person(); 2 //更改Student实例化对象原型的constructor属性的指向 3 Student.prototype.constructor = Student; 4 var Dave = new Student("Dave",19,"男"); 5 console.log(Dave.age);//19 6 Dave.play();//running
这种方法将上面两种方法融合在一起,避免了原型链、call和apply方法的缺陷。所以组合继承方法也是JS最常用的对象继承方法。
还有很多继承方法,这里只介绍了最常用的三种有不足之处欢迎指正。。。