本文共 4044 字,大约阅读时间需要 13 分钟。
答案:
原型继承
构造继承
组合继承(组合原型+构造)
实例继承
寄生组合继承
ES6 使用class extends继承
详细解析:
首先定义 父类Animal
//父类function Animal(name){ this.name = name; this.sum = function() { alert(this.name); }}Animal.prototype.age = 10; //父类原型属性
一、原型链继承:子类的原型等于父类的实例
instanceof 判断对象的具体类型(判断元素是否在另一个元素的原型链上),这里 dog1 继承了 父类Animal 的属性,返回true。
实例是子类的实例,也是父类的实例。
//原型链实现继承function Dog() { this.name = "xu";}Dog.prototype = new Animal(); //关键语句,子类型的原型指向父类型的实例// 这里实例化一个 Animal 时, 实际上执行了两步// 1,新创建的对象复制了父类构造函数内的所有属性及方法// 2,并将原型 __proto__ 指向了父类的原型对象var dog1 = new Dog();console.log(dog1.age); //10console.log(dog1 instanceof Animal); //trueconsole.log(dog1 instanceof Dog); //true
结果:
特点:
缺点:
二、构造函数继承:用 .call() 在子类的构造函数内部调用父类构造函数
实例并不是父类的实例,只是子类的实例,这里具体为 cat1 不是 父类Animal 的实例,因此返回false。
相对于原型链而言,借用构造函数有一个很大的优势,即可以在子类的构造函数中向父类构造函数传递参数。
//构造函数实现继承function Cat() { //调用 Animal 构造函数 Animal.call(this, "animalType"); //关键语句,使用call()方法继承父类构造函数中的属性 //为了保证父类的构造函数不会重写子类的属性,需要在调用父类构造函数后,定义子类的属性 this.age = "5"; //子类属性}//子类实例var cat1 = new Cat();console.log(cat1.name); //animalTypeconsole.log(cat1.age); //5console.log(cat1 instanceof Animal); //falseconsole.log(cat1 instanceof Cat); //true
结果:
特点:
缺点:
三、组合继承:结合了(原型链+构造)两种模式的优点,传参和复用
主要思想:使用原型继承使用对原型属性和方法的继承,通过构造函数继承来实现对实例属性的继承。这样既能通过在原型上定义方法实现函数复用,又能保证每个实例都有自己的属性。
通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用。
//组合继承function Cat(name) { Animal.call(this, name); //构造继承 ----第二次调用 父类Animal----}// ----第一次调用 父类Animal----Cat.prototype = new Animal(); //原型继承Cat.prototype.constructor = Cat; //组合继承需要修复构造函数的指向var cat = new Cat("miao");console.log(cat.name); //miao 继承了构造函数属性console.log(cat.age); //10 继承了父类原型的属性console.log(cat instanceof Animal); //trueconsole.log(cat instanceof Cat); //true
结果:
特点:
缺点:
调用了两次父类构造函数(耗内存),生成了两份实例。子类的构造函数会代替原型上的那个父类构造函数。
四、实例继承:为父类实例添加新特性,作为子类实例返回
实例是父类的实例,不是子类的实例,这里具体为 cat1 不是 子类Cat 的实例,因此返回false
//实例继承核心:为父类实例添加新特性,作为子类实例返回function Cat(name) { var instance = new Animal(); instance.name = 'cc'; return instance;}var cat = new Cat();console.log(cat.name); //ccconsole.log(cat instanceof Animal); //trueconsole.log(cat instanceof Cat); //false
结果:
特点:
缺点:
五、寄生组合继承
核心:在组合继承中,调用了两次父类构造函数,这里 通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点。
主要思想:借用 构造函数 继承 属性 ,通过 原型链的混成形式 来继承 方法。
// 父类function SuperType (name) { this.colors = ["red", "blue", "green"]; this.name = name; // 父类属性}SuperType.prototype.sayName = function () { // 父类原型方法 return this.name;};// 子类function SubType (name, subName) { // 调用 SuperType 构造函数 SuperType.call(this, name); // ----第二次调用 SuperType,继承实例属性---- this.subName = subName;};// ----第一次调用 SuperType,继承原型属性----SubType.prototype = Object.create(SuperType.prototype)SubType.prototype.constructor = SubType; // 注意:增强对象let instance = new SubType('An', 'sisterAn')
六、ES6 使用 class extends 继承
//ES6 class extends 继承class Person{ constructor(name, age){ //constructor构造函数 this.name=name; this.age=age; } show(){ console.log(this.name); console.log(this.age); }}class Worker extends Person{ //子类继承父类 constructor(name, age, job){ super(name, age); this.job=job; } showJob(){ console.log(this.job); }}let worker1 = new Worker('jia', 21, '班长');worker1.show();worker1.showJob();
结果:
通过class创建对象看起来就比较简洁,不用通过原型去实现继承。
在class的继承中,子类的_proto_属性表示构造函数的继承,指向父类,子类prototype属性的_proto_属性,表示方法的继承,总是指向父类的prototype属性。
class调用必须通过new 关键字。
class中的static 属性:用于定义类中的静态方法和静态属性关键字。
该方式声明的方法与属性,只能通过类名调用,可被继承,并且定义方法的 this 指向类 而不是实例。ES中的function 可以用call apply bind 的方式 来改变他的执行上下文,但是 class 不可以 。
转载地址:http://njuvi.baihongyu.com/