js之继承
js中的继承有多种方式,因为其不像java一样有可以直接类继承(es6 中扩展了extend),他用的是原型链继承
首先我们来构造一个动物的父类
function Animal (name = 'Animal'){ this.name = name; this.play = function(){ console.log( this.name+"can play"); } } // 定义了name 属性和play方法 Animal.prototype.say = function(){ console.log("hello,i can speak Animal language") } // 在原型链上挂载了 say 方法
在该父类中我们有一个play的实例私有方法和一个say方法,该方法为原型链共享方法,所有的实例都可以修改该方法。
1.原型继承,将子类的原型指向父类的实例,这样子类的实例就可以通过原型链去继承父类的方法以及属性
function Cat(){} Cat.prototype = new Animal(); Cat.prototype.name = 'lowcat'; var cat = new Cat(); console.log(cat.name); //输出lowcat console.log(cat.say()); //输出 hello, i can speak Animal language console.log(cat.play()); //输出 lowcat can play console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); // true
优点 1. 非常纯粹的继承关系 实例是子类的实例 也是父类的实例
2. 父类新增原型方法/原型属性 子类都能访问到
3. 简单
缺点 1.要想为子类新增原型属性和方法,必需要在new Animal()语句执行之后 (因为先要把原型指向搞清楚)
2.无法实现多继承
3.来自原型的所有属性被所有实例共享
4.创建子类实例时,无法想父类构造函数传参(因为直接执行父类的构造函数)
2.构造继承(使用call函数改变当亲父类的上下文)
function Dog(name = 'pony'){ Animal.call(this); this.name = name ; }
var dog = new Dog("tony"); console.log(dog.name); //输出tony console.log(dog.play());// 输出 i can play
console.log(dog.say()); 输出错误 console.log(dog instanceof Animal); //false console.log(dog instanceof Dog); // true
优点 :1. 解决了原型继承中所有子类实例共享父类的属性与方法
2. 创建子类实例是,可以向父类传递参数
3. 实现了多继承
缺点 :1.实例斌不是父类的实例,只是子类的实例
2.只能继承父类的实例属性和方法,不能继承原型属性方法
3.无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
此继承方式使用了call函数 改变当前子类构造函数的运行环境,所有子类能够访问到父类的方法,但却不能访问原型链,因为在原型链上并没有实质的改变;
三、工厂模式继承(内部使用obj直接作为父类的实例,然后return 回去当作子类的实例)
function Fish (name = "fish") { var obj = new Animal(); obj.name = name; return obj; }
var fish = new Fish("fish"); console.log(fish.name); //输出 fish console.log(fish.play()); // 输出 i can play console.log(fish.say()); // 输出hello ,i can speak Animal lanvuage console.log(fish instanceof Animal);// true console.log(fish instanceof Fish); //true
优点: 1.不限制调用方式,不管是new 还是直接调用 返回的对象具有相同的效果
缺点:1.实例是父类的实例,不是子类的实例
2.不支持多继承
四、拷贝继承
function Mouse(name ="mouse"){ var animal = new Animal(); for(var p in animal){ Mouse.prototype[p] = animal[p]; } Mouse.prototype.name = name; }
var mouse = new Mouse("Jack"); console.log(mouse.name); // Jack console.log(mouse.play()); // i can play console.log(mouse.say()); // hello, i can speak Animal language console.log(mouse instanceof Animal); //ture console.log(mouse instanceof Mouse) // ture
优点 1.支持多继承 (多复制几个父类)
缺点 1.效率低,用了遍历copy(内存占用高)
2.无法获取父类的不可枚举方法(在对象中的atrributes object(属性描述对象)可以设置对象属性的状态)
五、寄生组合继承(首先将父类的上下文拿到子类当中,在将子类的原型指向父类的原型,这里并不是直接指向)
function Bird(name ="bird"){ Animal.call(this); this.name = name; } (function(){ var Obj = function(){}; // 首先创建一个空对象 Obj.prototype = Animal.prototype; // 将空对象的原型指向父类的原型 Bird.prototype = new Obj(); // 将子类的原型指向 空对象的实例,见解继承了父类的原型 Bird.prototype.constructor = Child; // 因为此时obj的原型的constrcutor指向了obj的构造函数,手动修复指向 })(); var bird = new Bird(); console.log(bird.name); console.log(bird.play()); console.log(bird.say()); console.log(bird instanceof Animal); console.log(bird instanceof Bird);
这个方法十分完美,解决了组合继承的两次实例化问题