1

一、基本概念和背景

面向对象程序设计(OOP:Object-oriented programming)是一种程序设计范型,同时也是一种程序开发的方法。对象指的是类的实例。
面向对象(Object Oriented,OO)是一种对现实世界理解和抽象的方法,是计算机编程技术[1] 发展到一定阶段后的产物。

面向对象的三大特点(封装,继承,多态)缺一不可。通常“基于对象”是使用对象,但是无法利用现有的对象模板产生新的对象类型,继而产生新的对象,也就是说“基于对象”没有继承的特点。而“多态”表示为运行时类型确定(父类类型的子类对象实例),动态绑定执行对象,没有了继承的概念也就无从谈论“多态”。
示例:绘制一个图形,图形已有为三角形、圆;
Java 示例

// 父类:抽象、封装
public class  Graph {
 void draw() {
  System.out.println("draw a Graph");
 }
}
// 继承:子类type1
public class Triangle extends Graph {
 @Override
 void draw() {
  System.out.println("draw a Triangle");
 }
}
 
// 继承:子类type2
public class Circle extends Graph {
 @Override
 void draw() {
  System.out.println("draw a Circle");
 }
}
// 多态:运行时类型确定
public class Drawer {
 static void draw(Graph a) {
  a.draw();
}

// 运行调用
public static void main(String args[]) {
  Graph p = new Triangle();
  Drawer.draw(p); // "draw a Triangle"
  p = new Circle();
  Drawer.draw(p); //"draw a Circle"
}

Javascript ES5示例

// 封装
function Graph(){}
Graph.prototype.draw = function (){
  console.log("draw a Graph");
}
// 原型链引用
function Trianggle(){}
Trianggle.prototype = new Graph();
Trianggle.prototype.draw =  function (){
  console.log("draw a Trianggle");
}
// 原型链引用
function Circle(){}
Circle.prototype = new Graph();
Circle.prototype.draw =  function (){
  console.log("draw a Circle");
}
// 相对多态:无显示对象类型
function Drawer(obj){
  obj.draw();
}
// 运行时类型确定
var p = new Trianggle();
Drawer(p);
p = new Circle();
Drawer(p); 

大多面向对象语言中,面向对象一般基于面向’类‘的设计。类是代码抽象的模式之一:实例化(instantiation)、继承(inheritance)、和 (相对)多态(polymorphism)。类和实例的关系好比房屋建造。建筑师会绘制建筑蓝图,建筑工人根据蓝图落地成实际建筑,建筑就是蓝图的复制(概念和物理上的复制)。类就是建筑的抽象,含有一个建筑的所有特性。实例就是实际的建筑,包含了门、窗以及实际材料、地理位置。类通过复制操作被实例化为对象形式,实例间无直接关系。但这无法直接对应到 JavaScript 的实现机制,因为 Javascript 没有实质类的概念,ES6的 class也是一种语法糖1

二、JavaScript 封装方法

面向对象编程强调的是数据和操作数据行为的的相互关联,因此最好的设计就是把数据和它相关的行为打包(封装)起来。
封装(Encapsulation)是指一种将抽象性函数式接口的实现细节部份包装、隐藏起来的方法

2.1 好处

(1) 良好的封装能降低耦合度
(2) 方面数据统一管理、对象内部结构灵活修改
(3) 对其成员有更精确的控制,易于扩展
(4) 隐藏实现信息、细节,只暴露需要的 API

2.2 封装模式(创建对象模式) 构造函数2 & 原型链

(1)工厂模式 - 函数 【内部对象创建属性方法并返回对象,使用this】

 function createPerson(name, age, job){
        var o = new Object();
        o.name = name;
        o.age = age;
        o.job = job;
        o.sayName = function(){
            alert(this.name);
        }
        return o;
 }
var person1 = createPerson(“John”, 18, “Teacher");
var person2 = createPerson(“Greg”, 30, “Doctor”);

(2) 构造函数模式【内部 this创建属性、方法;无 return; 通过改变 this指向创建实例】

**原生构造函数**:隐式创建对象,属性、方法赋值给 this对象,无 return;
function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    }
}
var person1 = new Person(“John”, 18, “Teacher");
var person2 = new Person(“Greg”, 30, “Doctor”);

构造函数: new 显示创建 对象

步骤1: 创建一个新对象
步骤2:讲构造函数的作用是赋值给新对象 (this 只向新对象 词法作用域发生改变)
步骤3:执行构造函数的代码 (为新对象 添加属性 )
步骤4:返回新对象

function Person(name, age, job){
   this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    }
}

构造函数作为函数; 问题:作用域改变,每个传入的对象重建一遍;)
var person1 = new Object();

 Person.call( person1, “John”, 18, “Teacher");

var person2 = new Person(“Greg”, 30, “Doctor”);

(3) 原型模式 【原型对象创建属性、方法】

   构造函数无传参, 共享原型对象的属性,其中一个实例对原型对象的引用类型属性做修改,影响所有其他实例对该属性的使用;
 (* 引用类型属性受影响  内存分配机制不同引起)
function Person(){}

Person.prototype.name =  'Nicholas';
Person.prototype.age = 27;
Person.prototype.job = 'Doctor';
Person.prototype.friends =  ['Lily', 'John', 'Alex']
Person.prototype.sayName = function() {
     console.log(this.name);
}
Person.prototype.getFriends = function() {
     console.log(this.friends);
}
 var person1 = new Person();
 person1.name = "person1";

 var person2 = new Person();
 person2.name = "person2";
 person2.sayName();  //“person2"
 person2.getFriends(); // ["Lily", "John", "Alex"]
 person1.friends.push(“Van");
 person1.sayName(); //“person1"
 person2.getFriends();  //["Lily", "John", "Alex", "Van"]

(4) 构造函数 & 原型模式结合 【构造函数独立属性方法,原型对象创建公用属性、方法】
好处:构造函数模式 定义实例个性化属性;原型模式定义方法和共享属性;功能模块化 职责单一

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = [‘Lily', 'John', 'Alex'];
}

Person.prototype ={
    constructor: Person,
      sayName: function(){
         alert(this.name);
     }
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
person1.friends.push('Van');
var person2 = new Person(“Greg", 27, "Doctor");

(5) 动态原型模式 (使用:功能模块化)【构造函数独立属性方法,原型对象按需创建公用属性、方法】

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = [‘Lily’, 'John', 'Alex'];
    
    if(typeof this.sayName != ‘function’){
            Person.prototype.sayName = function(){
                    alert(this.name);
            };
    }
}

(6) 寄生构造函数模式 -【 已有对象扩展 - 原型链 】 (使用:第三方库扩展 )

   1.写法上和工厂模式一样 调用时多加 new  
   2.此模式创建的对象 与构造函数无关系, instanceof操作符无意义
function  Person(name, age, job){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){
        alert(this.name);
    }
    return o;
}
 // 工厂模式
// var person1 =  Person(“John”, 18, “Teacher”);
// 寄生模式
 var friend  =  new Person(“John”, 18, “Teacher”); // 返回新对象的实例

(7) 稳妥模式 【 内部建对象不用 this 闭包 - 词法作用域 】(稳妥对象-无公共属性,方法不引用 this)(使用:适用于安全环境 - 禁用 this 和 new)

 1.写法上和寄生模式类似 , 除方法不使用 this  
 2.此模式创建的对象 与构造函数无关系, instanceof操作符无意义
function  Person(name, age, job){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){
        alert(name); //无 this
    }
    return o;
}
var friend = Person(“John”, 18, “Teacher”); //无 new

  1. 类实质是对象复制的概念,JavaScript 则是一种对象委托引用的概念,对引用类型变量变更其[prototype]依然拥有共享功能。
  2. 构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载。

凌乐天
28 声望2 粉丝

前端路上蹦哒的屌丝