《JavaScript设计模式与开发实践》之面向对象的JavaS

2017-06-27  本文已影响0人  我是白夜

面向对象的JavaScript

JavaScript 没有提供传统面向对象语言中的类式继承,而是通过原型委托的方式来实现对象与对象之间的继承。JavaScript 也没有在语言层面提供对抽象类和接口的支持。正因为存在这些跟传统面向对象语言不一致的地方,我们在用设计模式编写代码的时候,更要跟传统面向对象语言加以区别。所以在正式学习设计模式之前,我们有必要先了解一些 JavaScript在面向对象方面的知识。

动态类型语言和鸭子类型

鸭子类型指导我们只关注对象的行为,而不关注对象本身,也就是关注 HAS-A, 而不是 IS-A。

IS--A基于类继承或接口实现,IS-A是表达这句话的一种方式:“这个东西是那个东西的一种”。例如:野马是一种马。(是一个)
HAS-A关系是基于用法(即引用)而不是继承。换句话说,如果类A中的代码具有对类B实例的引用,则“类A HAS-A类B”。例如:马有缰绳。(有一个)

下面这个故事更清晰的阐明此概念

从前在 JavaScript王国里,有一个国王,他觉得世界上最美妙的声音就是鸭子的叫声,于是国王召集大臣,要组建一个 1000 只鸭子组成的合唱团。大臣们找遍了全国,终于找到 999只鸭子,但是始终还差一只,最后大臣发现有一只非常特别的鸡,它的叫声跟鸭子一模一样,于是这只鸡就成为了合唱团的最后一员。

多态

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

多态背后的思想是将“做什么”和“谁去做以及怎样去做”分离开来,也就是将“不变的事物”与 “可能改变的事物”分离开来。

把不变的部分隔离出来,把可变的部分封装起来,这给予了我们扩展程序的能力,程序看起来是可生长的,也是符合开放 — 封闭原则的,相对于修改代码来说,仅仅增加代码就能完成同样的功能,这显然优雅和安全得多。

一段“多态”的JavaScript代码

 var makeSound = function(animal) {
   if (animal instanceof Duck) {
     console.log('嘎嘎嘎');
   } else if (animal instanceof Chicken) {
     console.log("咯咯哒");
   }
 }

var Duck = function() {};
var Chicken = function() {};

 makeSound(new Duck());
 makeSound(new Chicken());

对象的多态性

var Duck = function() {},
    Chicken = function() {},
    Dog = function() {},
    mackSoundObj = function(animal) {
      animal.sound();
};
Duck.prototype.sound = function() {
  console.log('嘎嘎嘎');
};
Chicken.prototype.sound = function() {
  console.log('咯咯哒');
};
Dog.prototype.sound = function() {
  console.log('汪汪汪');
};
mackSoundObj(new Duck());
mackSoundObj(new Chicken());
mackSoundObj(new Dog());

类型检查和多态

使用继承得到多态效果

  1. 实现继承
  2. 接口继承

JavaScript的多态

多态在面向对象程序设计中的作用

设计模式与多态

封装

封装的目的是将信息隐藏。一般而言,我们讨论的封装是封装数据和封装实现,其实封装应该被视为“任何形式的封装”

  1. 封装数据和封装实现
  2. 封装类型和封装变化

封装数据

var myObject = (function() {
  var __name = 'sven'; // 私有(private)变量
  return {
    getName: function() { // 公开(public)方法
      return __name;
    }
  }
})();
console.log(myObject.getName()); // 输出:sven
console.log(myObject.__name) // 输出:undefined

封装实现

封装不仅仅是隐藏数据,还包括隐藏实现细节、设计细节以及隐藏对象的类型等。

封装类型

封装类型是静态类型语言中一种重要的封装方式。一般而言,封装类型是通过抽象类和接口来进行的 。把对象的真正类型隐藏在抽象类或者接口之后,相比对象的类型,客户更关心对象的行为。在许多静态语言的设计模式中,想方设法地去隐藏对象的类型,也是促使这些模式诞生的原因之一。比如工厂方法模式、组合模式等。

封装变化

“找到变化并封装之”。

“考虑你的设计中哪些地方可能变化,这种方式与关注会导致重新设计的原因相反。它不是考虑什么时候会迫使你的设计改变,而是考虑你怎样才能够在不重新设计的情况下进行改变。这里的关键在于封装发生变化的概念,这是许多设计模式的主题。”

——《设计模式》

创建型模式:封装创建对象的变化

结构型模式:封装的是对象之间的组合关系

行为型模式:封装的是对象的行为变化

JavaScript 对象系统

在以类为中心的面向对象编程语言中,类和对象的关系可以想象成铸模和铸件的关系,对象总是从类中创建而来。而在原型编程的思想中,类并不是必需的,对象未必需要从类中创建而来,一个对象是通过克隆另外一个对象所得到的。

原型模式不单是一种设计模式,也被称为一种编程泛型。

使用克隆的原型模式

 var Plane = function() {
   this.blood = 100; //血量
   this.attackLevel = 1; // 攻击等级
   this.defenseLevel = 1; //防御等级
 };

var plane = new Plane();

plane.blood = 500;
plane.attackLevel = 10;
plane.defenseLevel = 4;

//不支持ES5的兼容处理
Object.create = Object.creat || function(obj) {
  var F = function() {};
  F.prototype = obj;
  return new F();
}

var clonePlane = Object.create(plane);

console.log(clonePlane);

克隆是创建对象的手段

利用“吸血鬼系统”例子可以映射Javascript的原型链,根对象(Object)为“吸血鬼祖先”。

体验Io语言

原型编程范型的一些规则

基于原型链的委托机制就是原型继承的本质。

JavaScript中的原型继承

利用 ECMAScript 5提供的 Object.getPrototypeOf 来查看对象的原型

var obj1 = new Object();
var obj2 = {};

console.log(Object.getPrototypeOf(obj1) === Object.prototype); // 输出:true
console.log(Object.getPrototypeOf(obj2) === Object.prototype); // 输出:true

new 运算符从构造器中得到一个对象

function Personal(name){
    this.name=name;
}
Personal.prototype.getName = function(){
  return this.name;
}
var aaa = new Personal('steven');
console.log(aaa.name);//steven
console.log(aaa.getName());//steven
console.log(Object.getPrototypeOf(aaa) === Personal.prototype); //true

原型继承的未来

设计模式是对语言不足的补充,如果要使用设计模式,不如去找一门更好的语言。

上一篇 下一篇

猜你喜欢

热点阅读