定义

把具有相同属性和行为的事物抽象成一个事物类别,通过实例化,让这个事物类别构造出一个具体的事物,这个具体的事物就是对象。

例子

  1. 比如人就是一个事物类别,人有名字、身高、体重等属性。人同时有说话、吃饭、睡觉、喝水等行为。通过实例化人这个个事物类别后,可以得到具体一个人。假如实例化一个小明,其名字是小明、身高180、体重130,那么小明就是人的一个实例。
  2. 选项卡功能是一个事物类别,它有页面切换方式的属性。也有点击导航按钮切换页面的方法。通过实例化这个选项卡的事物类别后,我们可以得到一个选项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();

  1. <a name="CPNd4"></a>
  2. ### ES6
  3. ```javascript
  4. class People{//声明一个类
  5. //成员属性,一般定义在构造函数上
  6. constructor(name, height, weight){ //类的构造函数
  7. this.name = name;
  8. this.height = height;
  9. this.weight = weight;
  10. }
  11. //成员方法
  12. intro(){
  13. console.log(`I am ${this.name}. ${this.height} tall. And ${this.weight} kilos.`);
  14. }
  15. eat(){
  16. console.log('I am eating');
  17. }
  18. sleep(){
  19. console.log('I am sleeping');
  20. }
  21. drink(){
  22. console.log('I am drinking water');
  23. }
  24. }
  25. export { People };

区别

  • ES5 实例的proto.constructor是function
  • ES6 实例的proto.constructor是class
  • 本质是一样,但是过程有不同。来源不一样。

三大特性

  1. 继承
  2. 封装
  3. 多态

继承

子类通过一种特定的形式去使用父类上的成员(对象或实例)属性和方法

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
    })
  }
}

多态

成员方法的重载和和重写,根据不同类执行同一方法,会有不同的表现。
如动物类都被 猫、狗、鸡、鸭继承,牠们都重写动物类叫的方法。
并且执行叫这方法都会有不同的声音。

重载

在同一个类中有写多次同样的方法,方法根据其参数的数量与类型的不同会有不同的表现

重写

子类重写父类的方法

强类型面向对象语言才有意义

面向对象意义

面向对象的方法其实就是程序结构化过程,是对程序结构化(顺序、判断、循环)的抽象,使其更好地管理数据的属性与方法。
使代码易于复用,并且减少防止耦合。
使程序更加有结构、更加易于管理、更加易读易迭代。

面向过程与面向对象对比

面向过程与面向对象