JavaScript常见的六种继承方式
方式一、原型链继承
子类型的原型为父类型的一个实例对象
//父类型
function Person(name,age){
this.name = name,
this.age = age,
this.play = ['学习','追剧','做饭'],
this.setName = function(){
}
}
Person.prototype.setAge = function(){
}
// 子类型
function Student(price){
this.price = price,
this.setScore = function(){
}
}
// Student.prototype.sayHello = function () { }//在这里写子类的原型方法和属性是无效的,
//因为会改变原型的指向,所以应该放到重新指定之后
Student.prototype = new Person();//子类型的原型为夫类型的一个实例对象
// Student.prototype.sayHello = function () { }
var s1 = new Student(15000)
var s2 = new Student(14000)
s1.play.push('电风扇')
s1.name = 'wj'
// s1中play属性发生变化,与此同时,s2中play属性也会跟着变化
console.log(s1,s2,s1.play,s2.play,s1.name)
方式二: 借用构造函数继承
在子类型构造函数中通用call()调用父类型构造函数
//父类型
function Person(name,age){
this.name = name,
this.age = age,
this.play = ['学习','追剧','做饭'],
this.setName = function(){}
}
// 子类型
Person.prototype.setAge = function(){}
function Student(name,age,price){
Person.call(this,name,age);//相当于 this.Person(name,age)
// this.name = name
// this.age = age
this.price = price
}
var s1 = new Student('Tom',20,15000)
console.log('s1',s1)
console.log(s1.setAge())
//Uncaught TypeError: s1.setAge is not a function
注:只能继承父类的属性和方法,不能继承父类原型的熟悉和方法
方式三: 原型链+借用构造函数的组合继承
通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用。
function Person(name, age) {
this.name = name,
this.age = age,
this.setAge = function () {console.log('1111') }
}
Person.prototype.setBB = function () {
console.log("111")
}
function Student(name, age, price) {
Person.call(this,name,age)
this.price = price
this.setScore = function () { }
}
Student.prototype = new Person()
Student.prototype.constructor = Student//组合继承也是需要修复构造函数指向的
Student.prototype.sayHello = function () { }
var s1 = new Student('Tom', 20, 15000)
var s2 = new Student('Jack', 22, 14000)
var p1 = new Person('wj',11)
console.log(s1,p1)
console.log(s1.constructor) //Student
console.log(p1.constructor) //Person
方式四: 组合继承优化1
通过父类原型和子类原型指向同一对象,子类可以继承到父类的公有方法当做自己的公有方法,而且不会初始化两次实例方法/属性,避免的组合继承的缺点。
function Person(name, age) {
this.name = name,
this.age = age,
this.setAge = function () { }
}
Person.prototype.setBB = function () {
console.log("111")
}
function Student(name, age, price) {
Person.call(this, name, age)
this.price = price
this.setScore = function () { }
}
Student.prototype = Person.prototype
Student.prototype.sayHello = function () { }
var s1 = new Student('Tom', 20, 15000)
console.log(s1)
// 没办法辨别是对象是子类还是父类实例化
方式五: 组合继承优化2
借助原型可以基于已有的对象来创建对象,var B = Object.create(A)以A对象为原型,生成了B对象。B继承了A的所有属性和方法。
function Person(name, age) {
this.name = name,
this.age = age
this.setPaly = function(){}
}
Person.prototype.setAge = function () {
console.log("111")
}
function Student(name, age, price) {
Person.call(this, name, age)
this.price = price
this.setScore = function () {}
}
Student.prototype = Object.create(Person.prototype)//核心代码
Student.prototype.constructor = Student//核心代码
var s1 = new Student('Tom', 20, 15000)
console.log(s1 instanceof Student, s1 instanceof Person) // true true
console.log(s1.constructor) //Student
console.log(s1)
// 目前来说,最完美的继承方法!
注:Student继承了所有的Person原型对象的属性和方法。目前来说,最完美的继承方法!
方式六:ES6中class 的继承
ES6中引入了class关键字,class可以通过extends关键字实现继承,还可以通过static关键字定义类的静态方法,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
需要注意的是,class关键字只是原型的语法糖,JavaScript继承仍然是基于原型实现的。
// S6中class 的继承
class Person {
//调用类的构造方法
constructor(name, age) {
this.name = name
this.age = age
}
//定义一般的方法
showName1() {
console.log("调用父类的方法")
console.log(this.name, this.age);
}
}
let p1 = new Person('kobe', 39)
console.log(p1)
//定义一个子类
class Student extends Person {
constructor(name, age, salary) {
super(name, age)//通过super调用父类的构造方法
this.salary = salary
}
showName2() {//在子类自身定义方法
console.log("调用子类的方法")
console.log(this.name, this.age, this.salary);
}
}
let s1 = new Student('wade', 38, 1000000000)
console.log(s1)
s1.showName2()
// 并不是所有的浏览器都支持class关键字
注:并不是所有的浏览器都支持class关键字
未来的我会感谢现在努力的自己。