目录
类的概念
类的设计模式:实例化,继承和(相对)多态
- 封装:把实现某个功能的代码进行封装处理,后期想实现这个功能 直接调用这个函数即可,低耦合,高内聚
- 多态:重载:方法名相同,参数类型或者个数不同,这样会认为是多个方法 重写:子类重写父类方法
- 继承:子类继承父类的方法 ,子类的实例既拥有子类赋予的私有/公有属性方法 也想拥有父类赋予的私有/公有属性方法
类理论:
面向对象编程强调的是数据和操作数据的行为本质上是互相关联的。
类是一个通用的基础,继承是实例化的实例具有类的方法和属性,而实例则是一个独立的个体,有自己独有的东西。多态值的是一个类实际化的多个实例,可以改写这个继承的方法。。
类设计模式:
类是一种设计模式,有多种高级设计模式建立在面向对象类的基础上:迭代器模式、观察者模式、工厂模式、单例模式等等。
- 迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。
- 观察者模式(有时又被称为发布/订阅模式)是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
- 工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。
- 单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例
js实际上没有类,类是一种设计模式,所以可以使用方法来模拟类。
类的机制:
在面向类的语言中,必须将类实例化才可以对抽象化的类进行操作。
- 类通过复制操作被实例化为对象形式;
- 类实例是由一个特殊的类方法共造的,这个方法名通常和类名相同,被称为构造函数;
这里提到是复制而不是引用。用构造函数new一个实例,构造函数返回的是对象,这个对象指向创建的这个实例,每new一个实例,相当于创建一个新对象,每个实例直接是相互独立的,这样就明白是复制而不是引用。
class Person {
name = '';
// 构造函数
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}
var person = new Person('Lee');
person.sayName();
类的继承:
我们常说子类继承父类,但是用子类DNA是继承父类DNA会更好理解。类通过继承和方法重写实现多态。
-
多态并不表示子类和父类有关联,子类得到的只是父类的一份副本。类的继承其实就是复制。
-
在大多数面向类语言中,可以使用super来在子类中引用父类。
-
但是JS本身并不提供多重继承。
class Father {
constructor(surname) {
this.surname = surname;
}
saySurname() {
console.log(this.surname);
}
}
class Son extends Father {
constructor(surname, name) {
super(surname);
this.name = name;
}
sayFullName() {
console.log(`${this.name} - ${this.surname}`);
}
}
var son = new Son('Lee', 'Prosper');
console.log(son.surname); // Lee
console.log(son.name); // Prosper
son.saySurname(); // Lee
son.sayFullName(); // Prosper - Lee
类的多态
多态的字面意思就是多种状态,同一操作作用于不同的对象上,可以产生不同的解释和不同的执行结果
class Animal {
say() {
console.log('Hello');
}
eat() {
console.log('动物用嘴吃饭-狼吞虎咽!!!');
}
}
class Person extends Animal {
eat() {
super.eat();
console.log('人用嘴吃饭-斯文的可以了!!!');
}
}
var person = new Person();
person.say(); // Hello
person.eat(); // 动物用嘴吃饭-狼吞虎咽!!! 人用嘴吃饭-斯文的可以了!!!
混入
混入模式(无论显式还是隐式)可以用来模拟类的复制行为,但是通常会产生丑陋并且脆 弱的语法,比如显式伪多态(OtherObj.methodName.call(this, …)),这会让代码更加难 懂并且难以维护。
- 因为JavaScript中只有单继承,当我想要使用其他类的方法,或者说想要增加新的方法,但是又不能够修改自己这个类,更别说其父类了,这个时候就是只能够使用混入了
- 混入的本质,还是继承,通过继承当前这个类,创建一个新类返回去
显示混入 使用call绑定this
混合复制
function mixin(sobj,tobj) {
for(var key in sobj) {
if(!(key in tobj)) { //什么都不存在的情况下复制
tobj[key] = sobj[key];
}
}
return tobj;
}
var vehicle = {
engines: 1,
ignition: function() {
console.log("点火器点着了");
},
drive: function() {
this.ignition();
console.log('发车啦');
}
}
var car = mixin(vehicle, {
wheels: 4,
drive: function() {
// vehicle.drive();
//显示绑定 将vehicle.drive绑定到当前this上 this.ignition 调用的就是vehicle里的ignition方法
vehicle.drive.call(this);
console.log('轮子开始走了');
}
})
var a = mixin(vehicle, {})
// vehicle.drive()
car.drive()
// a.drive()
寄生继承
function Vehicle() {
this.engines = 1;
}
Vehicle.prototype.ignition = function() {
console.log('打开引擎');
};
Vehicle.prototype.drive = function() {
this.ignition();
console.log('开始走');
};
function Car() {
var car = new Vehicle(); //实例化一个Vehicle对象
car.wheels = 4; //加入新的属性
var vehDrive = car.drive; //维持一个函数drive的引用
car.drive = function() { //重写drive
// vehDrive();
vehDrive.call(this); //绑定到当前this
console.log(this.wheels + '走起~');
}
return car;
}
var aCar = new Car();
aCar.drive();
aCar.ignition();
隐式混入
在构造函数调用或者方法调用中使用call,把某个对象的某个属性绑定进来
在a.say()
中的赋值操作都会应用在b上而不是a上
var a = {
say: function() {
this.name = 'zz';
this.age = '18'
}
}
var b = {
say1:function() {
a.say.call(this) //隐式混入
}
}