人狠话不多,直接进入正题
如何实现一个类
在ES5中可以使用构造函数实现一个类。
什么是构造函数?通过new来实例化对象的函数叫做构造函数,定义时首字母大写。
下面代码声明了一个构造函数Animal,通过new实例化了一个对象animal,它拥有name属性和sayName方法。
// ES5
function Animal(name) {
this.name = name
this.sayName = function () {
console.log(this.name)
}
}
var animal = new Animal('dog')
animal.sayName() // dog
但这里有一个缺点,就是每个实例对象都会拥有各自的sayName方法,无法重用,这有点资源浪费了。对于这个可以重用的方法,我们可以把它放在Animal这个类原型对象上面。(这里涉及到原型链的知识点,后面补充)
//ES5
function Animal(name) {
this.name = name
}
Animal.prototype.sayName = function () {
console.log(this.name)
}
var animal = new Animal('dog')
animal.sayName() // dog
console.log(animal)
var animal2 = new Animal('cat')
animal2.sayName() // cat
console.log(animal2)
可以对比下两种写法的不同之处
这里有一个重要的知识点
animal.__proto__ === Animal.prototype
实例对象和类是通过原型链相关联的
在ES6中引入了Class(类)这个概念,可以把它看做是一个语法糖。
// ES6
class Animal {
constructor(name) {
this.name = name
}
age = 2
sayName() {
console.log(this.name)
console.log(this.age)
}
}
const animal = new Animal('dog')
animal.sayName() // dog 2
这里的constructor是一个构造方法,它内部this定义的属性和方法都是实例对象上的属性和方法(可以理解为ES5中构造函数的作用)。声明的sayName方法,其实就是放在了这个类的原型对象上。
简单理解为,constructor中定义的属性和方法属于实例对象;在类内部(constructor外部)定义的方法存在于类的原型对象上,定义的属性属于实例对象。
与ES5的不同之处,class定义的类内部声明的方法都是不可枚举的
如何继承一个类
「在ES5中可以通过组合继承实现(原型链式继承+构造函数继承)」
// 原型链式继承
function Parent() {
this.nums = [1, 2, 3]
}
function Child() {}
Child.prototype = new Parent()
const child1 = new Child()
child1.nums.push(4)
console.log(child1.nums) // [1,2,3,4]
const child2 = new Child()
console.log(child2.nums) // [1,2,3,4]
原型链式继承有个缺点,改变实例对象中的引用类型数据会影响其他的实例对象。
// 构造函数继承
function Parent() {
this.nums = [1, 2, 3]
}
function Child() {
Parent.call(this)
}
const child1 = new Child()
child1.nums.push(4)
console.log(child1.nums) // [1,2,3,4]
const child2 = new Child()
console.log(child2.nums) // [1,2,3]
构造函数继承也有个缺点,就是不能重用公用的方法。所以就诞生了组合继承
// 组合继承
function Parent(name) {
this.name = name
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child(name) {
Parent.call(this, name)
}
Child.prototype = new Parent()
Child.prototype.constructor = Child // 将实例原型上的构造函数指定为当前子类的构造函数
const child = new Child('大头儿子')
child.getName() // 大头儿子
还有原型式继承、寄生式继承、寄生组合式继承,了解下就好~(不想写了哈哈哈哈)
接下来说一下ES6实现继承的方式
// extends实现继承
class Animal {
constructor(name) {
this.name = name
}
sayName() {
console.log(this.name)
}
}
class Child extends Animal {
constructor(name) {
super(name)
this.name = name
}
}
const child = new Child('dog')
child.sayName() // dog
在子类中,如果没有显示调用constructor构造方法,则会默认隐式调用constructor方法。
class Child extends Animal {
}
// 等同于
class Child extends Animal {
constructor(...args) {
super(...args);
}
}
另一个需要注意的地方是,在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。
总结
ES5中通过构造函数可以实现类,有六种方式可以实现继承
ES6新增了class和extends语法糖实现一个类和类的继承
JS的继承主要通过原型链来实现。
没什么好说的了...
完。
本文使用 mdnice 排版