一、基本概念和背景
面向对象程序设计(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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。