使用类可以大大简化我们的代码,本篇将会说一说从ES 6之前的继承到ES 6规定的类
ES 6之前
1. 类的定义
我们可以使用三种方法来创建一个数组或对象
let arr1 = [1,2,3];
let arr2 = new Array(1,2,3);
let arr3 = Array(1,2,3);
let obj1 = { color: 'red' };
let obj2 = new Object({ color: 'red' });
let obj3 = Object({ color: 'yellow' });
但是我们最常用也是被推荐的方法都是字面量创建,这是因为如果使用
new
方法,会有重载的概念:意思就是说,根据传入的参数不同,会有不同的结果,举个栗子,了解一下
let arr = new(3); // Array(3) {length: 3}
我们想要创建一个[3]
结果他给我们创建了以长度为3的空数组,显然这就有问题,这里先不说这个,前面说的所有都是为了引出所谓的安全类
// 创建一个构造函数(也就是一个类)
function People(name,age) {
this.name = name;
this.age = age;
};
// 分别new和直接调用People创建一个实例
let p1 = new People('zs',20);
let p2 = People('ls',30);
console.log(p1,p2); People{} undefined;
当我们直接调用People
创建实例时,它会将People
当做普通函数,而普通函数中的this
是指向他的直接调用者,在这里也就是window
了,所以我们直接调用People
创建时,不仅创建不成功,而且还会造成全局污染,如果全局也有一个name
变量,那么它的值就会被改变,所以我们说这个类不安全,那么如何创建一个安全类
// 创建一个安全类
function People(name,age) {
if(this instanceof People) {
this.name = name;
this.age = age;
} else {
return new People(name,age);
}
}
这样我们自己创建的类也就可以像
Array Object
那样通过People
直接调用了,他们的实现原理是一致的
2. 类的继承(重头戏)
1. 构造函数式继承
// 创建一个父类
function Animal(legs) {
this.legs = legs;
};
// 在父类原型上添加一个getLeg方法
Animal.prototype.getLeg = function() { console.log(this.legs); };
// 创建一个子类,通过构造函数继承父类
function Dog(legs, eyes) {
Animal.call(this,legs); // 将Animal这个构造函数绑定在Dog中实现继承
this.eyes = eyes;
};
// 创建一个子类实例
let animal = new Animal(8);
animal.getLeg(); // 8
let dog = new Dog(4,2);
cosonle.log(dog.legs); // 4
dog.getLeg(); // dog.getLeg() is not a function;
可以看出,构造函数式继承,只能继承父类的自身属性(也就是卸载大括号里的),但是不能继承父类原型上的属性
2. 类式继承(原型式继承)
如果我们加上
Dog.prototype = Animal.prototype
是不是就可以继承父类的原型方法了?
function Animal(legs) {this.legs = legs};
Animal.prototype.getLeg = function() {
return `动物有${this.legs}条腿`;
};
function Dog(legs) {
Animal.call(this,legs);
};
Dog.prototype = Animal.prototype;
let dog = new Dog(4);
console.log(dog.getLeg()); // 动物有四条腿;
// 但是当我们有其他需求时(比如Dog的getLeg 要返回"狗有4条腿")
// 此时修改Dog原型方法
Dog.prototype.getLeg = function() {
return `狗有${this.legs}条腿`;
};
let animal = new Animal(4);
console.log(animal.getLeg()); //狗有4条腿
console.log(dog.getLeg()); //狗有4条腿
我们只希望改变Dog原型的方法,结果Animal的原型的方法也被改变了,这不合理,这是应为引用类型传递的是地址,这个可以看看JS数据类型的事,可以通过新建解决
Dog.prototype = Object.create(Animal.prototype);
// 或者
Dog.prototype = new Animal();
问题(还挺多):
子类原型对象上出现多余的父类的自身属性,而且错误(值是undefined)
多执行了一次父类构造函数
子类的constructor属性获取错误,指向错误
无法复用 构造函数中存储属性(自身属性)问题
3. 组合式继承
结合前两中继承方式,就可以实现一个比较完整的继承,但是必须解决子类constructor指向的问题
function People (name,age) {
this.name = name;
this.age = age;
};
People.prototype.getName = function() {
console.log(`我的名字是${this.name}`);
};
function Kid(name,age,sex) {
People.call(this,name,age); // 使用构造函数的方法继承父类自身属性
this.sex = sex;
};
Kid.prototype = Object.create(People.prototype); // 原型继承继承父类原型属性
Kid.prototype.constructor = Kid(); // 改变constructor指向
let kid = new Kid('小白',10,'男');
kid.getName();
问题 :多执行了一次父类构造函数;无法复用 构造函数中存储属性(自身属性)问题
4. 寄生式继承
寄生式:在一个继承方法中,创建一个寄生类,等于父类的原型,在实例寄生类赋值给子类原型
// 创建一个寄生类
function inherit(child, parent) {
//1 定义寄生类
function F() {
// 4. 修改constructor指向
this.constructor = child;
};
// 2. 让寄生类的原型等于父类的原型
F.prototype = parent.prototype;
// 3. 将寄生类 的实例赋值给子类原型
child.prototype = new F();
return child;
};
function People (name,age) {
this.name = name;
this.age = age;
};
People.prototype.getName = function() {
console.log(`我的名字是${this.name}`);
};
function Kid(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
};
inherit(Kid,People); // 寄生式继承
let kid = new Kid('小白',10,'男');
kid.getName(); // 我的名字是小白
问题: 无法复用 构造函数中存储属性(自身属性)问题
5. 寄生组合式继承(寄生+构造函数)
这是ES 6之前的最高配置,只需要在子类对象中添加构造函数式继承语句
People(父类).call(this,参数1,参数2)
就OK了,基本没问题
ES 6
1. 类的定义
// 创建类
class Book {
// 构造函数
constructor (title, price) {
// 实例数据(每个实例都会有的特有数据)
this.title = title;
this.price = price;
}
// 原型方法(每个实例都可以使用的公用方法)
getTitle() {
console.log(this.title);
}
getPrice() {
console.log(this.price);
}
// 原型属性(添加给自身的属性,自能自己访问)
set num(val) {
this._num = val;
}
get num() {
return this._num;
}
// 静态数据(只能通过Book.xx访问)
static get write() {
return 'zzq';
}
static getWrite() {
return this.write;
}
}
// 外部添加静态数据
Book.msg = 'hello';
Book.getMsg = function() {
return this.msg;
}
// 实例化
let b1 = new Book('javascript', 59);
let b2 = new Book('面试题', 60);
console.log(b1, b2); // Book {title: "javascript", price: 59}
console.log(b1._num); // undefined;
console.log(Book.write); //zzq
类的继承
ES 6继承非常容易,而且还可以重写 构造函数,不过既然是继承,那就要给constructor添加super()
,它的参数就是父类传入的参数
// JsBook 继承于 Book
class JsBook extends Book{
// 可以重写构造函数
constructor(title, price, score) {
// 继承父类的部分
super(title, price);
// 自己拥有 的部分
this.score = score;
}
}
let jb = new JsBook('js', 60, 100);
到这里,JS类的发展史就说完了,内容挺多,要完全理解还是需要点时间滴,感谢观看,记得点赞~~