共计 3645 个字符,预计需要花费 10 分钟才能阅读完成。
前戏:
js 继承是在前端面试及 js 库开发中常常遇到的问题。在面向对象开发时继承常识也是十分重要,自己浏览了一些文章总结了 js 继承的要点,心愿能对大家有所帮忙。
什么是继承?
艰深的说:某一个工程须要蓝图实现来工程,蓝图就是 类
。但在一个新工程的蓝图设计时须要复制一些老蓝图上的工程教训来晋升效率,这个过程就相当于继承。
js 继承有哪些? 一起来看看
1. 类式继承
// 父类
function SuperClass() {this.superValue = true;}
SuperClass.prototype.getSuperValue = function() {return this.superValue;}
// 子类
function SubClass() {this.subValue = false;}
SubClass.prototype = new SuperClass();
SubClass.prototype.getSubValue = function() {return this.subValue;}
var instance = new SubClass();
console.log(instance instanceof SuperClass)//true
console.log(instance instanceof SubClass)//true
console.log(SubClass instanceof SuperClass)//false
这种形式是最原始的形式,应用父类的一个实例重写到子类原型对象上,从而实现父类成员的继承,有点继承的样子,然而总感觉怪怪的,你确定把一个实例赋值到原型上真的没有问题吗?
毛病:
- 援用类型问题, 如果父类有个数组成员,只实例化一次赋值到原型上,那就是每次 new 进去的子实例都是在应用同一个数组,结果可想而知。
- 不能传参
- 重写了 prototype,如果要额定给子类原型加货色就要加在
SubClass.prototype = new SuperClass();
重写语句之后,很不不便 - instance.constructor 结构器指向父类。子类的实例结构器是父类?那不胡扯吗?
2. 构造函数继承
function SuperClass(id) {this.books = ['a','b'];
this.id = id;
}
SuperClass.prototype.showBooks = function() {console.log(this.books);
}
function SubClass(id) {
// 继承父类
SuperClass.call(this,id);
}
var instance1 = new SubClass(10);
var instance2 = new SubClass(11);
instance1.books.push('c');
console.log(instance1)
console.log(instance2)
instance1.showBooks();
比下面一个略微好了一点,也能传参了, 每次进去的数组也是惟一的了。原理是在子类结构的时候应用 call 的个性借用一下父类的构造函数,把父类的成员都设置在子类中来实现继承。额。。那原型呢?
毛病:
- 继承不了父类原型上的成员
- 每次实例化子类就会 call 一下父类,多执行了一遍
3. 组合式继承
function SuperClass(name) {
this.name = name;
this.books = ['A','B'];
}
SuperClass.prototype.getBooks = function() {console.log(this.books);
}
function SubClass(name,time) {SuperClass.call(this,name);
this.time = time;
}
SubClass.prototype = new SuperClass();
SubClass.prototype.getTime = function() {console.log(this.time);
}
还能够,能用,原理也就是联合了以上两种(构造函数、类式继承)的继承形式,并修复了重大的毛病。以前应该用这种办法的人也比拟多吧
毛病:
- call 一遍就算了又 new 了一遍 不感觉怪怪的么?call 曾经设置了一遍成员,prototype 只有父的 prototype 啊,不要把其余无关的货色搞进来。
- prototype 还是被重写了
4. 原型式继承
function inheritObject(o) {
// 申明一个过渡对象
function F() {}
// 过渡对象的原型继承父对象
F.prototype = o;
// 返回过渡对象的实例,该对象的原型继承了父对象
return new F();}
var book = {
name:'A book',
likeBook:['B Book','C book']
}
var newBook = inheritObject(book);
newBook.name = 'AA1 book';
newBook.likeBook.push('CC book');
var otherBook = inheritObject(book);
otherBook.name = 'EE book';
otherBook.likeBook.push('FF book');
console.log(newBook,otherBook);
这就像类式继承一样,间接把父的成员放到子类的原型上,通过原型链来实现继承。inheritObject 就像是 object.create()和 new 的模仿实现,产出十分污浊的对象。
黑人问号.jpg。应用这种办法意思是放弃了构造函数来实现继承?
毛病:
- 子类设置成员写在里面,设置成员的代码一堆一堆的,代码组织差。
- 每次继承都用同一个 likeBook,并没有新创建一个,结果可想而知,那还不是和第一种办法差不多,用不了
- 这算的上是一种继承么,先拿到父类的货色而后再去想子类?
5. 寄生式继承
var book = {
name:'A book',
likeBook:['B book','C book']
}
function createBook(obj) {
// 通过原型形式创立新的对象
var o = new inheritObject(obj);
// 拓展新对象
o.getName = function(name) {console.log(name)
}
// 返回拓展后的新对象
return o;
}
仍然保留了 inheritObject 函数来实现父成员的继承,相比下面的原型继承代码更有组织性了,独自封装了一个函数来实现继承,而后应用原型继承来继承对应的父类
毛病:
曾经差不多做到了继承,然而原型继承问题仍然没有解决
6. 寄生组合式继承
function inheritObject(o) {function F() { }
F.prototype = o;
return new F();}
function inheritPrototype(subClass,superClass) {
// 复制一份父类的原型正本到变量中
var p = inheritObject(superClass.prototype);
// 修改因为重写子类的原型导致子类的 constructor 属性被批改
p.constructor = subClass;
// 设置子类原型
subClass.prototype = p;
}
// 继承
function SuperClass(name) {
this.name = name;
this.books=['a book','b book'];
}
SuperClass.prototype.getName = function() {console.log(this.name);
}
function SubClass(name,time) {SuperClass.call(this,name);
this.time = time;
}
inheritPrototype(SubClass,SuperClass);
SubClass.prototype.getTime = function() {console.log(this.time);
}
var instance1 = new SubClass('f1','2017')
var instance2 = new SubClass('r2','2018');
instance1.books.push('new book');
console.log(instance1,instance2);
继承终极版。这个继承形式是在解决组合继承遇到的问题,先给子类连贯上污浊的父原型成员,而后再借用父类构造函数获取到父构造函数里的成员,根本解决下面呈现的问题。
总结
剩下没说的就是 ES6 的继承,也是相当于一个 ES6 的语法糖,源码也是基于寄生组合式继承。毛病就是对低版本的浏览器不敌对,以上总结了 6 种 js 继承形式,每一个继承形式都是修复之前继承的有余,重点是了解它们的特点和之间的差别,弄明确为什么要这样做,这样能力彻底了解 js 继承的内容。最初,心愿能对你有帮忙~