什么是继承?
继承就是你爸爸不少钱,因此你就继承你爸爸,变成了富二代,也是个有钱的主,忽然你爸爸世界杯赌球,输了个精光,因而你也变成了穷光蛋。这个就是继承
非也,非也。java
C# 继承jquery
public class A { private int a; private int b; } public class B :A { private int c; private int b; } //B 继承 A
ES6 继承es6
export default class A { constructor(props){ super(props) } test() { alert('test') } } export default class B extends A { constructor(props){ super(props) } test1() { this.test() } } //B 继承 A
要用 ECMAScript 实现继承机制,您能够从要继承的基类入手。全部开发者定义的类均可做为基类。出于安全缘由,本地类和宿主类不能做为基类,这样能够防止公用访问编译过的浏览器级的代码,由于这些代码能够被用于恶意攻击。
选定基类后,就能够建立它的子类了。是否使用基类彻底由你决定。有时,你可能想建立一个不能直接使用的基类,它只是用于给子类提供通用的函数。在这种状况下,基类被看做抽象类。浏览器
尽管 ECMAScript 并无像其余语言那样严格地定义抽象类,但有时它的确会建立一些不容许使用的类。一般,咱们称这种类为抽象类。安全
建立的子类将继承超类的全部属性和方法,包括构造函数及方法的实现。记住,全部属性和方法都是公用的,所以子类可直接访问这些方法。子类还可添加超类中没有的新属性和方法,也能够覆盖超类的属性和方法。数据结构
和其余功能同样,ECMAScript 实现继承的方式不止一种。这是由于 JavaScript 中的继承机制并非明确规定的,而是经过模仿实现的。这意味着全部的继承细节并不是彻底由解释程序处理。做为开发者,你有权决定最适用的继承方式。app
所谓"构造函数",其实就是一个普通函数,可是内部使用了this变量。对构造函数使用new运算符,就能生成实例,而且this变量会绑定在实例对象上。函数
原理:构造函数使用 this 关键字给全部属性和方法赋值(即采用类声明的构造函数方式)。由于构造函数只是一个函数,因此可以使 ClassA 构造函数成为 ClassB 的方法,而后调用它。ClassB 就会收到 ClassA 的构造函数中定义的属性和方法。this
function ClassA(name){ this.name = name; this.say = function(){ console.log(this.name) } } function ClassB(name){ this.newSay = ClassA this.newSay(name) delete this.newSay; }
为ClassA赋予了newSay方法(函数名只是指向他的指针),而后调用该方法,传递他的是ClassB构造函数的参数name,全部新属性和新方法都必须在新方法的代码行后定义,不然,可能回覆盖超类的相关属性和方法。es5
运行调用:
var a = new ClassA('one') var b = new ClassB('two') a.say() //输出 'one' b.say() //输出 'two'
function ClassA(name){ this.name1 = name; this.say = function(){ console.log(this.name1) } } function ClassB(name){ this.name2 = name; this.method = function(){ console.log(this.name2) //注意这里的变量name2 不能和ClassA里都变量相同,不然回覆盖掉ClassA的值 } } function ClassC(name1,name2){ this.newSay = ClassA this.newSay(name1) delete this.newSay; this.newMethod = ClassB this.newMethod(name2) delete this.newMethod } // test var a = new ClassA('one') var b = new ClassB('two') var c = new ClassC('three','four') a.say() // 输出 'one' b.method() // 输出 'two' c.say() // 输出 'three' c.method() // 输出 'four'
弊端:若是存在两个类 ClassA 和 ClassB 具备同名的属性或方法,ClassB 具备高优先级。由于它从后面的类继承。除这点小问题以外,用对象冒充实现多重继承机制垂手可得。
因为这种继承方法的流行,ECMAScript 的第三版为 Function 对象加入了两个方法,即 call() 和 apply()。
call()
function ClassA(name){ this.name = name; this.say = function(){ console.log(this.name) } } function ClassB(name){ ClassA.call(this,name) //让ClassA里面的this等于新建立的ClassB对象 } //test var a = new ClassA('one') var b = new ClassB('two') a.say() //输出 'one' b.say() //输出 'two'
apply()
function ClassA(name){ this.name = name; this.say = function(){ console.log(this.name) } } function ClassB(name){ ClassA.apply(this,arguments) //把 ClassB 的整个 arguments 对象做为第二个参数传递给 apply() 方法 } //test var a = new ClassA('one') var b = new ClassB('two') a.say() //输出 'one' b.say() //输出 'two'
缺点:没法继承原型链上的属性和方法
原理:继承这种形式在 ECMAScript 中本来是用于原型链的,prototype 对象是个模板,要实例化的对象都以这个模板为基础,prototype 对象的任何属性和方法都被传递给那个类的全部实例。原型链利用这种功能来实现继承机制。
与对象冒充类似,子类的全部属性和方法都必须出如今 prototype 属性被赋值后,由于在它以前赋值的全部方法都会被删除。为何?由于 prototype 属性被替换成了新对象,添加了新方法的原始对象将被销毁。
注意:调用 ClassA 的构造函数,没有给它传递参数。这在原型链中是标准作法。要确保构造函数没有任何参数。
function ClassA(){ } ClassA.prototype.name = 'chuchur' ClassA.prototype.say = function(){ console.log(this.name) } function ClassB(){} /** * 彻底删除了prototype对象原先的值,而后赋予一个新的值。 * 此时 Cat.prototype.constructor == Animal ==>true */ ClassB.prototype = new ClassA() //注意下面一行代码的意思 /** * 任何一个prototype对象都有一个constructor属性,指向它的构造函数 * 没有ClassB.prototype = new ClassA(),那么ClassB.prototype.constructor是指向ClassB,加了以后指向ClassA * 更重要的是,每个实例也有一个constructor属性,默认调用prototype对象的constructor属性。 * 因此 (new ClassB()).constructor == ClassA ==>true, * 这显然会致使继承链的紊乱,所以咱们必须手动纠正,将ClassB.prototype对象的constructor值改成ClassB, * 当每次改变prototype的值时,必然要复原construcotr */ ClassB.prototype.constructor = ClassB ClassB.prototype.name = '' ClassB.prototype.method = function(){ console.log(this.name) } //test var a = new ClassA() var b = new ClassB() a.name = 'one' b.name = 'two' //这里不从新复制,则name的取值为chuchur,也就是初始化值 a.say() // 输出 one b.say() // 输出 two b.method() // 输出 two
缺点:原型链的弊端是不支持多重继承。记住,原型链会用另外一类型的对象重写类的 prototype 属性。
这种继承方式使用构造函数定义类,并不是使用任何原型。对象冒充的主要问题是必须使用构造函数方式,这不是最好的选择。不过若是使用原型链,就没法使用带参数的构造函数了。
建立类的最好方式是 用构造函数定义属性,用原型定义方法。这种方式一样适用于继承机制,用对象冒充继承构造函数的属性,用原型链继承 prototype 对象的方法。
function ClassA(mail){ this.mail = mail } ClassA.prototype.sayMail = function(){ console.log(this.mail) } function ClassB(mail,name){ ClassA.call(this,mail) this.name = name } ClassB.prototype = new ClassA() ClassB.prototype.constructor = ClassB ClassB.prototype.sayName = function(){ console.log(this.name) } //test var a = new ClassA('chuchur@qq.com') var b = new ClassB('dev@chuchur.com','chuchur') a.sayMail() //输出 chuchur@qq.com b.sayMail() //输出 dev@chuchur.com b.sayName() //输出 chuchur
class A { sayName(){ console.log('chuchur') } } class B extends A { constructor() { super(); } }
ES5 的继承,实质是先创造子类的实例对象this,而后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制彻底不一样,实质是先将父类实例对象的属性和方法,加到this上面(因此必须先调用super方法),而后再用子类的构造函数修饰this,使得父类的全部行为均可以继承。
super做为函数调用时,表明父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。
注意,super虽然表明了父类A的构造函数,可是返回的是子类B的实例,即super内部的this指的是B,所以super()在这里至关于A.prototype.constructor.call(this)。
super还能够被看成对象使用,这时,super在普通方法之中,指向A.prototype,因此super.sayName()就至关于A.prototype.sayName()。
class A { sayName(){ return 'chuchur' } } class B extends A { constructor() { super(); console.log(super.sayName()) } }
ES6 容许继承原生构造函数定义子类,能够自定义原生数据结构(好比Array、String等)的子类,这是 ES5 没法作到的。extends关键字不只能够用来继承类,还能够用来继承原生的构造函数。所以能够在原生数据结构的基础上,定义本身的数据结构.
class SuperArray extends Array { constructor() { super(); this.history = [[]]; } commit() { this.history.push(this.slice()); } revert() { this.splice(0, this.length, ...this.history[this.history.length - 1]); } } var x = new SuperArray(); x.push(1); x.push(2); x // [1, 2] x.history // [[]] x.commit(); x.history // [[], [1, 2]]
浏览器的 ES5 实现之中,每个对象都有__proto__属性,指向对应的构造函数的prototype属性。Class 做为构造函数的语法糖,同时有prototype属性和__proto__属性,所以同时存在两条继承链。
(1)子类的__proto__属性,表示构造函数的继承,老是指向父类。
(2)子类prototype属性的__proto__属性,表示方法的继承,老是指向父类的prototype属性。
class A { } class B extends A { } B.__proto__ === A // true B.prototype.__proto__ === A.prototype // true
上面代码中,子类B的__proto__属性指向父类A,子类B的prototype属性的__proto__属性指向父类A的prototype属性。
用图形来表示
ES5继承
ES6继承
请转到这篇文章:js-继承-jquery