JavaScript中的私有/静态属性介绍


Posted in Javascript onJuly 26, 2012

•模拟块级作用域
大家都知道在JavaScript中没有块级作用域的概念,我们可以通过使用闭包来模拟实现块级作用域,看下面的示例:

(function () { 
for (var i = 0; i < 10; i++) { 
//Do Nothing 
} 
alert(i); //输出10 
})();

第6行可以访问到for循环块中的变量i,如果我们稍微修改以上代码,把for循环块放置在闭包中,情况就不一样了:
(function () { 
(function () { 
for (var i = 0; i < 10; i++) { 
//Do Nothing 
} 
})(); 
alert(i); //Error: 'i' is undefined 
})();

在第8行访问变了i时,出现错误,实现了我们想要的块级作用域。
•私有属性
在JavaScript中没有块级作用域的概念,同样也没有私有属性的概念,但是存在私有变量。如果我们想把一些数据封装隐藏起来要怎么做呢?想必大家可能已经想到了,可以通过使用闭包+私有变量的方式来实现对象的私有属性。
<1>.实例私有属性
实例私有属性的特点就是每个对象都会包含独立的属性,对象和对象之间没有共享。为了实现这个目标,可以在构造函数中增加一个私有变量,然后定义公共方法来访问这个私有变量,就如同其他OO语言的setter和getter一样,下列示例就实现了实例的私有属性:
//实例私有变量 
function MyObject(name) { 
//定义私有变量 
//注意:此处没有用this.name,如果使用this.name变成公共属性了 
var privateName = name; 
//定义私有熟悉 
var privateFunction = function () { 
return "Private Function"; 
} 
//公共方法访问私有熟悉 
MyObject.prototype.getName = function () { 
return privateName; 
} 
MyObject.prototype.getFunction = function () { 
return privateFunction(); 
} 
} 
var moGyy = new MyObject("gyy"); 
alert(moGyy.getName()); //输出gyy 
alert(moGyy.getFunction()); //输出Private Function 
var moCyy = new MyObject("cyy"); 
alert(moCyy.getName()); //输出cyy 
alert(moCyy.getFunction()); //输出Private Function

在上面的示例中创建的两个对象moGyy和moCyy的getName返回不同的值,同时如果想调用私有方法同样也需要公共接口。上面的示例中两个公共函数之所以能访问私有变量,是因为两个公共函数都是闭包,而闭包的作用域链中包含了包含函数的变量对象,因此在进行变量查找时,顺着作用域链可以访问包含函数中的私有变量。在上面的示例中把公共方法添加到MyObject的原型中,目的是防止每次创建对象都创建功能一样的两个函数实例。
<2>.静态私有属性
在有些情况下我们可能希望数据全局共享,那么可能就会用到静态属性,我们还是希望这个属性为私有的,那么怎样实现静态私有属性呢?首先这个私有应该在构造函数的外部,为了把构造函数外部的变量和构造函数结合为一体,可以使用闭包把私有变量和构造函数都包含在其作用域中,为了在闭包外面访问内部的构造函数,可以使用一个全局的变量来引用构造函数,如下代码示例:
//静态私有变量和实例私有变量 
(function () { 
//定义私有变量 
var staticPrivateValue = ""; 
//构造函数,把构织函数赋值给一个全局的变量 
MyObject = function (name) { 
//定义实例变量 
this.name = name; 
}; 
//定义两个公共方法用于访问私有变量,再一次把公共方法添加到原型中 
MyObject.prototype.getPrivateValue = function () { 
return staticPrivateValue; 
} 
MyObject.prototype.setPrivateValue = function (value) { 
staticPrivateValue = value; 
} 
})(); 
var mo = new MyObject("jeff-gyy"); 
mo.setPrivateValue("gyycyy"); //设置私有属性的值 
alert(mo.getPrivateValue()); //输出gyycyy 
alert(mo.name); //输出jeff-gyy 
var mo1 = new MyObject("jeff-cyy"); 
alert(mo1.getPrivateValue()); //输出gyycyy 
alert(mo1.name); //输出jeff-cyy

从上面的代码来看mo1调用getPrivateValue函数返回的值就是mo设置的值"gyycyy",为什么会这样呢?首先我们定义了一个匿名函数并立即调用函数,函数包含了私有变量staticPrivateValue,那么为MyObject定义的两个原型方法其实通过闭包的作用域链可以访问在包含函数的私有变量,也就是getPrivateValue和setPrivateValue两个函数的作用域链中都包含了匿名函数的变量对象,我们知道作用域链中包含的变量对象其实就是一个指针,所以创建的两个对象通过公共方法房屋私有变量时,其实访问的都是匿名函数的变量对象中的staticPrivateValue,因此实现变量实例间共享的目的。从传统OO语言的角度来看我们实现的静态属性其实并不是真正意义上的静态,只是实现了静态属性实例共享的特点。
<3>.模块模式和增强模块模式
还有一种全局共享数据的方式就是singleton, 可以使用模块模式来实现Object类型的单例模式,也可以使用增强模块模式实现自定义类型的单例模式,如下示例:
//自定义构造函数 
var mo = new function () { 
//私有变量 
var privateValue = ""; 
//普通模块模式 
return { 
publicValue: "public", 
//访问私有变量 
getPrivateValue: function () { 
return privateValue; 
}, 
setPrivateValue: function (value) { 
privateValue = value; 
} 
} 
}(); 
mo.setPrivateValue("private value"); 
alert(mo.getPrivateValue()); 
alert(mo.publicFunction());

模块模式使用匿名函数来封装内部实现,就上面的示例匿名函数中包含了私有变量privateValue,返回的对象中的公共函数通过闭包的作用域链访问包含函数中的私有变量,由于定义的匿名函数被立即调用,因此变量mo引用的是返回的对象。上面的单例模式返回的是一个Object对象,可以使用增强模块模式实现自定义类型的单例模式:
//增强模块模式 
//自定义构造函数 
function MyObject(name) { 
this.name = name; 
}; 
//自定义构造函数 
var mo = new function () { 
//私有变量 
var privateValue = ""; 
//增强模块模式 
var o = new MyObject("gyycyy"); 
o.publicValue = "public"; 
//访问私有变量 
o.getPrivateValue = function () { 
return privateValue; 
} 
o.setPrivateValue = function (value) { 
privateValue = value; 
} 
return o; 
}(); 
mo.setPrivateValue("private value"); 
alert(mo.getPrivateValue()); 
alert(mo.publicFunction());

以上代码示例实现了MyObject的单例模式。
最后需要提一点的是使用闭包有利也有弊,由于闭包作用域链引用包含函数的变量对象,因此会占用额外的内存,而且进行变量查找是也需要通过作用域链,因此会消耗查找时间,闭包越深情况更严重。另外在IE(早些版本)中由于垃圾回收机制使用引用计数,因此可能会出现循环引用的情况,导致内存泄露,如下示例:
function assignHandler(){ 
var element = document.getElementById("someElement"); 
element.onclick = function(){ 
alert(element.id); 
}; 
}

在上面的代码中创建了一个闭包作为element的事件,该闭包引用了包含函数assingHandler的变量对象,而恰恰是对变量对象的引用使得element引用计数至少为1,因此element不会被回收,导致内存泄露。修改的方法大家可以想想。
Javascript 相关文章推荐
js或css实现滚动广告的几种方案
Jan 28 Javascript
ListBox实现上移,下移,左移,右移的简单实例
Feb 13 Javascript
javascript使用call调用微信API
Dec 15 Javascript
理解javascript定时器中的单线程
Feb 23 Javascript
js的form表单提交url传参数(包含+等特殊字符)的两种解决方法
May 25 Javascript
JS实现控制文本框的内容
Jul 10 Javascript
js选项卡的制作方法
Jan 23 Javascript
react-router v4如何使用history控制路由跳转详解
Jan 09 Javascript
jQuery实现百度图片移入移出内容提示框上下左右移动的效果
Jun 05 jQuery
js中比较两个对象是否相同的方法示例
Sep 02 Javascript
JavaScript中变量提升和函数提升的详解
Aug 07 Javascript
vue 使用 v-model 双向绑定父子组件的值遇见的问题及解决方案
Mar 01 Vue.js
13 个JavaScript 性能提升技巧分享
Jul 26 #Javascript
了解一点js的Eval函数
Jul 26 #Javascript
基于jquery实现的一个选择中国大学的弹框 (数据、步骤、代码)
Jul 26 #Javascript
javascript小组件 原生table排序表格脚本(兼容ie firefox opera chrome)
Jul 25 #Javascript
浅谈javascript的原型继承
Jul 25 #Javascript
基于jquery的多功能软键盘插件
Jul 25 #Javascript
基于jQuery判断两个元素是否有重叠部分的代码
Jul 25 #Javascript
You might like
一棵php的类树(支持无限分类)
2006/10/09 PHP
PHP取整数函数常用的四种方法小结
2012/07/05 PHP
php修改数组键名的方法示例
2017/04/15 PHP
Yii框架实现多数据库配置和操作的方法
2017/05/25 PHP
Laravel框架使用Seeder实现自动填充数据功能
2018/06/13 PHP
javascript下过滤数组重复值的代码
2007/09/10 Javascript
javascript HTMLEncode HTMLDecode的完整实例(兼容ie和火狐)
2009/06/02 Javascript
jQuery 通过事件委派一次绑定多种事件,以减少事件冗余
2010/06/30 Javascript
jquery JSON的解析方式示例介绍
2014/07/27 Javascript
js判断浏览器是否支持html5
2014/08/17 Javascript
javaScript实现可缩放的显示区效果代码
2015/10/26 Javascript
Bootstrap与KnockoutJs相结合实现分页效果实例详解
2016/05/03 Javascript
使用vue.js实现checkbox的全选和多个的删除功能
2017/02/17 Javascript
JS实现移动端实时监听输入框变化的实例代码
2017/04/12 Javascript
ES6中Array.includes()函数的用法
2017/09/20 Javascript
JS获取日期的方法实例【昨天,今天,明天,前n天,后n天的日期】
2017/09/28 Javascript
mockjs+vue页面直接展示数据的方法
2018/12/19 Javascript
35个Python编程小技巧
2014/04/01 Python
有关wxpython pyqt内存占用问题分析
2014/06/09 Python
Python自动登录126邮箱的方法
2015/07/10 Python
python 实现判断ip连通性的方法总结
2018/04/22 Python
Python将视频或者动态图gif逐帧保存为图片的方法
2019/09/10 Python
使用pygame写一个古诗词填空通关游戏
2019/12/03 Python
Python如何使用argparse模块处理命令行参数
2019/12/11 Python
解决Python spyder显示不全df列和行的问题
2020/04/20 Python
使用tensorflow进行音乐类型的分类
2020/08/14 Python
解决python3输入的坑——input()
2020/12/05 Python
历史学专业大学生找工作的自我评价
2013/10/16 职场文书
配件采购员岗位职责
2013/12/03 职场文书
顶岗实习计划书
2014/01/10 职场文书
师范毕业生求职信
2014/07/11 职场文书
党员转正申请报告
2015/05/15 职场文书
时尚女魔头观后感
2015/06/04 职场文书
幼儿园毕业典礼家长致辞
2015/07/29 职场文书
openstack云计算keystone组件工作介绍
2022/04/20 Servers
Android RecyclerView实现九宫格效果
2022/06/28 Java/Android