本文共 6161 字,大约阅读时间需要 20 分钟。
var str1 = "abc";var str2 = "abc";console.log(str1 == str2)console.log(str1 === str2)// 上面的代码实际上是执行了这个操作// var str = String("abc")
// 那么如果var str1 = String("abc");var str2 = new String("abc");console.log(str1 == str2)console.log(str1 === str2)// 我们可以在控制台输出一下 str1 和 str2,一看就知道为什么不一样了。
我们可以得到结论:
==
比较的是变量(对象)的值===
比较的是变量(对象)的地址new
关键字,一定是在堆中开辟一块新内存var myDiv = document.getElementById("myDiv");
typeof mydiv
的方式查看Object
那就一定是存放在堆空间的,其他存放在常量池。myDiv
实际上是 div
标签的实例myDiv.constructor
的方法看这个实例到底是哪个实例类那么能不能用 new HTMLDivElement()
new 一个新的 HTMLDiv 类呢?
不管对错,先猜猜看。
实际操作一遍发现浏览器报错了,浏览器不允许你私自 new 一个 HTMLDiv 类
那么该如何 new 出一个 HTMLDiv 类呢?--------------------------------------我是分割线--------------------------------------
浏览器提供了这么一个方法 document.createElement("div")
,通过这种方式,它能够在内存中创建一个 DOM 对象,并且是 HTMLDivElement 的对象。
var myDiv1 = document.createElement("div");
这是一个典型的工厂模式。
我们可以发现myDiv.constructor
和 myDiv1.constructor
一模一样,这就说明,这两个是同样一个模板下所产生的不同的实例 但是问题来了,我希望我的模板下有且只有一个实例,节约内存,例如 body
标签,全局唯一,这个时候该怎么设计?
--------------------------------------我是引导君--------------------------------------
JavaScript 有一个特性,就是动态对象可以随意复制其行为和属性。
大家来说出自己的理解。
不要着急,我先继续往下讲。
var obj = {};// 这就是一个简单的单例,同时也是 Object 的一个实例,我们可以在这里面扩展任何我们想要的属性// 例如 var Obj = {name: "abc",age: 1}
这段代码在我们项目代码里非常普遍,但是这样的实例,也是单例模式,有个不好的地方,那就是,这个单例根本无法扩展,而且使用起来也非常不安全,因为我们可以随时改变这个里面的内容。
--------------------------------------我是不正经的正题君--------------------------------------
那么,单例模式在 JavaScript 中该如何设计呢?
(function(){})()// 熟悉我的写法的人肯定知道// 这段代码是创建一个匿名函数,并且立即调用// 那么这么写到底有什么用呢?
这种写法是有用的,它帮我实现了一个闭包,这段代码的 {}
中帮我实现了一个闭包临时作用域。
var SingleTest = (function(){ // 这个 return 的 function 就是刚刚说的模板类 return function(){ console.log("进入构造器函数"); }})()// 这个时候我们就可以var i1 = new SingleTest();var i2 = new SingleTest();console.log(i1===i2) // false
大家注意,有基础的应该都知道,函数在 JavaScript 里面有两种使用方式,一种是函数调用(小写),另一种是构造器(大写),行业潜规则。
但是我希望不管我怎么 new ,我都想使用内存中的同一块地址,也就是i1===i2
,那么该怎么做呢?
var SingleTest = (function(){ var _instance = null; return function(){ console.log("进入构造器函数"); if (!_instance) { console.log("第一次 new,局部变量(实例)_instance 为 null"); _instance = this; return _instance; } else { console.log("不是第一次 new,局部变量(实例)_instance 不为 null,直接返回"); return _instance; } }})()
大家先理解理解这段代码。
this
代表的是当前创建的这块内存空间的引用,所以定义的属性或者方法都可以用 this
来操作。
这个时候 console.log(i1===i2)
看看会发生什么。
--------------------------------------我是不正经的参数君--------------------------------------
如果我要给这个单例传一个参数,我们要访问实例里面的 name 属性怎么办?
var SingleTest = (function(){ var _instance = null; return function(ops){ // 我们经常会这么做 // 通过这种方式我们可以过滤掉不传参数带来的空引用问题 ops = ops || {}; if (!_instance) { _instance = this; // 通过 for 循环,遍历迭代我们的参数 for (var prop in ops) { _instance[prop] = ops[prop]; } return _instance; } else { for (var prop in ops) { _instance[prop] = ops[prop]; } return _instance; } }})()var i1 = new SingleTest({name:"zhangsan"});var i2 = new SingleTest({name:"lisi"});console.log(i1.name);// 那么输出的值是多少呢?// 很明显上面写了两个 for 循环,我们代码里也经常有这种情况发生,这个时候该怎么优化?
var SingleTest = (function(){ var _instance = null; var _default = {} // 封装 for 循环 function _init(ops) { for (var prop in ops) { this[prop] = ops[prop]; } } return function(ops=_default){// es6支持 // ops = ops || {}; 这种方式已经 out 了 if (!_instance) { _instance = this; _init.call(_instance, ops); } else { _init.call(_instance, ops); } return _instance; }})()var i1 = new SingleTest({name:"zhangsan"});var i2 = new SingleTest({name:"lisi"});console.log(i1.name);
这个代码已经优化度很高了,但是还可以进行优化,比如说,如果我这里面不止 _init
方法,还有其他方法,例如,function _method1(){}
function _method2(){}
等,在函数体内进行调用的时候,你会发现,这么设计并不是一个好主意。
--------------------------------------我是正经的优化君--------------------------------------
简单来说,就是将这些方法加到原型链中。
var SingleTest = (function(){ var _instance = null; var _default = {} function SingleInstance(ops=_default){ if (!_instance) { _instance = this; this._init(ops); } else { _instance._init(ops); } return _instance; } // 将方法加到原型中去 // _的意思是,私有属性或方法,行业规则。 SingleInstance.prototype._init = function(ops) { for (var prop in ops) { this[prop] = ops[prop]; } } return SingleInstance;})()var i1 = new SingleTest({name:"zhangsan"});var i2 = new SingleTest({name:"lisi"});console.log(i1.name);
你会发现,这样做代码量并没有减少多少,但是优点是,在写入其他方法的时候,可以用 this
直接相互调用已存在的方法。
new
出不同的实例,直接对 _instance
做处理就好了,因为我已经把这个单例打包成了闭包,不会影响外面的调用者。 但是这样还有一个 bug !
那就是如果有些人不上规矩,想直接调用SingleTest({name:"mazi"})
,这个时候你会发现,控制台报错了。 那么该如何优化,让这种调用也兼容呢? --------------------------------------我是万恶的bug君--------------------------------------
原因就是,这样做是直接调用这个函数堆栈,这就意味着,当前函数的作用域并不是堆里面的 this
。
奔主题。
var SingleTest = (function(){ var _instance = null; var _default = {} function SingleInstance(ops=_default){ // instanceof 是表示 this 是不是 SingleInstance 的实例 if (this instanceof SingleInstance) { if (!_instance) { _instance = this; this._init(ops); } else { _instance._init(ops); } } else { if (!_instance) { _instance = new SingleInstance(); _instance._init(ops); } else { _instance._init(ops); } } return _instance; } SingleInstance.prototype._init = function(ops) { for (var prop in ops) { this[prop] = ops[prop]; } } return SingleInstance;})()var i0 = SingleTest({name:"wangwu"})var i1 = new SingleTest({name:"zhangsan"});var i2 = new SingleTest({name:"lisi"});console.log(i0 === i1);console.log(i0 === i2);
至此,这个单例已经优化完毕。
或许还可以继续优化,但是这个不重要了,讲到这足够了。 我想说的是,你们不要记代码,没用的,试着去理解我的思路,思路是通用的。转载地址:http://kxnbo.baihongyu.com/