Javascript 这个JS单例模式如何/为什么工作?
我遇到了一种非常有趣的方法来创建一个JavaScript单例,它可以用Javascript 这个JS单例模式如何/为什么工作?,javascript,constructor,singleton,this,Javascript,Constructor,Singleton,This,我遇到了一种非常有趣的方法来创建一个JavaScript单例,它可以用new关键字实例化,比如var x=new SingletonClass()。我对变量作用域和闭包等有很好的理解,但是我很难准确地理解为什么这段代码是这样工作的 // EDIT: DO NOT USE this code; see the answers below function SingletonClass() { this.instance = null; var things = []; functio
new
关键字实例化,比如var x=new SingletonClass()
。我对变量作用域和闭包等有很好的理解,但是我很难准确地理解为什么这段代码是这样工作的
// EDIT: DO NOT USE this code; see the answers below
function SingletonClass() {
this.instance = null;
var things = [];
function getInstance() {
if (!this.instance) {
this.instance = {
add: function(thing) {
things.push(thing);
},
list: function() {
console.log(things.toString());
}
};
}
return this.instance;
}
return getInstance();
}
var obj1 = new SingletonClass();
obj1.add("apple");
obj1.list(); //"apple"
var obj2 = new SingletonClass();
obj2.add("banana");
obj1.list(); //"apple,banana"
obj2.list(); //"apple,banana"
obj1.add("carrot");
obj1.list(); //"apple,banana,carrot"
obj2.list(); //"apple,banana,carrot"
我的直觉告诉我,每次实例化一个新的SingletonClass
,this
引用那个新的对象——但是由于构造函数返回一个完全独立的对象,我认为this
会被丢弃。但它仍然存在。怎么用?为什么?
这里有一些我不知道的小细节。有人能照点光吗
编辑:结果显示此代码不正确。它之所以“神奇地”保存对实例的引用,是因为它实际上在全局对象中静默地存储它。充其量这是一种不好的做法,毫无疑问容易出现错误。当构造函数调用
getInstance()
时,getInstance()
中的this
指针被设置为window
,因此this.instance
内部是一个全局变量window.instance
这段代码只使用了一个全局变量window.instance
来跟踪一个实例,可以使它变得更简单
一个更干净的实现方法是这样。一个全局实例存储为函数本身的属性,而不是存储在顶级全局变量中
function SingletonClass() {
var things = [];
// if there is a previous instance, return it
if (SingletonClass.inst) {
return SingletonClass.inst;
}
// if not called with 'new', force it
if (!this instanceof SingletonClass) {
return new SingletonClass();
}
// remember the first created instance
SingletonClass.inst = this;
// add methods that can see our private `things` variable
this.add = function(thing) {
things.push(thing);
}
this.list = function() {
console.log(things.toString());
}
}
它使用的是
窗口
-getInstance
的此
与单例类
的以下内容不同:
function Singleton() {
console.log("This is:", this);
this.instance = null;
function _get() {
console.log("_get's this is:", this);
if (!this.instance) {
console.log("Instance is null");
this.instance = { test: 1 };
}
return this.instance;
}
return _get();
}
var x = new Singleton();
// Output is:
This is:
Singleton
_get's this is:
Window
Instance is null
在JavaScript中实现单例模式的更好方法可能是:
function Singleton() {
// Handle not being called with `new`
if (!this instanceof Singleton) {
return new Singleton();
}
var instantiating = !!Singleton.instance,
things = [];
Singleton.instance = instantiating ? this : Singleton.instance;
if (instantiating) {
this.list = function() {
console.log(things.toString());
}
this.add = function(item) {
things.push(item);
}
}
return Singleton.instance;
}
这仍然受到限制。例如,如果有人用另一个对象替换了Singleton.instance
,那么Singleton
的不同实例之间的测试可能会中断-例如:
var x = new Singleton();
// Har, har, we be breaking things!
Singleton.instance = {"not": "the", "same": "at", "all": true};
var y = new Singleton();
console.log(x === y); // false
不要被函数
getInstance
中的this
所迷惑,this
是全局对象window
,因此您正在创建一个对象并分配给window对象,下次调用构造函数时,您将检查window.instance
是否存在
代码this.instance=null代码>毫无意义,只是让你困惑。移除它不会改变任何事情
下面是来自
当执行代码newfoo(…)
时,会发生以下情况:
将创建一个从foo.prototype继承的新对象
使用指定的参数调用构造函数foo
这将绑定到新创建的对象。新的foo相当于
new foo()
,即如果未指定参数列表,则调用foo
没有争论
构造函数返回的对象成为结果
整个新表达式的属性。如果构造函数没有
显式返回一个对象,使用在步骤1中创建的对象
相反(通常构造函数不返回值,但它们可以
如果要覆盖普通对象创建,请选择这样做
过程。)
注意步骤3,当构造函数中有return语句时,返回的结果将是新表达式的结果。函数中的关键字this
表示一个窗口
对象。在SingletonClass
函数中,它表示SingletonClass
对象
对于词法范围,add
和list
函数总是引用相同的事物
数组。这似乎是关于单例的一个很好的链接:
下面是一个与您在上面所做工作相关的示例:
var myInstance = {
method1: function () {
return "Apple";
},
method2: function () {
return "Orange";
},
banana: "Banana"
};
myInstance.grape = "Grape";
console.log(myInstance.method1(), myInstance.method2(), myInstance.banana, myInstance.grape);
以下是工作链接:
你在哪里找到的?我觉得它坏了。“getInstance”函数是在没有显式接收器的情况下调用的,因此this
将是全局对象(浏览器中的窗口
)。请注意,如果在严格模式下调用,getInstance
中的this
将未定义,引发引用错误(或类似错误)正在尝试评估这个实例。你是对的@Pointy,我现在意识到它(概念上)被破坏了。我从来没有想过,这个
可以指的是全局对象。是的,我现在明白了。我想当然地认为,这个
总是意味着,这个
,我甚至从未想过检查全局对象。我会用一个大的“请勿使用”注释来修改代码,以防有人想出复制它的好主意。;)很遗憾,我只能接受一个答案,因为我也喜欢这个答案(尤其是替换代码)。最初吸引我关注我发布的代码的是,它声称这样做没有向类中添加属性——但事实证明它是snake oil,而是存储在窗口中。