定义:保证一个类(构造函数)仅有一个实例,并提供一个访问它的全局访问点。
1.作者举了最简单的两个例子:
//首先在这里定义了一个构造函数,singleton的意思是单身汉。
var Singleton = function (name) {
this.name = name;
this.instance = null;
//instance的意思是例子
};
Singleton.prototype.getName = function () {
alert(this.name);
};
//下面要注意,getInstance方法是直接写在Singleton身上的。
//而且,函数里面的this也是指向Singleton的。
//我读到这里的时候会下意识的这样想:不都是说构造函数中的this是指向实例的么?
//其实是这样的,大家常说的构造函数的this指向实例,是因为new的关系。
//new的内部实现,把构造函数apply给了实例,所以才会改变this的指向。
//所以下面的代码this.instance是可以取到的。
Singleton.getInstance = function (name) {
if (!this.instance) {
this.instance = new Singleton(name)
}
return this.instance;
}
var a = Singleton.getInstance('cyw');
var b = Singleton.getInstance('mn');
/* 在这里,我们来看一下,这会产生什么效果? */
console.log(a === b); // true
a.getName();//'cyw'
b.getName();//'cyw'
/*
我们发现,不管getInstance方法执行了几次,他都只是返回第一次构造的实例。
这是因为,this.instance充当了一个标记的角色。
它在第一次执行之后,就永远的被改写成了实例。
以后再怎么调用,都只是返回那个实例。
*/
再来看下一个例子,和第一个很像。
var Singleton = function (name) {
this.name = name;
};
Singleton.prototype.getName = function () {
alert(this.name);
};
Singleton.getInstance = (function () {
var instance = null;
return function (name) {
if (!instance) {
instance = new Singleton(name);
}
return instance;
}
})();
var a = Singleton.getInstance('cyw');
var b = Singleton.getInstance('mn');
console.log(a === b);// true
/*
* 这里的实现方式略有不用
* 在第三个函数直接使用了一个立即执行函数进行包裹
* 所以拿到的是里面那个匿名函数
* 匿名函数又使用了立即执行函数里面的变量instance:这个就是一个闭包
*/
- 读到这里我们学会了,判断 + 改写 + return可以实现这种效果。
- 但是作者又说这种实现方式很不好
-
这个构造函数的'透明性'差
b8389b504fc2d5624fce51daef1190ef77c66cc7.jpg - 还要使用Singleton.getInstance方法才能获得实例。
新目标1:改写这个函数,要求直接使用构造函数就能获得单例
写一个在页面创建一个唯一的DIV的例子。
var CreateDiv =(function () {
var instance;
var CreateDiv = function (html) {
if (instance) {
return instance;
}
this.html = html;
this.init();
return instance = this;//这句话就是intance = this;return instance;
};
CreateDiv.prototype.init = function () {
var div = document.createElement('div');
div.innerHTML = this.html;
document.body.appendChild(div);
};
return CreateDiv;
})();
var a = new CreateDiv('hello');
var b = new CreateDiv('world');
console.log(a === b);
/*
* 读这段代码,我们可以发现 ,就是把本来外面的方法,拿到构造函数里面了。
* 还是利用的立即执行函数
* 外面的函数名和里面的函数名字一样可能会带来理解上的误区,你当然可以把里面的换一个名字
* 真正的构造函数写在里面 ,外面的构造函数拿到的是里面的构造函数的引用
* 让 instance = this; 因为this就是实例 */
- 读到这里,作者又觉得这个方法的实现有问题
var CreateDiv = function (html) {
if (instance) {
return instance;
}
this.html = html;
this.init();
return instance = this;//这句话就是intance = this;return instance;
};
这个函数同时做了两件事情:
1.创建对象和初始化init方法(我们可以注意到定义在原型上的init方法,是在构造函数中被调用的。)
2.确保了只有一个实例。
这违反了"单一职责原则",不方便复用,而且看起来很怪。
ps.注意大牛的思考方式,这是最重要的。
我们使用代理类的方式来解决这种情况:
var CreateDiv = function (html) {
this.html = html;
};
CreateDiv.prototype.init = function () {
var div = document.createElement('div');
div.innerHTML = this.html;
document.body.appendChild(div);
}
// 首先这就是一个普通的类,他是实现的是职责就是创建div
//现在我们来引入代理类,来实现单例模式
var ProxySingletonCreateDiv = (function () {
var instance;
return function (html) {
if (!instance) {
return new CreateDiv(html);
}
return instance;
}
})()
var a = new ProxySingletonCreateDiv('hello');
var b = new ProxySingletonCreateDiv('world');
console.log(a === b);//true
// 这几句代码写来写去,好像都在哪见过,但是从新的组合和设计确实实现了作者口中的优化。
javascript中的单例模式
在javascript中我们并不需要如此的依赖类的概念
其实
var a = {};
就是一个单例模式的例子。
首先它是独一无二的。
其次他还是全局可以访问的。
但是a这个引用太危险了,他随时可能在编程的过程中被改写。
- 为了避免这种情况,我们可以使用下面两种小技巧。
1.命名空间
var namespace = {
a: function () {
console.log(1);
},
b: function () {
console.log(2);
}
}
我们把a和b写成对象的属性,这样就使其远离了全局作用域。
好玩的东西: 动态创建命名空间
var MyApp = {};
MyApp.namespace = function (name) {
var current = MyApp;
var parts = name.split('.');
for (var i in parts) {
if (!current[parts[i]]) {
current[parts[i]] = {}
}
current = current[parts[i]];
}
};
MyApp.namespace('one');
MyApp.namespace('two.three.four');
MyApp.namespace('two.three.five');
相当于:
var MyApp = {
one: {},
two: {
three: {
four: {},
five: {}
}
}
}
2.使用闭包封装私有变量
var user = (function () {
var _name = 'cyw';
var _age = '25';
return {
getUserInfo: function () {
return _age + '-' + _name;
}
}
})()
下划线'_'是私有变量的常用命名规则。
惰性单例
惰性的意思就是,需要的时候才创建实例
以创建登录窗口为例
我们把单例模式不变的逻辑单独提取出来
var getSingle = function (fn) {
var result;
return function () {
return result || (result = fn.apply(this, arguments));
}
}
//再写一个简单不过的创建div的方法。
var createLoginLayer = function () {
var div = document.createElement('div');
div.innerHTML = '我是登录窗口';
div.style.display = 'none';
document.body.appendChild(div);
return div;
}
/* 两个方法都是单一职能,结合起来就形成了惰性单例模式 */
var createSingleLoginLayer = getSingle(createLoginLayer);
var div = createSingleLoginLayer();
- 希望这个读书笔记可以坚持下来。
- 写第一篇就有一种感受,现在的mvvm框架功能太强太。
- 如果用框架来写,文中的好多技巧都无处发挥了。
- 一个vue的v-if还用什么单例模式(也可能是我现在认识比较浅薄)
- 虽然能看的懂代码,但我希望更能学会大牛的思考方式,希望能得到进步
- 还要继续找工作,初级前端的工作太难找了。
网友评论