最近在复习JS基础,对于JS中的面向对象写法在此总结一下。
第一种:TS写法(ES6写法)
这种写法是在做项目里经常遇到的。特别是最近我参与的一个NG4的项目,全程TS,爽的不要不要的。
- 先声明一个Car类(为了保证在ES6环境也可以运行,我没写TS的类型注解)
//ES6,使用传统的类、继承、多态和构造函数
class Car {
constructor(name) {
this.name = name;
this.gear = 0;
this.speed = 0;
}
drive() {
clearInterval(this.driving);
this.gear++;
this.driving = setInterval(() => {
this.speed += this.gear * 10;
console.log(`老司机的${this.name}车速到了${this.speed}`);
if (this.speed >= 200) {
this.stop();
}
}, 100);
}
stop() {
console.log("翻车了!");
this.gear = 0;
clearInterval(this.driving);
}
}
let car = new Car("五菱宏光");
car.drive();
- 这种风格和后端语言的风格非常接近,构造函数+自有方法。
- 里面使用的模板字符串和箭头函数都是ES6的新特性,有兴趣的可以去看文档。
- 接下来我们声明一个YunNanCar类,并实例化它。
class YunNanCar extends Car {
constructor(name, personName) {
super(name);
this.personName = personName;
}
drive() {
clearInterval(this.driving);
this.gear++;
this.driving = setInterval(() => {
this.speed += this.gear * 10;
console.log(`${this.personName}的${this.name}车速到了${this.speed}`);
if (this.speed >= 300) {
this.stop();
}
}, 100);
}
}
let kunmingCar = new YunNanCar("昆明车", "樛木");
kunmingCar.drive();
- 运行代码,毫无违和感。
第二种:ES5写法
第二种就是传统的ES5实现OOP,高级程序设计那本书推崇的方法。
//ES5,(高级程序设计书)使用原型链+构造函数
function Car(name) {
this.name = name;
this.gear = 0;
this.speed = 0;
}
Car.prototype.drive = function () {
clearInterval(this.driving);
this.gear++;
this.driving = setInterval(function () {
//这里可以用self,但是不好
this.speed += this.gear * 10;
console.log("老司机的" + this.name + "车速到了" + this.speed);
if (this.speed >= 200) {
this.stop();
}
}.bind(this), 100);
};
Car.prototype.stop = function () {
console.log("翻车了!");
this.gear = 0;
clearInterval(this.driving);
};
// var car = new Car("五菱宏光");
// car.drive();
function YunNanCar(name, personName) {
//继承
Car.call(this, name);
this.personName = personName;
}
YunNanCar.prototype = Object.create(Car.prototype);
// 高程上的,会有意想不到的bug
// YunNanCar.prototype = new Car();
// YunNanCar.prototype.constructor = Car;
YunNanCar.prototype.drive = function () {
clearInterval(this.driving);
this.gear++;
this.driving = setInterval(function () {
//这里可以用self,但是不好
this.speed += this.gear * 10;
console.log(this.personName + this.name + "车速到了" + this.speed);
if (this.speed >= 300) {
this.stop();
}
}.bind(this), 100);
}
var kunmingCar = new YunNanCar("昆明车", "樛木");
kunmingCar.drive();
- 这是前端们在艰难探索后找到的模仿传统类式OOP的方法,构造+原型链方法。
- 和属性有关的用构造,和方法有关的添在原型上。目的是防止只用构造函数的时候不能复用函数,和只用原型链的时候会共享引用类型属性。
- 没有箭头函数的时候,this会有问题,优雅的方法是采用bind强绑定,还有一种方法是使用self变量存放this。
- 在设置原型链关联的时候,我没有使用高程的用法,原来需要指定YunNanCar的prototype为Car的实例,还要指定YunNanCar的prototype的constructor为Car,但其一那样关联原型链如果Car出了事YunNanCar也会跟着倒霉,其二是constructor根本不靠谱。所以使用更加靠谱的Object.create,如果要兼容旧式浏览器,就写段Polyfill代码。
第三种:纯面向对象写法
第三种方法是为JS量身定做的,和传统的OOP思想完全不一样,没有类,也没有继承,这种模式里没有父类,子类继承父类的概念。只有对象之间互相关联的关系。
比如说,传统OOP:我要一辆车,肯定是从车的基类继承,我要滑板鞋 肯定是从鞋的基类继承。
而行为委托根本不需要基类,在只有车和滑板鞋的情况下,滑板鞋也想飙车怎么办?于是滑板鞋和云南车关联起来,借用它的drive方法就可以了。
//JS中的纯面向对象,这里的三个方法可以分开写(ES5语法)比如init:function(name){..}
let Car = {
init(name) {
this.name = name;
this.gear = 0;
this.speed = 0;
},
drive() {
clearInterval(this.driving);
this.gear++;
this.driving = setInterval(() => {
this.speed += this.gear * 10;
console.log(`老司机的${this.name}车速到了${this.speed}`);
if (this.speed >= 200) {
this.stop();
}
}, 100);
},
stop() {
console.log("翻车了!");
this.gear = 0;
clearInterval(this.driving);
}
}
let Shoe = Object.create(Car);
Shoe.product = function (name, personName) {
this.init(name);
this.personName = personName;
}
//独特的飙车方法
Shoe.racing = function () {
clearInterval(this.driving);
this.gear++;
this.driving = setInterval(() => {
this.speed += this.gear * 10;
console.log(`${this.personName}的${this.name}滑板鞋速度到了${this.speed}`);
if (this.speed >= 300) {
this.stop();
}
}, 100);
}
let kunmingShoe = Object.create(Shoe);
kunmingShoe.product("滑板鞋", "樛木");
kunmingShoe.racing();
- 这里我为了节省力气用了es6语法,es5语法使用传统的方法声明就好了,像init:function(name){..}这样。
- Car对象里面有三个方法。是特有的。
- 让滑板鞋关联这个车,接着给滑板鞋对象添加两个方法。
- produce和racing方法和车毛关系都没有,但是它们的实现借用了车的三个方法,所以达到了滑板鞋也能飙车的目的。