继承
子类的原型对象--继承
// 类式继承
// 声明父类
function SupperClass(){
this.superValue = true
}
// 为父类添加公有方法
SupperClass.prototype.getSuperValue = function(){
return this.superValue;
}
// 声明类
function SubClass(){
this.subValue = false;
}
// 继承父类
SubClass.prototype = new SuperClass()
// 为子类添加公有方法
SubClass.prototype.getValue = function(){
return this.subValue
}
var instance = new SubClass()
instance.getSuperValue() // true
instance.getSubValue() // false
这里声明类两个类, 而且第二个类的原型prototype被赋予了第一个类的实例
类的原型对象的作用就是为类的原型添加共有方法,但类不能直接访问这些属性和方法,必须通过原型prototype来访问而我们实例化一个父类的时候,新创建的对象复制了父类的构造函数内的属性与方法并且将原形proro指向了父类的原型对象,这样就拥有了父类的原型对象上的属性与方法,并且这个新创建的对象可直接访问到父类原型对象的属性与方法.如果我们将这个新创建的对象赋值给子类的原型, 那么子类的原型就可以访问到父类的原型属性和方法.
我们可以通过instanceof来检测某个对象是否是某个类的实例,或者说某个对象是否继承了某个类.这样就可以判断对象与类之间的继承关系了.
instanceof是通过判断对象的prototype链来确定这个对象是否是某个类的实例,而不关心对象与类的自身结构
console.log(instance instanceof SuperClass); //true
console.log(instance instanceof SubClass); //true
console.log(SubClass instanceof SuperClass); //false
instanceof 是判断前面的对象是否是后面的类(对象)的实例, 它并不表示两者的继承.
我们在实现subClass继承superClass时是通过将superClass的实例赋值给subClass的原型prorotype,所以SubClass.prototype继承了superClass
console.log(SubClass.prototype instanceof SuperClass) //true
所创建的所有对象都是Object的实例
console.log(instance instanceof Object) // true
这种类继承还有2个缺点.
一: 由于子类通过原型prototype对父类实例化,继承了父类.所以说父类中的共有属性要是引用类型,就会在子类中被所有实例共有, 因此一个子类的实例更改子类原型从父类构造函数中继承的共有属性就会影响到其它子类.
function SuperClass(){
this.books = ['js', 'html', 'css']
}
function SubClass(){}
SubClass.prototype = new SuperClass()
var instance1 = new SubClass();
var instance2 = new SubClass();
console.log(instance2.books); // ['js', 'html', 'css']
instance1.books.push('设计模式')
console.log(instance2.books); // ['js', 'html', 'css', '设计模式']
instance1 改变了instance2的值
二:由于子类实现的继承是靠原型prototype对父类的实例化实现的, 因此在创建父类的时候,是无法向父类传递参数的, 因为在实例化父类的时候也无法对父类否找函数内的属性进行初始化.
构造函数继承
// 构造函数式继承
// 声明父类
function SuperClass(id){
// 引用类型共有属性
this.books = ['js', 'css', 'html'];
// 值类型共有属性
this.id = id;
}
// 父类声明原型方法
SupperClass.prototype.showBooks = function(){
console.log(this.books)
}
// 声明子类
function subClass(id){
// 继承父类
SuperClass.call(this, id)
}
// 创建第一个子类的实例
var instance1 = new subClass(10)
// 创建第二个子类的实例
var instance2 = new subClass(1);
instance1.books.push('设计模式')
console.log(instance1.books) // ['js', 'css', 'html', '设计模式']
console.log(instance1.id) // 10
console.log(instance1.books) // ['js', 'css', 'html']
console.log(instance1.id) // 1
instance1.showBooks() // TypeErroe
SuperClass.call(this, id),call这个方法可以更改函数的作用环境,因此在子类中,对superClass调用这个方法就是将子类汇总的变量在父类中执行了一遍,由于父类中是给this绑定属性的,因此子类自然也就继承了父类的共有属性.由于这种类型的继承没有涉及原型prototype,所以父类的原型方法自然不会倍子类继承, 而如果想要被子类继承就必须放在构造函数中,这样创建出的每个实例都会单独拥有一份而不是共用,这样就违背了代码复用的原则.综合这两种模式的优点,后来就有了组合继承
组合继承
类式继承是用过子类的原型prototype对父类实例化来实现的,构造函数式继承是通过在子类的构造函数作用环境中执行一次父类的构造函数来实现的,所以只要在继承中同时做到这两点即可.
// 组合式继承
// 声明父类
function SuperClass(name){
// 值类型共有属性
this.name = name;
// 应用类型共有属性
this.books = ['js', 'css', 'html'];
}
// 父类原型共有方法
SuperClass.prototype.getName = function(){console.log(this.name)}
// 声明子类
function SubClass(name, time){
// 构造函数式继承父类name属性
SuperClass.call(this, name)
// 子类中新增共有属性
this.time = time;
}
// 类式继承 子类原型继承父类
SubClass.prototype = new SupperClass();
// 子类原型方法
SubClass.prototype.getTime = function(){console.log(this.time)}
在子类构造函数中执行父类构造函数,在子类原型上实例化父类就是组合模式,这样就融合了类式继承的优点
var instance1 = new SubClass('js book', 2014)
instance1.books.push('设计模式')
console.log(instance1.books); // ['js', 'css', 'html', '设计模式']
instance1.getName() // js book
instance1.getTiem() // 2014
var instance2 = new SubClass('css book', 2011);
console.log(instance2.books) // ['js', 'css', 'html']
instance2.getName() // css book
instance2.getTime() // 2011
这种继承模式在使用构造函数继承时执行了一遍父类的构造函数, 而在实现子类原型的类式继承时又调用了一遍父类构造函数.因此父类构造函数调用了两遍.
洁净的继承者--原型式继承
借助原型prototype可以根据已有的对象创建一个新的对象, 同时不必创建新的自定义对象类型
// 原型是继承
function inheritObject(o){
// 声明一个过渡函数对象
function F () {}
// 过渡对象的原型继承父对象
F.prototype = o;
// 返回过度对象的一个实例, 该实例的原型继承了父对象
return new F();
}
它是对类式继承的一个封装,过渡对象就相当于类式继承中的子类,只不过在原型中作为一个过渡对象出现的, 目的是为了创建要返回的新的实例化对象,由于F过渡类的构造函数中无内容, 开销比较小, 使用起来也比较方便.
var book = {
name: 'js book';
alikeBook: ['css', 'js']
}
var newBook = inheritObject(book);
newBook.name = 'ajax book';
newBook.alikeBook.push('as book')
var otherBook = inheritObject(book);
newBook.name = 'flash book';
newBook.alikeBook.push('xml book')
console.log(newBook.name); // ajax book
console.log(newBook.alikeBook); // ['css'. 'js', 'as book', 'xml book']
console.log(otherBook.name); // flash book
console.log(otherBook.alikeBook); // ['css'. 'js', 'as book', 'xml book']
console.log(book.name); // js book
console.log(book.alikeBook); // ['css'. 'js', 'as book', 'xml book']
寄生式继承
function inheritObject(o){
function F(){}
F.prototype = o;
return new F();
}
// 寄生式继承
// 声明基对象
var book = {
name: "js book",
alikeBook: ["css", "js", "html"]
};
function createBook(obj){
// 通过原型继承方式创建新对象
var o = new inheritObject(obj)
o.getName = function(){
console.log(name)
};
// 返回拓展后的新对象
return o;
}
寄生式继承就是对原型继承的第二次封装,并且在这第二次封装中对继承的对象进行拓展,这样创建的对象不仅仅有父类中的属性和方法而且还添加新的属性和方法
寄生大概值得就是像寄生虫一样寄托于某个对象内部生长。当然寄生式继承这种增强新创建对象的继承思想也是寄托于原型继承模式
终极继承者--寄生组合式继承
组合式继承将类式继承同构造函数继承组合使用,但是这种方式有一个问题,就是子类不是父类的实例,而子类的原型是父类的实例,所以才有了寄生组合式继承
寄生式寄生式继承, 寄生式继承依托于原型继承, 原型继承又与类继承相像,另一种继承模式式构造函数继承, 子类不是父类实例的问题是由于类是继承引起的
/**
* 寄生式继承 类继承
* 传递参数 subClass 子类
* 传递参数 superClass 父类
**/
function inheritObject(o){
var o = new F();
F.prototype = o;
return new F();
}
function inheritPrototype(subClass, superClass){
// 复制一份父类的原型副本保存在变量中
var p = inheritObject(superClass.prototype);
// 修正因为重写子类原型导致子类的constructor属性被修改
p.constructor = subClass;
// 设置子类的原型
subClass.prototype = p;
}
组合式继承中,通过构造函数继承的属性和方法是没问题的, 所以这里我们组要理解通过寄生式继承重新继承父类的原型。我们需要继承的仅仅是父类的原型,不再需要调用父类的构造函数,换句话说,在构造函数继承中我们已经调用了父类的构造函数,因此我们需要的就是父类的原型对象的一个副本,而这个副本我们通过原型继承便可得到,但是这么直接赋值给子类会有问题,因为父类原型对象复制得到的复制对象p中的constructor指向的不是subClass子类对象,因此寄生式继承中要对复制对象p做一次增强,修复其constructor属性指向不正确的问题,最后将得到的复制对象p复制给子类的原型,这样子类的原型就继承了父类的原型并且没有执行父类的构造函数
// 定义父类
function SuperClass(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
// 定义父类原型方法
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("js book", 2014)
var instance2 = new SubClass("css book", 2015)
console.log(instance1.colors); // ["red", "blue", "green"];
console.log(instance2.colors); // ["red", "blue", "green"];
instance2.getName(); // 'css book'
instance2.getTime(); // '2015'
多继承
在JS中继承是依赖于原型prototype链实现的, 只有一条原型链, 所以理论不能继承多个父类
extend
// 单继承 属性赋值
var extend = function(target, source){
// 遍历源对象的属性
for(var property in source){
// 将源对象中的属性复制到目标对象中
targer[property] = source[property]
}
// 返回目标对象
return target;
}
var book = {
name: 'js',
alike: ['css', 'js', 'html']
}
var anotherBook = {
color: 'red'
}
extend(anotherBook, book)
console.log(anotherBook.name) // js
console.log(anotherBook.alike) // ['css', 'js', 'html']
anotherBook.alike.push('ajax');
anotherBook.name = '设计模式';
console.log(anotherBook.name); //设计模式
console.log(anotherBook.alike); // ['css', 'js', 'html', 'ajax']
console.log(book.name); // js
console.log(book.alike); // ['css', 'js', 'html', 'ajax']
多继承
// 多继承 属性复制
var mix = fuction(){
var i = 1,
len = arguments.length,
target = arguments[0],
arg;
// 遍历被继承的对象
for (; i < len; i++){
// 缓存当前对象
arg = argument[i];
// 遍历被缓存的对象中的属性
for (var property in arg){
// 将被缓存对象中的属性复制到新目标中
target[property] = arg[property]
}
}
return target;
}
绑定到Object上
Object.prototype.mix = function(){
var i = 0,
len = arguments.length,
arg;
// 遍历被继承的对象
for (; i < len; i++){
// 缓存当前对象
arg = argument[i];
// 遍历被缓存的对象中的属性
for (var property in arg){
// 将被缓存对象中的属性复制到新目标中
this[property] = arg[property]
}
}
}
otherBook.mix(book1, book2);
多态
多态是同一个方法多用调用方式
// 多态
// 定义一个方法, 如果不传参数返回10, 如果传一个参数返回10+参数 如果传两个参数就返回两个参数相加的结果
function add(){
// 获取参数
var arg = arguments,
len = arg.length;
switch(len){
case 0:
return 10;
case 1:
return 10 + arg[0];
case 2:
return arg[0] + arg[1];
}
}
网友评论