前端的继承
关于前端的继承,其实我一直都是有点模糊的,看了很多相关文章,等去面试的时候别人一问就混乱,所以想记录一下我自己的看法。等再次混乱的时候在打开看一下哈。
一. 原型链继承
字面上就是说从原型链上继承,也就是prototype的方法。
//定义一个 Animal 构造函数,作为 Dog 的父类
function Animal () {
this.Name= 'Animal';
}
Animal.prototype.sayName = function () {
alert(this.Name);
}
function Dog (name) {
this.name = name;
}
//改变Dog的prototype指针,指向一个 Animal 实例
Dog.prototype = new Animal();
//上面那行就相当于这么写
//var animal = new Animal();
//Dog.prototype = animal;
Dog.prototype.sayHi= function () {
alert(this.name);
}
var dog1 = new Dog('bingGan');
dog1.sayName(); //Animal
dog1.sayHi(); // bingGan
console.log(dog1.__proto__ === Dog.prototype) ; //true 对象的__proto__ 指向他的构造函数的prototype.
优点 :我们可以通过原型链的方式,实现 Dog 继承 Animal 的所有属性和方法。
缺点:容易对原型上的引用类型(array,function,object)等值进行修改。
引用类型的属性值会受到实例的影响而修改。
比如说 Animal上有一个数组arr,在新建Dog实例的时候,往原型链数组push一个元素,那么原型链上的arr 就会被改变成push之后的值。
二. 构造函数继承
在子类的构造函数中,通过 apply ( ) 或 call ( )的形式,调用父类构造函数,以实现继承。
//父类
function Parent() {
this.name= 'Parent';
this.arr = [1,2,3];
}
//子类:学生,继承了“人”这个类
function Student(age) {
this.age=age;
Parent.call(this); //改变Student的this作用域 ,使他指向Parent,从而实现继承。
}
var stu =new Student(18);
console.log(stu.arr); //[1,2,3]
stu.arr.push(4);
console.log(stu.arr); //[1,2,3,4]
var stu1 =new Student (16);
console.log(stu1.arr); //[1,2,3] //没有受到实例修改引用类型的影响。
在子类函数中,通过call ( ) 方法调用父类函数后,子类实例 stu, 可以访问到 Student 构造函数和 Parent构造函数里的所有属性和方法。这样就实现了子类向父类的继承,而且还解决了原型对象上对引用类型值的误修改操作。
优点: 实例修改引用类型,不会影响到其原型链上的值。
缺点:每个子类实例都会拷贝一份父类构造函数中的方法,作为实例自己的方法,占用内存。
三.组合继承
指的是,把上述两种方法组合在一起实现的继承。
把方法挂载到父类的原型对象上去,属性写在constructor上,实现方法复用,然后子类通过原型链继承,就能调用这些方法。
//父类
function Parent() {
this.name= 'Parent';
this.arr = [1,2,3];
}
Parent.prototype.sayHello = function (){
cosole.log(this.name) ;
}
Parent.prototype.haha = function (){
cosole.log('好开心哇') ;
}
//子类:学生,继承了“人”这个类
function Student(name) {
this.name=name;
Parent.call(this); //改变Student的this作用域 ,使他指向Parent,从而实现继承。
}
Student.prototype = new Person();//此时 Student.prototype 中的 constructor 被重写了,会导致 stu.constructor === Person
Student.prototype.constructor = Student;//将 Student 原型对象的 constructor 指针重新指向 Student 本身
var stu =new Student('rouqiu');
var stu1 =new Student('bingGan');
stu.sayHello () ; // rouqiu
stu.haha(); //好开心哇
stu1.sayHello () ; // bingGan
将 Person 类中需要复用的方法提取到 Person.prototype 中,然后设置 Student 的原型对象为 Person 类的一个实例,这样 stu 就能访问到 Person 原型对象上的属性和方法了。其次,为保证 stu和 stu1 拥有各自的父类属性副本,我们在 Student 构造函数中,还是使用了 Person.call ( this ) 方法。如此,结合原型链继承和借用构造函数继承,就完美地解决了之前这二者各自表现出来的缺点。
总结
关于原生继承有这三种方式,等我忘了我再看一遍,嘻嘻,加油!晴晴要努力哇