定义
把具有相同属性和行为的事物抽象成一个事物类别,通过实例化,让这个事物类别构造出一个具体的事物,这个具体的事物就是对象。
例子
- 比如人就是一个事物类别,人有名字、身高、体重等属性。人同时有说话、吃饭、睡觉、喝水等行为。通过实例化人这个个事物类别后,可以得到具体一个人。假如实例化一个小明,其名字是小明、身高180、体重130,那么小明就是人的一个实例。
- 选项卡功能是一个事物类别,它有页面切换方式的属性。也有点击导航按钮切换页面的方法。通过实例化这个选项卡的事物类别后,我们可以得到一个选项A,它的页面切换方式为淡入淡出。
ES5
```javascript function People(name, height, weight){ //对象属性 name, height, weight this.name = name; this.height = height; this.weight = weight; }
//对象方法 People.prototype.intro = function(){ console.log(‘I am ‘ + this.name + ‘. ‘ + this.height + ‘tall. And ‘ + this.weight + ‘kilos.’); }
People.prototype.eat = function(){ console.log(‘I am eating’); }
People.prototype.sleep = function(){ console.log(‘I am sleeping’); }
People.prototype.drink = function(){ console.log(‘I am drinking water’); }
var xiaoming = new People(‘小明’, 180, 70); var xiaohong = new People(‘小红’, 160, 40);
xiaoming.intro(); xiaoming.drink();
xiaohong.intro(); xiaohong.eat();
<a name="CPNd4"></a>
### ES6
```javascript
class People{//声明一个类
//成员属性,一般定义在构造函数上
constructor(name, height, weight){ //类的构造函数
this.name = name;
this.height = height;
this.weight = weight;
}
//成员方法
intro(){
console.log(`I am ${this.name}. ${this.height} tall. And ${this.weight} kilos.`);
}
eat(){
console.log('I am eating');
}
sleep(){
console.log('I am sleeping');
}
drink(){
console.log('I am drinking water');
}
}
export { People };
区别
- ES5 实例的proto.constructor是function
- ES6 实例的proto.constructor是class
- 本质是一样,但是过程有不同。来源不一样。
三大特性
- 继承
- 封装
- 多态
继承
子类通过一种特定的形式去使用父类上的成员(对象或实例)属性和方法
ES5:
通过JS对象原型与原型链的特性,实现对象与对象之间的继承关系。
- 原型链继承 ```javascript Professor.prototype = { name: ‘Mr.Zhang’, tSkill: ‘JAVA’ } function Professor(){}
var professor = new Professor();
Teacher.prototype = professor; //把实例化出来的professor对象赋给Teacher的原型, //这就是原型链继承的关键 function Teacher(){ this.name = ‘Mr.Wang’; this.mSkill = ‘JS/JQ’; }
var teacher = new Teacher(); console.log(teacher.tSkill); //JAVA 通过原型链最终可以访问到Professor的原型tSkill
Student.prototype = teacher; functon Student(){ this.name = ‘Mr.Li’; this.pSkill = ‘HTML/CSS’; } var student = new Student(); console.log(student); //继承所有原型链上的属性与方法
- call/apply 继承 借用构造函数
```javascript
//原型链继承的方式并不科学,例如teacher / student是没有必要继承其继承对象的name属性
Teacher.prototype.wife = 'Ms.Liu';
function Teacher(name, mSkill){
this.name = name;
this.mSkill = mSkill;
}
function Student(name, mSkill, age, major){
Teacher.apply(this, [name, mSkill]); //每次实例时都会执行Teacher函数
this.age = age;
this.major = major;
}
var student = new Student('Mr.Zhang', 'JS/JQ', 18, 'Computer');
console.log(student.wife); //undefined 只用call/apply是无法访问继承对象的原型,因为其本质
//不是继承,而是借助call/apply来改变this指向来借用别人的构造函数
- 公共原型继承 组合继承 伪经典继承 ```javascript Teacher.prototype = { pSkill:’JS/JQ’; } function Teacher(){ this.name = ‘Mr.Li’; this.tSkill = ‘JAVA’; } var teacher = new Teacher();
function Student(){ this.name = ‘Mr.Wang’; } Student.prototype = Teacher.prototype; //把Student原型对象指向Teacher原型对象
Student.prototype.age = 18;//如果除了修改Student的原型也修改了Teacher原型。 console.log(teacher.age); //18 因为这是公共原型。
var student = new Student(); console.log(student); //此时student有继承teacher的原型对象的属性与方法 //但没有继承teacher实例化对象的属性
- 圣杯模式继承 寄生组合继承
```javascript
Teacher.prototype = {
pSkill:'JS/JQ';
}
function Teacher(){
this.name = 'Mr.Li';
this.tSkill = 'JAVA';
}
var teacher = new Teacher();
function Student(){
this.name = 'Mr.Wang';
}
//为解决公共原型继承的问题。
function Buffer(){} //创建一个中间对象Buffer
Buffer.prototype = Teacher.prototype; //让Buffer与Teacher公共原型
var buffer = new Buffer();
//var buffer = Object.create(teacher); ES5.1后也可以使用Object.create来代替上面三行代码。
Student.prototype = buffer; //继承buffer
Student.prototype.age = 18;
var student = new Student();
console.log(student.pSkill); //JS/JQ 可以通过原型链访问到Teacher及其原型上面属性与方法
console.log(student.age); //18 被Buffer分离后,修改student原型并不会影响到Teacher。
console.log(teacher.age); //undefined 不会被修改
ES6:
extends
关键字继承,也是面向对象语言通用的继承操作的关键字 ```javascript class Polygon { constructor(height, width) { this.name = ‘Polygon’; this.height = height; this.width = width; } }
class Square extends Polygon { constructor(length) { super(length, length); this.name = ‘Square’; } }
<a name="NOLSV"></a>
### 封装
面向对象中的"封装"是面向对象语言的一个重要特性。
> 通过封装,控制类的属性与方法的可访问方式对其信息隐藏
<a name="H5gz1"></a>
#### 关键字
- private
- 只有类的内部可访问
- public
- 完全开放访问
- protected
- 可供自身与子类访问
<a name="NRWj9"></a>
#### 好处
- 减少程序的耦合
- 自由修改类内部结构
- 对成员准确的控制
- 隐藏隐私信息
> ES6并没有支持面向对象的封装特性。
> TypeScript是有此支持。
<a name="CRW68"></a>
#### 在ES6中私有变量曲线救国的方法
借助Symbol是唯一值的方法。
```javascript
const doAjax = Symbol('doAjax');
class Http{
[doAjax](){
//实现方法...
}
post(url, data, dataType, callback){
this[doAjax]({ //使用this[]就能访问到
type:'POST',
url,
data,
dataType,
scuccess: callback
})
}
}
多态
成员方法的重载和和重写,根据不同类执行同一方法,会有不同的表现。
如动物类都被 猫、狗、鸡、鸭继承,牠们都重写动物类叫的方法。
并且执行叫这方法都会有不同的声音。
重载
在同一个类中有写多次同样的方法,方法根据其参数的数量与类型的不同会有不同的表现
重写
子类重写父类的方法
强类型面向对象语言才有意义
面向对象意义
面向对象的方法其实就是程序结构化过程,是对程序结构化(顺序、判断、循环)的抽象,使其更好地管理数据的属性与方法。
使代码易于复用,并且减少防止耦合。
使程序更加有结构、更加易于管理、更加易读易迭代。