ES5中类、对象和原型
ES5没有真正定义类的关键字,它通过函数首字母大写的方式来告诉别人这是一个类,看起来是比较诡异的。比如下面代码:
function People(name, age) {
// 实例属性
this.name = name
this.age = age
}
// 类实例化对象
let xiaoming = new People('xiaoming', 22)
接下来举例子都是用People这个类来举例子,具体实例化对象会是某一个person对象。如同上面代码所示,我们通过首字母大写的方式定义一个类(或者说告诉大家,这不是一个普通函数,这是一个类),如果写成函数的话,要首字母小写,也就是写成people的形式。但这样的话,就很容易混淆了,前面用function定义的话,这到底是函数还是类呢?判断是类还是函数的关键在于new这个关键字。如果通过new赋值的话,就是一个类,并实例化对象。如果没有的话,就算是一个函数。至于是不是首字母大写,不是重点。我们来看下面一个例子。
function People() {
console.log('我的名字是小明')
}
People()
这样的语句就会直接执行函数,这就是类和函数之间的区别,在于关键字new而不是是否首字母大写,我们一般约定,首字母大写的是类。
ES5中也没有构造函数的关键字。构造函数可以理解为,类在实例化对象时所传的最初的参数,让每一个对象具有各自不同的特点。比如我们最初的这个代码:
function People(name, age) {
// 实例属性
this.name = name
this.age = age
}
// 类实例化对象
let xiaoming = new People('xiaoming', 22)
我们可以传递xiaoming也可以传递xiaoli,这样就会声明两个不同的实例化对象。我们通过的是this
关键字来实现构造函数的功能。this
表示对于当前对象的引用。我们没有构造函数关键字的话,People既算是一个类,同时也担负起了类中构造函数的责任。
我们演示完在类中如何添加属性之后,下面看看如何在类中添加方法。方法和函数也是容易混淆的概念,具体实现的话功能基本上是一样的。就是定义的地方不同而已。如果不在类中去定义,我们称之为函数,如果定义在类里面称之为类的方法。实例化对象之后,对象还可以调用方法。
在类中定义方式,使用的是原型。原型是一个称之为prototype
的属性,这个属性是一个对象。在它上面定义的属性和方法,可以被实例化的对象所共享,我们可以将方法定义在类的原型上,这样实例化对象时,实例化的对象也可以调用对应的方法了。我们一起来看一下代码:
function People(name, age) {
this.name = name
this.age = age
}
// 实例方法
People.prototype.showName = function () {
console.log('我的名字是' + this.name)
}
let xiaoming = new People('xiaoming', 22)
xiaoming.showName()
从图中可以看出__proto__
下面挂载了showName()方法。
ES5中静态属性和静态方法
我解释完这些之后,你可能还有一个疑惑,你说类可以实例化对象,而且类要大写。那为什么我见到有代码可以写成如Math.random()
这样呢?按照这个之前讲解的规则,它好像没有实例化对象吧。是这样的,这个称之为静态方法,与之对应的还有一个静态属性。不需要将定义的类实例化对象就可以调用他们。
// 静态属性
People.count = 0
// 静态方法
People.getCount = function(){
console.log(this.age) // undefined
console.log('当前共有' + People.count + '个人')
}
这样就可以像Math.random()
一样,直接调用静态属性和静态方法了。
ES5中的继承
// 父类
function Animal(name) {
this.name = name
}
// 子类
function Dog(name, color) {
// call的作用是用于改变this指向,传入得第一个参数用于表示指向谁
// 这里用于指向Dog(传入得是指向Dog的this)
// 第二个参数是Anmial中所对应的参数
Animal.call(this, name) // 继承属性
// 自己个性化的属性
this.color = color
}
let d1 = new Dog('wangcai', 'white')
但是需要注意的是,写成这样的形式只能继承父类的属性,并不能继承父类的方法。如果想要继承父类的方法,需要写成这样的形式:
// 父类
function Animal(name) {
this.name = name
}
Animal.prototype.showName = function () {
console.log('名字是:' + this.name)
}
// 子类
function Dog(name, color) {
// call的作用是用于改变this指向,传入得第一个参数用于表示指向谁
// 这里用于指向Dog(传入得是指向Dog的this)
// 第二个参数是Anmial中所对应的参数
Animal.call(this, name) // 继承属性
// 自己个性化的属性
this.color = color
}
// 原型继承
Dog.prototype = new Animal()
Dog.prototype.constuctor = Dog
let d1 = new Dog('wangcai', 'white')
d1.showName()
这种继承称之为组合继承,结合了原型继承的和构造函数继承。不过可以看到,这样写起来真的比较难以理解。在ES6中,我们引入了很多新的关键字,代码写起来要更优雅,下面我们一起来看一下在ES6中如何实现这一点。
ES6中类、对象和继承
class People {
constructor(name, age) {
this.name = name
this.age = age
}
showName() {
console.log('我的名字是' + this.name)
}
// 静态方法
static getCount() {
console.log('当前共有' + People.count + '个人')
}
}
People.count = 1
let xiaoming = new People('xiaoming', 22)
xiaoming.showName()
我们看到有新的关键字class
用于声明这是一个类,新的关键字constructor
用于声明构造函数,新的关键字static
用于声明静态方法。静态属性的话,同样是不能写在类里面,要写在外面,而且同样静态属性和静态方法不能被实例化对象所调用,只能被类所调用。
虽然ES6中的类实现是一种基于原型的语法糖形式,但看起来要比ES5的写法容易理解多了。下面我们再来看一下类的继承。
class Animal {
constructor(name) {
this.name = name
}
showName() {
console.log('名字是:' + this.name)
}
}
class Dog extends Animal{
constructor(name, color) {
super(name)
this.color = color
}
showColor() {
console.log('颜色是' + this.color)
}
}
let xiaohua = new Dog('xiaohua', 'white')
xiaohua.showName()
xiaohua.showColor()
在ES6中引入了两个新的关键字来实现继承,分别是extends
用来继承父类的方法,和super
用来让子类继承父类中构造函数的属性。