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

面向对象的JavaScript

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

动态类型语言和鸭子类型

  • 静态类型语言

    • 优点:
      1. 编译时就能发现类型不匹配的错误,编辑器可以帮助我们提前避免程序在运行期间有可能发生的一些错误。
      2. 如果在程序中明确地规定了数据类型,编译器还可以针对这些信息对程序进行一些优化工作,提高程序执行速度。
    • 缺点:
      1. 迫使程序员依照强契约来编写程序,为每个变量规定数据类型。
      2. 类型的声明也会增加更多的代码,在程序编写过程中,这些细节会让程序员的精力从思考业务逻辑上分散开来。
  • 动态类型语言

    • 优点:

      1. 编写的代码数量更少,看起来也更加简洁,程序员可以把精力更多地放在业务逻辑上面。
    • 缺点:

      1. 无法保证变量的类型,从而在程序的运行期有可能发生跟类型相关的错误。
  • 鸭子类型

鸭子类型指导我们只关注对象的行为,而不关注对象本身,也就是关注 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 对象系统

  • 原型模式和基于原型继承的 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中的原型继承

  • 所有的数据都是对象。
  • 要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它。
  • 对象会记住它的原型。
  • 如果对象无法响应某个请求,它会把这个请求委托给它自己的原型。
  • JavaScript 中的根对象是Object.prototype 对象。 Object.prototype 对象是一个空的对象。
  • 其实并不能说对象有原型,而只能说对象的构造器有原型。

利用 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

原型继承的未来

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

  • 通过 Object.create 来创建对象的效率并不高,通常比通过构造函数创建对象要慢。
  • 通过设置构造器的prototype 来实现原型继承的时候,除了根对象 Object.prototype 本身之外,任何对象都会有一个原型。
  • 通过 Object.create( null ) 可以创建出没有原型的对象。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容