一、认识class定义类
1.1. 类的概念
- 在面向对象编程中, 一个 类 定义了一个对象的特征。类是定义对象属性和方法的模板,是用来绘制具体对象实例的“蓝图”;
- 在ES6(ECMAScript2015)新的标准中使用了class关键字来直接定义类;
- 但是类本质上依然是构造函数、原型链的语法糖而已;
- 它和构造函数的特性其实是一致的;
class Person {}; var p = new Person(); console.log(Person.prototype); //{} console.log(Person.prototype.__proto__); //[Object: null prototype] {} console.log(Person.prototype.constructor); //[class Person] console.log(p.__proto__ === Person.prototype); //true
1.2. 类的定义
- 可以使用两种方式来声明类:类声明和类表达式;
class Person {}; var Person = class {};
1.3. 类的构造函数
- 在创建对象的时候给类传递一些参数:
- 每个类都可以有一个自己的构造函数(方法),这个方法的名称是固定的constructor;
- 当我们通过new操作符,操作一个类的时候会调用这个类的构造函数constructor;
- 每个类只能有一个构造函数,如果包含多个构造函数,那么会抛出异常;
- 通过new关键字操作类的时候,会调用这个constructor函数,执行如下操作:
- 1)在内存中创建一个
新的对象(空对象); - 2)这个对象内部的
[[prototype]]属性会被赋值为该类的prototype属性; - 3)构造函数内部的
this,会指向创建出来的新对象; - 4)执行函数的内部代码(函数体代码);
- 5)如果构造函数
没有返回非空对象,则返回创建出来的新对象;
- 1)在内存中创建一个
二、类的实例方法
- 我们定义的属性都是直接放到了this上,也就意味着它是放到了创建出来的新对象中:
- 对于实例的方法,我们是希望放到原型上的,这样可以被多个实例来共享;
- 这个时候我们可以直接在类中定义;
class Person { constructor(name, age) { this.name = name; this.age = age; } eating() { console.log(this.name + '在吃东西!'); } running() { console.log(this.name + '在跑步啊!'); } }; var p = new Person('yzh', 18); p.eating(); //yzh在吃东西! p.running(); //yzh在跑步啊!
三、类的访问器方法
- 写对象的属性描述符时有说过对象可以添加setter和getter函数,那么类也是可以的;
class Person { constructor(name, age, height) { this.name = name; this.age = age; this._height = height; } //类的访问器方法 get height() { console.log('拦截访问操作'); return this._height; } set height(newVal) { console.log('拦截设置操作'); this._height = newVal; } }; var p = new Person('yzh', 18, 180); console.log(p.height); //拦截访问操作 180 p.height = 1.78; //拦截设置操作 console.log(p.height); //拦截访问操作 1.78
四、类的静态方法
- 静态方法通常用于定义直接使用类来执行的方法,不需要有类的实例,使用static关键字来定义;
var names = ['ace', 'sabot', 'luffy']; class Person { constructor(name, age) { this.name = name this.age = age } static staticProperty = '静态属性'; static staticMethod() { return '已调用静态方法'; } static randomPerson() { var index = Math.floor(Math.random() * names.length); //0-2 var name = names[index]; var age = Math.floor(Math.random() * 50); return new Person(name, age); } }; var p = new Person('yzh', 18); console.log(p); console.log(Person.staticProperty); console.log(Person.staticMethod()); //随机10个对象 for (var i = 0; i < 10; i++) { console.log(Person.randomPerson()); };
不能在类的实例上调用静态方法,而应该通过类本身调用;
五、类的继承
5.1. extends关键字
- ES6中新增了使用extends关键字,可以方便的帮助我们实现继承;
- extends关键字用于类声明或者类表达式中,以创建一个类,该类是另一个类的子类;
class Person { } class Student extends Person { }
5.2. super关键字
- 在子(派生)类的构造函数中使用this或者返回默认对象之前,必须先通过super调用父类的构造函数;
- super的使用位置有三个:子类的构造函数、实例方法、静态方法;
class Person { constructor(name, age) { this.name = name; this.age = age; } eating() { console.log(this.name + '在吃东西!'); } running() { console.log(this.name + '在跑步啊!'); } personMethods() { console.log('处理逻辑1'); console.log('处理逻辑2'); console.log('处理逻辑3'); } static personStaticMethods() { console.log('personStaticMethods'); } }; class Student extends Person { constructor(name, age, son) { super(name, age); this.son = son; } // 子类重写父类方法; personMethods() { super.personMethods(); //调用公共逻辑; console.log('处理逻辑4'); console.log('处理逻辑5'); console.log('处理逻辑6'); } // 重写父类静态方法 static personStaticMethods() { super.personStaticMethods(); console.log('Student -> personStaticMethods'); } }; var stu = new Student('yzh', 18, 1804515); console.log(stu); stu.eating(); stu.running(); stu.personMethods(); Student.personStaticMethods();
六、继承内置类
/**
* 对系统Array类扩展
*
*/
class MyArray extends Array {
firstItem() {
return this[0]
}
lastItem() {
return this[this.length - 1]
}
}
var arr = new MyArray(1, 2, 3);
arr.push(4);
console.log(arr);
console.log(arr.firstItem()); //1
console.log(arr.lastItem()); //4
arr:
七、类的混入mixin
- JavaScript的类只支持单继承,也就是只能有一个父类;
- 在开发中我们需要在一个类中添加更多相似的功能时,这个时候可以使用混入(mixin);
class Person { }; function MixinRunner(BaseClass) { // 定义类名称并返回 class NewClass extends BaseClass { running() { console.log('在跑步!'); } } return NewClass; }; function MixinEater(BaseClass) { // 直接返回 return class extends BaseClass { eating() { console.log('在吃东西!'); } } }; var NewPerson = MixinEater(MixinRunner(Person)); var nc = new NewPerson(); nc.running(); //在跑步! nc.eating(); //在吃东西!
八、传统面向对象多态
- 传统面向对象多态是有三个前提:
- 1)必须要有继承(是多态的前提);
- 2)必须有重写(子类重写父类的方法);
- 3)必须有父类引用指向子类对象(var shape:Shape = new Rectangle());
// index.ts: class Shape { getArea() { } }; class Rectangle extends Shape { getArea() { return 100 } }; class Circle extends Shape { getArea() { return 200 } }; var r = new Rectangle(); var c = new Circle(); // 多态:当对不同数据类型执行同一个操作时,如果表现出来的行为(形态)不一样,那么就是多态的体现; function calcArea(shape: Shape) { console.log(shape.getArea()); }; calcArea(r); calcArea(c); export {};
九、JS面向对象多态
- 多态:当对不同数据类型执行同一个操作时,如果表现出来的行为(形态)不一样,那么就是多态的体现;
- 不同数据类型:传入的可能是obj对象可能是p对象;
- 行为不一样:一个调用obj.getArea(),一个调用p.getArea();
var obj = { name: 'yzh', getArea: function() { return 100 } }; class Person { getArea() { return 200 } }; var p = new Person(); function calcArea(foo) { console.log(foo.getArea()); }; calcArea(obj); calcArea(p);
因为js比较灵活所以不满足传统多态的前提,也是多态的体现;