前言
目前我在开发中也接触了一些js的设计模式,但是很多时候不知道自己使用了哪种设计模式或者说该使用何种设计模式。正好手头有一本书 《JavaScript设计模式与开发实践》 ,所以打算好好研读一下,改变一下面向过程的编码方式。
这本书是由曾探大佬撰写(图灵社区主页)。该系列文章我会根据自己的理解结合最新的相关规范来写,有错误的地方还望社区大佬指正。
前置知识
在开始设计模式学习之前,本书的第一章节主要讲了需要了解的关于js的一些知识。
动态类型语言
编程语言按照数据类型可以分为 静态类型语言 和 动态类型语言。
静态类型语言在编译时就已经确定了变量的类型,例如C语言在声明变量时需要指定变量的数据类型,如 int a = 1
;而动态类型语言则是在程序运行时,通过判断被赋予的值来确定类型,js就是一门典型的动态语言。
js
let a;
console.log(typeof a) // undefined
a = 10;
console.log(typeof a) // number
a = 'abc'
console.log(typeof a) // string
相比于静态类型语言,动态类型语言虽然可能会在程序运行时发生类型相关的错误,但是编写的代码更加简洁易懂,同时具有很强的灵活性,这一切建立在 鸭子类型 的概念上。
鸭子类型:如果它走起路来像鸭子,叫起来也是鸭子,那么它就是鸭子。
鸭子类型指导我们只关注对象的行为,而不需要关注对象本身。在动态类型语言的面向对象设计中,鸭子类型的概念至关重要,根据鸭子类型,可以总结出一条原则:面向接口编程,而不是面向实现编程。例如如果一个对象拥有push和pop方法与length属性,并可以通过下标存取属性,在某些情况下,就可以当作一个数组来进行使用。
多态
多态的实际含义是同一操作作用于不同的对象上面,可以产生不同的解释与不同的执行结果,通俗来讲就是给不同的对象发送同一个消息得到不同的反馈,多态的核心思想就是将"不变的"和"可变的"分离开。
举个例子:我养了一只猫和一只狗,我对他们下发"叫"的命令,猫会喵喵喵,狗会汪汪汪,在这个例子中,"叫"的命令是不变的,而具体每一只动物的叫声则是可变的,用js代码实现如下:
js
const makeSound = function (animal) {
if (animal instanceof Cat) {
console.log("喵喵喵");
} else if (animal instanceof Dog) {
console.log("汪汪汪");
}
};
class Cat {}
class Dog {}
makeSound(new Cat()); // 喵喵喵
makeSound(new Dog()); // 汪汪汪
在上述代码中,给Cat
和Dog
两个类发送了相同的makeSound
命令,得到了不同的反馈,但是这一段代码存在一个缺点,就是当我继续增加养的动物时,我需要同步增加makeSound
函数中的内容,当动物种类越来越多时,函数也变得越来越臃肿,所以我们需要把不变的部分单独提取出来,以下是优化后的代码:
js
const makeSound = function (animal) {
if (animal.sound instanceof Function) {
animal.sound(); // "叫"的命令是不变的
}
};
class Cat {
sound() {
console.log("喵喵喵");
}
}
class Dog {
sound() {
console.log("汪汪汪");
}
}
makeSound(new Cat()); // 喵喵喵
makeSound(new Dog()); // 汪汪汪
这一段代码通俗来讲就是给所有能叫的动物发送"叫"的命令,所以当继续增加动物时,只需要在动物这个类中声明"叫"的动作即可。由此可见,某一动物是否能发出叫声,只取决于动物有没有sound
方法,这就是在js这门动态类型语言中,类或者说对象的多态性是与生俱来的。将行为分布在各个对象中,并让这些对象各自负责自己的行为,这正是面向对象设计的优点。
封装
封装的目的主要是为了将一些属性或方法隐藏起来,然后向外提供一些公有方法。通过对对象的封装,对象内部的一些变化对于其他对象而言是透明的,只通过公有方法进行通信,符合"低耦合"原则。
在Java等语言中,提供了public、private等关键字来控制属性的访问权限,但是在js(ES6之前 )中并没有提供这样的关键字,所以我们需要利用到js的一个特性------ 闭包
在红宝书上,闭包的定义如下:闭包是指有权访问另外一个函数作用域中的变量的函数。
所以当我们返回一个函数调用了函数内部变量的时候,此时就产生了闭包,而这个变量也就成为了外部无法访问的私有变量:
js
var Obj = function() {
var _name = "张三"
return {
getName: function() {
return _name
},
setName: function(val) {
_name = val
}
}
}
var obj1 = new Obj()
console.log(obj1.getName()); // 张三
obj1.setName("李四")
console.log(obj1.getName()) // 李四
console.log(obj1._name) // undefined
而在ES6中,我们拥有了更多的方法来实现私有属性,我将在另一篇文章中展开细说。