刚开始学习设计模式以前,我是没想说要学习设计模式的,我只是由于想学习JS中的原型prototype知识,一开始我想JS中为何要存在原型这个东西?因而慢慢经过原型而接触到设计模式,后来发现我这个过程是很是正确的,即先学习设计模式,而后在剖析原型及其原理。
设计模式
我一开始,都是经过针对原型的知识点去看去学,发现仍是理解不了,就算理解了原型,能够说只是停留在表面的知道,你并不懂得如何运用原型?后来,我经过用设计模式的角度去理解原型prototype,我才焕然大悟!数组
可是在这一篇章里面,并无说到原型、原型模式,由于若是你要进入设计模式的角度去理解原型,你就必须得了解一下用JS来实现单例模式、工厂模式和构造函数模式,看看Javascript是若是巧妙地实现各类设计模式。浏览器
【单例模式】模块化
var person1 = { "name": "psg", "age": 24 }; var person2 = { "name": "fsf", "age": 22 }; console.log(person1.name); // ->page console.log(person1.age); // ->24 console.log(person2 .name); // ->fsf console.log(person2 .age); // ->22
上面的person1与person2都不是基本数据类型,都是对象数据类型,对象数据类型的做用:把描述同一个事物(同一个对象)的属性和方法放在一个内存空间下,起到了分组的做用(例如person1与person2,都是两个独立开来的栈内存,里面的属性和方法互不影响) -> 因而,咱们把这种分组编写代码的模式叫作“单例模式”。函数
-> 在“单例模式”中,咱们叫person一、person2叫作命名空间学习
单例模式是一种项目开发中常用的模式,由于项目中咱们可使用单例模式进行“模块化”开发。this
命名空间的做用,看下面代码spa
// searchRender 是一个命名空间 var searchRender = { change: function() { this.clickEvent(); //这里this你暂时看不出来是谁,由于只有执行的时候,才知道 } , clickEvent: function() { } }; // 可是想要使用searchRender里面的change方法,你必须以下调用 serachRender.clickEvent(); // 因此change方法里面的this,只能是searchRender
若是你学的比较深刻,看上面的代码你就能看出命名空间的做用是什么了,就是能灵活运用this。prototype
这里使用this的好处:当命名空间更名字的时候,你并不用改里面的调用者,由于this就表明命名空间。设计
总结
->单例模式解决了分组的问题
->可是单例模式的弊端也很明显,由于他仍然是手工做业模式,效率比较低,因而“工厂模式”就站出来解决了这个问题,由于工厂模式能够批量生产。
【工厂模式】
function createJsPerson(name, age) { var obj = {}; obj.name = name; obj.age = age; obj.writeJs = function() { console.log("My name is " + this.name + ", i can write js."); }; return obj; } var p1 = createJsPerson("psg", 24); var p2 = createJsPerson("fsf", 22) p1.writeJs(); // ->My name is psg, i can write js. p2.writeJs(); // ->My name is fsf, i can write js.
这样写,能够减小重复代码的产出,你只须要写好公共的代码,把他放进一个方法里面,而后经过接受不一样的参数,能够建立不一样的对象,效率比单例模式高多了!
接下来,讲一下“构造函数模式”,它的样子跟“工厂模式”很是类似
【构造函数模式】
function CreateJsPerson(name, age) { // var obj = {}; this.name = name; this.age = age; this.writeJs = function() { console.log("My name is " + this.name + ", i can write js."); } // return obj; } var p = new CreateJsPerson("psg", 24); p.writeJs(); // -> My name is psg, i can write js.
看到没有,CreateJsPerson这个方法,根本不会像工厂模式那样最后返回给你一个对象,它不须要,由于当你经过使用new关键字,浏览器会在后台中本身新建一个对象,而后经过this来指向该新建对象,最后自动默认返回一个对象给你。
->其实咱们有时也会不经意用到使用构造函数模式新进实例,例如建立一个数组
// 建立一个数组 var ary = []; // 字面量方式 var arry = new Array(); // 实例建立的方式-》构造函数模式执行的方式
->知识点一: 构造函数模式和工厂模式的区别?
1)执行的时候
普通函数执行 -> createJsPerson() -> 这时候,createJsPerson是一个普通函数名
构造函数模式 -> new CreateJsPerson() -> 经过new执行后,CreateJsPerson就是一个类 -> 开头大写是由于约定了,大写开头的函数就是表示一个类
new 出来的返回值(p),就是CreateJsPerson这个类的一个实例
2)在函数执行的时候
相同点 -> 都是造成私有做用域,而后形参赋值 -> 预解释 -> 代码从上到下执行(类和普通函数同样,它也有普通函数的一面)
不一样点 -> 在代码执行以前, 不用本身再手动建立对象,浏览器会默认的建立一个对象数据类型的值(类的实例),并做为函数返回值自动返回。
->知识点二: 构造函数也是函数数据类型
在JS中,全部类都是函数数据类型,createJsPerson是函数数据类型, new CreateJsPerson也是函数数据类型,CreateJsPerson它经过new执行变成了一类,但它自己也是一个普通的函数。
可是,JS中全部类的实例都是对象数据类型。
->知识点三: 若是给构造函数里面添加私有变量,它返回值(实例)会存在此变量吗?
unction Fn() { var num = 10; } var obj = new Fn; console.log(num); // -> undefined console.log(obj.num); // -> undefined
上面例子能够看出,这里的num只是函数私有做用域里面的一个私有变量,它跟实例没有任何关系。
->知识点四: 若是给构造函数直接返回一个对象,或者直接返回一个基本数据类型,那个实例到底仍是不是浏览器默认返回的实例呢?
1)手动添加自动返回基本数据类型
function Fn() { var num = 10; return 100; // 代码无效 } var f1 = new Fn; console.log(f1); // -> Object,这个Object就是空的,也是浏览器默认返回的对象 // 也就是说,return 100 这句代码无效
2)手动添加自动返回对象数据类型
function Fn() { var num = 10; return {name: 'psg'}; // 代码有效 } var f1 = new Fn; console.log(f1); // -> Object,这个Object就是 {name: 'psg'} // 也就是说,强制返回的手动添加的实例对象,彻底KO覆盖掉浏览器默认返回的实例对象
总结上面两点在构造函数模式当中,浏览器会默认把咱们的实例返回(返回的是一个对象数据类型的值)
// 可是若是咱们手动返回值,分两种状况
// ******状况一,返回的是一个基本数据类型的值,当前实例不变
// 例如: return 100
// 那么浏览器返回的值仍然是浏览器默认建立的对象
// ******状况二,返回的是一个引用数据类型的值,当前实例会被本身返回的值覆盖掉
// 例如: return {name: "psg"}
// 那么原先浏览器返回的默认值,将会被本身的手动建立返回的对象给覆盖掉。注意,必定要是对象哦
->知识点五: 检测某一个实例是否属于这个类 -> instanceof
下面这个例子颇有意思!!结合了知识点四。
function Fn() { var num = 10; // return 100 return {name: 'psg'} } var f1 = new Fn; console.log(f1 instanceof Fn); // ->false , 由于f1不是浏览器建立的默认实例
->知识点六:检测某个属性是否属于某个实例,检测某个属性是否为该对象的公有属性?
function Fn1() { this.x = 100; this.getX = function() { console.log(this.x); } } var fun1 = new Fn1; var fun2 = new Fn1; console.log(fun1.getX() === fun2.getX()); // -> fun1和fun2都是Fn1这个类的实例,都拥有x和getX这两个属性,可是这两个属性是各自私有的属性 // 可是如何检测某一个属性是否属于某一个实例 // in: 检测某一个属性是否属于某一个实例,不论是私有属性仍是公有属性,用in都是用来检测这个属性是否属于这个对象 console.log("getX" in fun1); // -> true // hasOwnProperty: 用来检测某个属性是否为这个对象的私有属性,这个方法只能检测私有的属性 console.log(fun1.hasOwnProperty("getX")); // -> true // 思考,检测某一个属性是否为该对象的“公有属性”,本身写一个 hasPubProperty function hasPubProperty(obj, attr) { // 首先保证它是一个对象的属性,而且不是私有属性,那就确定就是公有属性了 return ((attr in obj) && !obj.hasOwnProperty(attr)); } console.log(hasPubProperty(fun1, "getX")); // -> false;
这一章,主要讲了单例模式、工厂模式和构造函数模式,同时也由于工厂模式与构造函数模式代码上很是类似,可是实现原理彻底不相同,因此也讲了构造函数模式实例化对象的原理过程,以及他们两的区别。
下一章 的《【设计模式+原型理解】第二章:JS中为何要存在原型prototype这个东西?》会讲到原型模式,准确来讲是基于构造函数模式的原型模式,由于只有讲到构造函数模式,你才知道构造函数的优缺点,原型模式就是为了解决并改进构造函数的。
待续....