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

 更新时间:2012年07月26日 22:24:48   作者:  
大家都知道在JavaScript中没有块级作用域的概念,我们可以通过使用闭包来模拟实现块级作用域,看下面的示例
•模拟块级作用域
大家都知道在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与Java正则表达式写法的区别介绍

    JavaScript与Java正则表达式写法的区别介绍

    这篇文章主要介绍了JavaScript与Java正则表达式写法的区别介绍,需要的朋友可以参考下
    2017-08-08
  • ES6函数实现排它两种写法解析

    ES6函数实现排它两种写法解析

    这篇文章主要介绍了ES6函数实现排它两种写法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • 设置点击文本框或图片弹出日历控件的实现代码

    设置点击文本框或图片弹出日历控件的实现代码

    下面小编就为大家带来一篇设置点击文本框或图片弹出日历控件的实现代码。小编觉得挺不错的,现在分享给大家,也给大家做个参考,一起跟随小编过来看看吧
    2016-05-05
  • 微信小程序实现圆心进度条

    微信小程序实现圆心进度条

    这篇文章主要为大家详细介绍了微信小程序实现圆心进度条,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • JS识别浏览器类型(电脑浏览器和手机浏览器)

    JS识别浏览器类型(电脑浏览器和手机浏览器)

    本文给大家分享一段js代码关于识别浏览器的类型是手机浏览器还是电脑浏览器,有需要的朋友可以参考下本文
    2016-11-11
  • WordPress 插件——CoolCode使用方法与下载

    WordPress 插件——CoolCode使用方法与下载

    本插件原修改自 Chroder.com 的 WordPress Code Highlight 插件。但 CoolCode 插件在它基础上修改了很多的内容。
    2007-07-07
  • JS多个表单数据提交下的serialize()应用实例分析

    JS多个表单数据提交下的serialize()应用实例分析

    这篇文章主要介绍了JS多个表单数据提交下的serialize()应用,接合实例形式分析了原生javascript实现多个表单提交时serialize操作相关使用技巧与操作注意事项,需要的朋友可以参考下
    2019-08-08
  • javascript中对变量类型的判断方法

    javascript中对变量类型的判断方法

    在JavaScript中,有5种基本数据类型和1种复杂数据类型,基本数据类型有:Undefined, Null, Boolean, Number和String;复杂数据类型是Object,Object中还细分了很多具体的类型,比如:Array, Function, Date等等
    2015-08-08
  • 用javascript实现倒计时效果

    用javascript实现倒计时效果

    这篇文章主要为大家详细介绍了用javascript实现倒计时效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-02-02
  • 详解js根据百度地图提供经纬度计算两点距离

    详解js根据百度地图提供经纬度计算两点距离

    这篇文章主要介绍了js根据百度地图提供经纬度计算两点距离,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05

最新评论