将子类中的共性代码 ( 属性和方法 ) 抽取出来 放到父类中 每当有一个新的子类需要用到共性的属性或者方法时 不需要在自己内容复写一遍 只需要继承父类的代码
实现代码复用 共性代码不需要重写 只需要继承父类即可
提高了代码可维护性 (同时也是缺点) 父类中共性代码改变 子类跟随改变 子类也可以在自己的类中重写共性代码完成自己需求
继承是多态的前提条件 它使得类与类之间产生了联系
违反了开发原则(高内聚 低耦合) 类的耦合性增高了
耦合: 类与类之间的联系
内聚: 类独自完成某件事的能力
注意: 理解new和this是理解继承的前提
使用call方法改变父类中的this指向到子类上
function Fn(name1) { this.name = name1; } function FnSon(name2) { Fn.call(this, name2); //如果子类中有父类同名属性应写在call下面 } const fs = new FnSon('bob'); console.log(fs); // FnSon {name: 'bob'}
特点
写法简易
易于做多继承
只能继承构造函数内的属性和方法 无法继承构造函数原型上面的属性和方法
将父类原型Prototype深拷贝给子类原型Prototype
// 创建父类构造函数 function Parent(n){ this.name = n; } Parent.prototype.show = function(){ console.log(this.name); } // 创建子类构造函数 function Child() {} //将父类Prototype内容深拷贝给子类Prototype Child.prototype = { ...Parent.prototype, constructor: Child //用这种方法继承会缺失constructor 要自己指明 } Child.prototype.show = function() { console.log('hello'); } // 重写父类中的show方法
特点
只能继承父类构造函数原型上属性和方法 不能继承构造函数内的属性和方法
可以实现多继承
将子类构造函数的Prototype指向父类实例对象的__proto__
function Parent(n){ this.name = n } Parent.prototype.show = function(){ console.log(this.name) } function Child() {} // 将父类的实例对象的__proto__给子类的原型 // 如果父类有参数要传 必须在原型链继承时候就给上实参(不实用) Child.prototype = new Parent('hello show'); // 在继承之前:子类构造函数的Prototype 指向 子类构造函数的Prototype // 完成继承之后:子类构造函数的Prototype 指向 父类实例对象的__proto__ 指向 父类构造函数的Prototype
注意: 如果把子类构造函数的Prototype直接指向父类构造函数的Prototype(浅拷贝) 那子类重写父类方法的时 父类跟随一起改变
特点
既能继承构造函数内的属性和方法,又能继承原型上的属性和方法
因为在继承时候就要处理参数 所以不实用
不适合多继承 (原型链多继承需要用B继承A 再用C继承B 这样就可以达到多继承(C继承A和B)目的 但是会增加原型链层级)
构造继承 + 原型继承/原型链继承
这种方法结合了单个方法的优点
// 构造继承 + 原型继承 function Parent(n){ this.name = n } Parent.prototype.show = function(){ console.log(this.name) } function Child(n){ // 用构造继承继承父类构造函数中属性和方法 // 弥补原型继承无法继承构造函数中属性和方法的缺点 Parent.call(this, n); } // 用原型继承能继承父类原型中属性和方法优势 // 弥补构造继承无法继承父类原型中属性和方法的缺点 Child.prototype = { ...Parent.prototype, constructor: Child //依旧缺少constructor 要自己指明 //constructor 是用来标记当前Prototype所属函数 }
// 构造继承 + 原型链继承 function Parent(n){ this.name = n } Parent.prototype.show = function(){ console.log(this.name) } // 用构造继承弥补了原型链继承必须在继承时就传参的缺点 function Child(n){ Parent.call(this, n); } // 用原型链继承能继承父类原型中属性和方法优势 // 弥补构造继承无法继承父类原型中属性和方法的缺点 Child.prototype = new Parent(); Child.prototype.constructor = Child; //缺少constructor 要自己指明 //constructor 是用来标记当前Prototype所属函数
使用extends关键字和super(形参)来达到继承
// ES6创建构造函数 class Parent{ constructor(name, age){ this.name = name; this.age = age; } show(){ console.log(this.name) } } // ES6extends关键字 class Child extends Parent{ constructor(n, a){ // 父类又称超类 // super 代表了父类构造函数 // 但是返回的是子类实例 // 即 super 内部的 this 指的是子类实例 // 因此 super(n, a) 在这里相当于 A.prototype.constructor.call(this, n, a) // n a对应了父类中的name age super(n, a); } // 重写父类中方法 show(){ console.log("重写父类show方法") } }
注意
实例对象 instanceof 构造函数
function Parent(){} function Child(){} // Child原型链继承parent Child.prototype = new Parent(); const p = new Parent(); const c = new Child(); console.log( p instanceof Parent ); // true console.log( c instanceof Child ); // true console.log( c instanceof Parent ); // true Child继承Parent console.log( p instanceof Child ); // false Parent并没有继承Child
构造函数.prototype.isPrototypeOf(实例对象)
function Parent(){} function Child(){} // Child原型链继承parent Child.prototype = new Parent(); const p = new Parent(); const c = new Child(); console.log( Parent.prototype.isPrototypeOf( p ) ); // true console.log( Child.prototype.isPrototypeOf( c ) ); // true console.log( Parent.prototype.isPrototypeOf( c ) ); // true Child继承Parent console.log( Child.prototype.isPrototypeOf( p ) ); // false Parent并没有继承Child
只要实例对象和构造函数存在关系 无论中间隔多少层都 返回true 否则返回false
可以利用这两种方法区分对象 数组 Set Map等
let arr = []; let obj = {}; console.log(arr instanceof Array) // true console.log(Array.prototype.isPrototypeOf(arr)) // true console.log(pbj instanceof Array) // false console.log(Array.prototype.isPrototypeOf(obj)) // false
Object.prototype.toString.call()
Object.prototype.toString.call("hello") // [object String]
注意
用instanceof和isPrototypeOf()不能检测undefined和null
这两种数据只能用万能检测法