JS设计模式 -- 面向对象 -- 1 封装

以下内容来自张荣铭《JavaScript设计模式》第2章。

两种编程风格 -- 面向过程与面向对象。

面向对象编程是指将需求抽象成一个对象,然后针对这个对象分析其特征(属性)与动作(方法),这个对象我们称之为类。

面向对象编程思想中,其中一个特点就是封装,即把需要的功能放在一个对象里,便于管理。但由于JavaScript属于解释性的弱类语言,没有经典强类型语言中那种通过class等关键字实现的类的封装方式。

1 封装

1-1 创建一个类

首先声明一个函数,保存在一个变量内。按照编程习惯,代表类的首字母一般会用大写;
然后这个函数(类)的内部通过对this(函数内部自带的一个变量,用于指向当前这个对象)变量添加属性或者方法来实现对类添加属性或者方法。
也可以通过在类的原型(类也是一个对象,也有原型prototype)上添加属性和方法。两种方法:一种是一一为原型对象属性赋值,另一种则是将一个对象赋值给类的原型对象。但两种方法不建议混用。

var Book = function (id, bookname, price) {
  this.id = id;
  this.bookname = bookname;
  this.price = price;
}

Book.prototype.display = function () {};
// 或者
Book.prototype = {
  display: function () {}
}

使用:
new 关键字来实例化(创建)新的对象。使用实例化对象的属性或者方法时,可以通过点语法访问。

JavaScript是一种基于原型prototype的语言,所以在创建一个对象时都有一个原型prototype用于指向其继承的属性、方法。这样通过prototype继承的方法并不是对象自身的,在使用这些方法时需要通过prototype一级一级查找得到。

通过 this 添加的属性、方法是在当前对象上添加的,是该对象自身拥有的,通过类创建一个新对象时,this 指向的属性和方法都会得到相应的创建。通过prototype继承的属性或者方法是每个对象通过prototype访问到,每次通过类创建一个新对象时这些属性和方法不会再次创建。

1-2 constructor属性

当创建一个函数或者对象时都会为其创建一个原型对象prototype,在prototype对象中会创建一个constructor属性。constructor属性指向的就是拥有整个原型对象的函数或对象。

1-3 属性与方法封装

通过JS函数级作用域的特征来实现在函数内部创建外界访问不到的私有化变量和私有化方法。

//  私有属性 与 私有方法  , 特权方法, 对象公有属性和对象共有方法, 构造器
var Book = functiion (id, name, price) {
  //  私有属性
  var num = 1;
  // 私有方法
  function checkId () {};
  //  特权方法
  this.getName = function () {};
  this.getPrice = function () {};
  this.setName = function () {};
  this.setPrice = function () {};
  //  对象公有属性
  this.id = id;
  // 对象公有方法
  this.copy = function () {};
  //  构造器
  this.setName(name);
  this.setPrice(price);
}

通过new关键字实例化对象时,由于对类执行一次,所以类的内部 this上定义的属性和方法复制到新创建的对象上,成为对象公有化的属性和方法,而其中的一些方法能访问到类的私有属性和方法。
通过new关键字创建的新对象,无法获取类外面通过点语法添加的属性和方法,但是可以通过类来使用。因此在类外面通过点语法定义的属性以及方法被称为类的静态共有属性和类的静态共有方法。
而类通过prototype创建的属性或者方法在类实例的对象中是可以通过this访问到的,所有prototype对象中的属性和方法称为共有属性和共有方法。

//  类静态公有属性(对象不能访问)
Book.isChinese = true;
//  类静态公有方法(对象不能访问)
Book.resetTime = function () {
  console.log('new-Time');
}
Book.prototype = {
  //  公有属性
  isJSBook: false,
  //  公有方法
  display: function () {}
}

听过new关键字创建的对象实质是对新对象this的不断赋值,并将prototype指向类的prototype所指向的对象,而类的构造函数外面通过点语法定义的属性方法是不会添加到新创建的对象上去的。因此想要在新创建的对象中使用 isChinese需要通过Book类,而不能通过this, 如 Book.isChinese
通过类的原型prototype上定义的属性在新对象里可以直接使用点方法访问。原因:新对象的prototype和类的prototype指向的是同一个对象。

例:

var b = new Book(11, 'javascript设计模式', 50);
console.log(b.num); //  undefined
console.log(b.isJSBook); // false
console.log(b.id); // 11
console.log(b.isChinese); // undefined

console.log(Book.isChinese); // true
Book.resetTime();  //  new-Time
1-4 闭包

闭包是有权访问另外一个函数作用域中变量的函数,即在一个函数内部创建另外一个函数。

//  利用闭包实现
var Book = (function () {
  // 静态私有变量
  var bookNum = 0;
  //  静态私有方法
  function checkBook (name) { };
  //  返回构造函数
  return function (newId, newName, newPrice) {
    //  私有变量
    var name, price;
    //  私有方法
    function checkID (id) { };
    //  特权方法
    this.getName = function () {};
    this.getPrice = function () {};
    this.setName = function () {};
    this.setPrice = function () {};
    // 公有属性
    this.id = newId;
    //  公有方法
    this.copy = function () {};
    bookNum++;
    if (bookNum > 100)  throw new Error('我们仅出版100本书');
    //  构造器
    this.setName(name);
    this.setPrice(price);
  }
})();

Book.prototype = {
  //  静态公有属性
  isJSBook: false;
  //  静态公有方法
  display: function () {}
}
//  利用闭包实现
var Book = (function () {
  // 静态私有变量
  var bookNum = 0;
  //  静态私有方法
  function checkBook (name) { };
  //  创建类
  function book(newId, newName, newPrice) {
    //  私有变量
    var name, price;
    //  私有方法
    function checkID (id) { };
    //  特权方法
    this.getName = function () {};
    this.getPrice = function () {};
    this.setName = function () {};
    this.setPrice = function () {};
    // 公有属性
    this.id = newId;
    //  公有方法
    this.copy = function () {};
    bookNum++;
    if (bookNum > 100)  throw new Error('我们仅出版100本书');
    //  构造器
    this.setName(name);
    this.setPrice(price);
  }

// 构建原型
  _book.prototype = {
    //   静态公有属性
    isJSBook: false;
    //  静态公有方法
    display: function () {}
  }

//   返回类
return _book;
})();

1-5 安全模式
//  图书安全类
var Book = function (title, time, type) {
  //  判断执行过程中, this是否是当前这个对象(如果是说明是用new创建的)
  if (this instanceof Book) {
    this.title = title;
    this.time = time;
    this.type = type;
    //  否则重新创建这个对象
  } else {
    return new Book(title, time, type);
  }
}

var book = Book('JavaScript', '2014', 'js');
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容