首页 > 编程语言 > 详细

JavaScript继承方式

时间:2020-03-25 10:31:19      阅读:55      评论:0      收藏:0      [点我收藏+]

一、原型链继承

子类型的原型为父类型的一个实例对象。

// 父类型
function Parent(name, age) {
  this.name = name;
  this.age = age;
  this.showName = function() {
    console.log(‘parentName‘, this.name);
  }
}
Parent.prototype.showAge = function() {
  console.log(‘parentAge‘, this.age)
}
// 子类型
function Children(sex) {
  this.sex = sex;
  this.showSex = function() {
    console.log(‘childrenSex‘, this.sex);
  }
}
// Children.prototype.sayHello = function () { }//在这里写子类的原型方法和属性是无效的,
// 因为会改变原型的指向,所以应该放到重新指定之后
Children.prototype = new Parent(); // 子类型的原型为父类型的实例对象
Children.prototype.sayHello = function () {
  console.log(‘children.sayHello‘)
} // 注:在这里写
var boy = new Children(‘boy‘);
boy.showSex(); // childrenSex boy
boy.showAge(); // parentAge undefined
boy.showName(); // parentName undefined
boy.sayHello(); // children.sayHello
console.log(‘boy‘, boy); // Children{sex:"boy", showSex:f(){}}
console.log(‘boy.sex‘, boy.sex); // boy
console.log(‘boy.name‘, boy.name); // undefined
console.log(‘boy.age‘, boy.age); // undefined

特点:

1.父类新增原型方法/原型属性,子类都能访问到

2.简单,易于实现

缺点:

1.无法实现多继承

2.来自原型对象的所有属性被所有实例共享

3.创建子类实例时,无法向父类构造函数传参

4.要想为子类新增属性和方法,必须要在Children.prototype = new Parent() 之后执行,不能放到构造器中

二、借用构造函数继承

在子类型构造函数中通用call()调用父类型构造函数

function Parent(name, age) {
  this.name = name;
  this.age = age;
  this.showName = function() {
    console.log(‘parentName‘, this.name);
  }
}
Parent.prototype.showAge = function() {
  console.log(‘parentAge‘, this.age);
}
function Children(name, age, sex) {
  Parent.call(this, name, age, sex); 
  // 相当于 this.Parent(name, age)
  // this.name = name;
  // this.age = age;
  this.sex = sex;
  this.showSex = function() {
    console.log(‘childrenSex‘, this.sex);
  }
}
Children.prototype.sayHello = function() {
  console.log(‘children.sayHello‘)
}
var boy = new Children(‘cb‘, 21, ‘boy‘)
boy.showSex(); // childrenSex boy
boy.showAge(); // boy.showAge is not a function
boy.showName(); // parentName cb
boy.sayHello(); // children.sayHello
console.log(‘boy‘, boy); // Children{age:21, name:"cb", sex:"boy",showName: f(){}, showSex:f(){}}
console.log(‘boy.sex‘, boy.sex); // boy
console.log(‘boy.name‘, boy.name); // cb
console.log(‘boy.age‘, boy.age); // 21

特点:

1.解决了原型链继承中子类实例共享父类引用属性的问题

2.创建子类实例时,可以向父类传递参数

3.可以实现多继承(call多个父类对象)

缺点:

1.实例并不是父类的实例,只是子类的实例

2.只能继承父类的实例属性和方法,不能继承原型属性和方法

3.无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

三:原型链+借用构造函数的组合继承

通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用。

function Parent(name, age) {
  this.name = name;
  this.age = age;
  this.showName = function() {
    console.log(‘parentName‘, this.name);
  }
}
Parent.prototype.showAge = function() {
  console.log(‘parentAge‘, this.age);
}
function Children(name, age, sex) {
  Parent.call(this, name, age, sex);
  this.sex = sex;
  this.showSex = function() {
    console.log(‘childrenSex‘, this.sex);
  }
}

Children.prototype = new Parent();
Children.prototype.constructor = Children; // 相比方法二 修复构造函数指向的
Children.prototype.sayHello = function() {
  console.log(‘children.sayHello‘)
}
var boy = new Children(‘cb‘, 21, ‘boy‘)
var p1 = new Parent(‘cb‘, 21)
boy.sayHello(); // children.sayHello
boy.showAge(); // parentAge 21
console.log(boy.constructor) // Children
console.log(p1.constructor) // Parent

特点:

1.可以继承实例属性/方法,也可以继承原型属性/方法

2.不存在引用属性共享问题

3.可传参

4.函数可服用

缺点:

调用了两次父类构造函数,生成两份实例

一次是在创建子类型原型的时候,另一次是在子类型构造函数的内部,子类型最终会包含父类型对象的全部实例属性,但我们不得不在调用子类构造函数时重写这些属性。

四、组合继承优化1

这种方式通过父类原型和子类原型指向同一对象,子类可以继承到父类的公有方法当做自己的公有方法,而且不会初始化两次实例方法/属性,避免的组合继承的缺点。

function Parent(name, age) {
  this.name = name;
  this.age = age;
  this.showName = function() {
    console.log(‘parentName‘, this.name);
  }
}
Parent.prototype.showAge = function() {
  console.log(‘parentAge‘, this.age);
}
function Children(name, age, sex) {
  Parent.call(this, name, age, sex);
  this.sex = sex;
  this.showSex = function() {
    console.log(‘childrenSex‘, this.sex);
  }
}
Children.prototype = Parent.prototype; // 不会初始化两次实例方法/属性 但是没办法辨别是实例是子类还是父类创造的
Children.prototype.sayHello = function() {
  console.log(‘children.sayHello‘)
}
var boy = new Children(‘cb‘, 21, ‘boy‘);
var p1 = new Parent(‘cb‘, 21)
boy.sayHello(); // children.sayHello
boy.showAge(); // parentAge 21
console.log(boy.constructor) // Parent
console.log(p1.constructor) // Parent
console.log(boy instanceof Parent) // true
console.log(boy instanceof Children) // true

特点:不会初始化两次实例方法/属性,避免的组合继承的缺点

缺点:没办法辨别是实例是子类还是父类创造的,子类和父类的构造函数指向是同一个。

五、组合继承优化2

借助原型可以基于已有的对象来创建对象, var B = Object.create(A) 以A对象为原型,生成了B对象。B继承了A的所有属性和方法。

function Parent(name, age) {
    this.name = name;
    this.age = age;
    this.showName = function() {
        console.log(‘parentName‘, this.name);
    }
}
Parent.prototype.showAge = function() {
    console.log(‘parentAge‘, this.age);
}
function Children(name, age, sex) {
    Parent.call(this, name, age, sex);
    this.sex = sex;
    this.showSex = function() {
        console.log(‘childrenSex‘, this.sex);
    }
}
Children.prototype = Object.create(Parent.prototype); // 核心代码
Children.prototype.constructor = Children; // 核心代码
Children.prototype.sayHello = function() {
    console.log(‘children.sayHello‘)
}
var boy = new Children(‘cb‘, 21, ‘boy‘);
var p1 = new Parent(‘cb‘, 21)
boy.sayHello(); // children.sayHello
boy.showAge(); // parentAge 21
console.log(boy.constructor) // Children
console.log(p1.constructor) // Parent
console.log(boy instanceof Parent) // true
console.log(boy instanceof Children) // true

特点:完美继承

六、ES6中class的继承

ES6中引入了class关键字,class可以通过extends关键字实现继承,还可以通过static关键字定义类的静态方法,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

需要注意的是,class关键字只是原型的语法糖,JavaScript继承仍然是基于原型实现的

class Parent {
  // 调用类的构造方法
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  // 定义一般的方法
  showName () {
    console.log("调用父类的方法")
    console.log(this.name, this.age);
  }
}
let p1 = new Parent(‘cb1‘, 100)
console.log(p1) // Parent {age: 100, name: "cb1"}
// 定义一个子类
class Children extends Parent {
  constructor(name, age, salary) {
    super(name, age) // 通过super调用父类的构造方法
    this.salary = salary
  }
  showName () { // 在子类自身定义方法
    console.log(this.name, this.age, this.salary);
  }
}
let s1 = new Children(‘cb2‘, 21, ‘20k‘)
console.log(s1) // Children {age: 21, name: "cb2", salary: "20k"}
s1.showName() // cb2, 21, 20k
console.log(p1.constructor)
/*
class Parent {
  // 调用类的构造方法
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  // 定义一般的方法
  showName () {
    console.log("调用父类的方法")
    console.log(this.name, this.age);
  }
}
*/
console.log(s1.constructor)
/*
class Children extends Parent {
  constructor(name, age, salary) {
    super(name, age) // 通过super调用父类的构造方法
    this.salary = salary
  }
  showName () { // 在子类自身定义方法
    console.log(this.name, this.age, this.salary);
  }
}
*/

特点:语法简单易懂,操作更方便

缺点:并不是所有的浏览器都支持class关键字

思考:Children.__proto__ === Parent ???

JavaScript继承方式

原文:https://www.cnblogs.com/chengbin1117/p/12563409.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!