对于面向对象的编程语言这一说法,准确来说应该是 支持使用面向对象程序设计思想进行编程的编程语言,而这一思想的本质其实是抽象。从这点出发,其实 JavaScript 就是一门面向对象的语言,但是其他面向对象的语言(比如 Java、C++)是基于 class 来实现的,而 JavaScript 是基于原型来实现”
class
es6 新增了 class,下面创建一个关于 Animal 的类
class Animal {
constructor(name) {
// 这里面的this会指向实例
this.name = name;
}
static parentName = "Animal"; // 静态属性
static getParentName() {
// 1.静态方法的this只会指向父类
// 2.可以通过父类调用
// 3.静态方法不能被实例继承,但可以被子类继承
console.log(this.parentName);
return this.parentName;
}
getName() {
console.log(this.name);
return this.name;
}
}
实现 class
其实 es6 的 class 就是一个语法糖,是基于 js 构造函数和原型去实现的,实现代码如下:
function createClass(Constructor, protoProps, staticProps) {
if (protoProps) {
Object.defineProperties(Constructor.prototype, protoProps);
}
if (staticProps) {
Object.defineProperties(Constructor, staticProps);
}
return Constructor;
}
function Animal(name) {
// 实例属性赋值
this.name = name;
}
// 在构造函数的原型对象上挂载实例方法
createClass(
Animal,
{
getName: {
value: function getName() {
console.log("调用实例方法,获取实例属性", this.name);
return this.name;
},
},
},
{
getParentName: {
// 在构造函数上挂载静态属性和方法
value: function getParentName() {
console.log("调用静态方法,获取静态属性", this.parentName);
return this.parentName;
},
},
parentName: {
value: "Animal",
},
}
);
let animal1 = new Animal("animal1");
// 调用实例方法
animal1.getName();
// 调用静态方法
Animal.getParentName();
在创建对象实例时 let animal1 = new Animal('animal1')
,主要会经历几个步骤
// 1.新建空对象
let newObj = new Object();
// 2.指向构造函数的原型对象,共享实例方法
newObj.__proto__ = Animal.prototype;
// 3.以新对象为上下文执行构造函数
let result = Animal.call(newObj, "animal1");
// 4.返回一个新对象
// 如果构造函数返回一个对象或函数,对象实例就是该对象或函数;否则返回一个新对象
return result instanceof Object ? result : newObj;
继承
下面通过 extends 关键字实现对 Animal 的继承
class Animal {
constructor(name) {
// 实例属性赋值,这里面的this会指向实例
this.name = name;
}
static parentName = "Animal"; // 静态属性
getName() {
console.log("调用实例方法,获取实例属性", this.name);
return this.name;
}
}
class Cat extends Animal {
constructor(name) {
super(name);
}
}
let cat = new Cat("jack");
cat.getName();
console.log(Cat.parentName);
// 可以发现,静态属性和方法也被子类继承了
实现 extends
extends 本质上是基于寄生式组合继承来实现的
- new 一个实例时,调用父类构造函数,继承实例属性
- 通过原型链继承父类实例方法
- 设置子类的 proto = 父类,让子类可以调用父类静态属性和方法
// 已知 Animal 这个父类
function _inherits(child, parent) {
let proto = Object.create(parent.prototype);
proto.constructor = child;
child.prototype = proto;
}
function Cat(name) {
// 调用父类的构造函数,继承实例属性
Animal.call(this, name);
}
// 继承父类实例方法
_inherits(Cat, Animal);
// 设置子类的 __proto__ = 父类,让子类可以调用父类静态属性和方法
Object.setPrototypeOf(Cat, Animal);
let cat = new Cat("jack");
cat.getName();
console.log(Cat.parentName);