面向对象(Object-Oriented, OO)是一种程序设计方法,目前主流编程语言均可提供面向对象编程。面向对象编程有三个特点:继承、封装、多态。面向对象编程中最重要的是类的概念,通过类可以创建任意多个具有相同属性和方法的对象。在ECMA-262中对象被定义为:“无序属性的集合,其属性可以包含基本值、对象或函数”

JavaScript中,对象的创建主要有两种方式:对象字面量和原型链。两种方式分别实现如下:

var a= {  //对象字面量
  name_tmp: value1,
}
function a() {  //原型链
}
//对象默认带有prototype,即原型对象
a.prototype.name_tmp = value;

1、对象

在JavaScript 中,所有的事物都是对象(Object),如字符串、数值、数组、函数(对,连函数都是对象)。

JavaScript允许自定义对象,创建自定义对象的最简单方式就是就是创建Object实例,然后可为它添加任意数量的属性和方法。

属性类型

ECMAScript中有数据属性和访问属性两种.。

数据属性:

  1. [[Configurable]]可配置
  2. [[Enumerable]]可枚举
  3. [[Writable]]可写

实例:定义属性

Object.defineProperty(a, "name", {  
  configurable: false,  // 将configurable配置为false,如果再进行配置将会抛出错误
  value: "Apple"
});   
 //throws error
 Object.defineProperty(a, "name", {
   configurable: true,
  value: "Apple"
});

访问器属性

  1. [[Configurable]]可配置
  2. [[Enumerable]]可枚举
  3. [[Get]]在读取属性时调用的函数
  4. [[Set]]在写入属性时调用的函数

ECMAScript6之前的版本中属性前面带有下划线的,用于表示只能通过对象方法访问的属性。

ECMAScript5中读取属性的特性使用的是Object.getOwnPropertyDescriptor()方法,API的使用详见手册。

创建对象

虽然Object构造函数或对象字面量都可以用来创建单个对象。如:

var a = new Object();
a.name = "Apple"

构造函数

可以通过构造函数的方式创建对象,直接将属性和方法赋给this对象,如下:

function Apple(name) {
    this.name = name
}
var a1 = new Apple("a1")
var a2 = new Apple("a2")

在真实开发中,对于创建对象,一般使用一些创建型设计模式创建对象,如工厂模式、抽象工厂模式、原型模式等。

2、继承

继承是面向对象语言中非常重要的概念,许多的面向对象语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法的签名;实现继承才继承实际的方法,ECMAScript中将描述的原型链作为实现继承的主要方法。实现原型链有一种基本的方法,代码如下:

function SuperApple(){
    this.value = "SuperApple";
}

SuperApple.prototype.getSuperValue = function(){
    return this.value;
};

function SubApple(){
    this.subValue = "SubApple";
}

//继承SuperApple
SubApple.prototype = new SuperApple();

SubApple.prototype.getSubValue = function (){
    return this.subValue;
};

var instance = new SubApple();
alert(instance.getSuperValue()); 

JavaScript主要通过原型链实现继承。原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型实现的。这样子类就能够访问超类的所有属性和方法,这一点和类的继承很相似,原型链的问题是对象实例共享所有继承的属性和方法,因此不适合单独使用。 解决这个问题的技术是借用构造函数,在子类型的内部调用超类的构造函数。 这样就能做到每个实例都有自己的属性,同时还能保证只是用构造函数模式来定义类型。 使用最多的继承模式是组合继承,这种模式使用原型链来继承共享的属性和方法,通过借用构造函数继承实例属性。

3、多态

多态的实际含义是将同一操作作用于不同对象上面,可以产生不同的解释和不同的运行结果。即给不同对象发送统一消息的时候,这些对象会根据这个信息分别给出不同的反馈。

下面是一段非多态和多态代码:

// 非多态代码实例
var makeSound = function(animal) {  
    if(animal instanceof Dog) {
        console.log('汪汪');
    } else if (animal instanceof Cat) {
        console.log('喵喵');
    }
}
var Dog= function(){}
var Cat= function() {};
makeSound(new Dog());
makeSound(new Cat());

//多态的代码示例
var makeSound = function(animal) {  
    animal.sound();
}
var Dog= function(){}
Dog.prototype.sound = function() {
    console.log('汪汪')
}
var Cat= function() {};
Cat.prototype.sound = function() {
    console.log('喵喵')
}
makeSound(new Dog());
makeSound(new Cat());

JavaScript的函数重载是多态的基础,同时函数又是对象,就可以延伸为对象的多态性,上面对象的多态性把不变的部分隔离开来,那就是所有的动物都会发出叫声,但不同的动物会发出不同的叫声。

使用继承能得到多态性,这是让对象表现多态性最常用的手段。

JavaScript的多态的思想归根结底就是解耦。

4、封装

封装实现的功能就是将数据隐藏。

最通俗最常见的封装数据,由最简单的语法解析来实现,这就是我们通常所知道的,但这所谓的把封装等同于封装数据这是一种非常狭隘的认知。封装还有封装类型的不同,封装的变化等。下面是实例:

//函数封装,解决代码的重复
function Apple(name, size) {  
  return {
    name: name,
    size: size
  }
}
var a1 = Apple("a1", 9);  
var a2 = Apple("a2", 10);
```

---

<center>欢迎关注我的微信公众号</center>
![](http://upload-images.jianshu.io/upload_images/3093748-7c07998b7495defc.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)