以下内容来自张荣铭《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');