JavaScript实现私有属性
JavaScript被很多人认为并不是一种面向对象语言,原因有很多种,比如JavaScript没有类,不能提供传统的类式继承;再比如JavaScript不能实现信息的隐藏,不能实现私有成员。本文并不是为了打破以上误解(实际上笔者自己也有困惑),只是简单介绍几种JavaScript实现私有属性的方式,以及各自的优劣。
1. 基于编码规范约定实现方式
很多编码规范把以下划线_开头的变量约定为私有成员,便于同团队开发人员的协同工作。实现方式如下:
function Person(name){
this._name = name;
}
var person = new Person('Joe');
这种方式只是一种规范约定,很容易被打破。而且也并没有实现私有属性,上述代码中的实例person可以直接访问到_name属性:
alert(person._name); //'Joe'
2. 基于闭包的实现方式
另外一种比较普遍的方式是利用JavaScript的闭包特性。构造函数内定义局部变量和特权函数,其实例只能通过特权函数访问此变量,如下:
function Person(name){
var _name = name;
this.getName = function(){
return _name;
}
}
var person = new Person('Joe');
这种方式的优点是实现了私有属性的隐藏,Person 的实例并不能直接访问_name属性,只能通过特权函数getName获取:
alert(person._name); // undefined
alert(person.getName()); //'Joe'
使用闭包和特权函数实现私有属性的定义和访问是很多开发者采用的方式,Douglas Crockford也曾在博客中提到过这种方式。但是这种方式存在一些缺陷:
- 私有变量和特权函数只能在构造函数中创建。通常来讲,构造函数的功能只负责创建新对象,方法应该共享于prototype上。特权函数本质上是存在于每个实例中的,而不是prototype上,增加了资源占用。
3. 基于强引用散列表的实现方式
JavaScript不支持Map数据结构,所谓强引用散列表方式其实是Map模式的一种变体。简单来讲,就是给每个实例新增一个唯一的标识符,以此标识符为key,对应的value便是这个实例的私有属性,这对key-value保存在一个Object内。实现方式如下:
var Person = (function() {
var privateData = {},
privateId = 0;
function Person(name) {
Object.defineProperty(this, "_id", { value: privateId++ });
privateData[this._id] = {
name: name
};
}
Person.prototype.getName = function() {
return privateData[this._id].name;
};
return Person;
}());
上述代码的有以下几个特征:
- 使用自执行函数创建Person类,变量privateData和privateId被所有实例共享;
- privateData用来储存每个实例的私有属性name的key-value,privateId用来分配每个实例的唯一标识符
_id; - 方法getName存在于prototype上,被所有实例共享。
这种方式在目前ES5环境下,基本是最佳方案了。但是仍然有一个致命的缺陷:散列表privateData对每个实例都是强引用,导致实例不能被垃圾回收处理。如果存在大量实例必然会导致memory leak。
造成以上问题的本质是JavaScript的闭包引用,以及只能使用字符串类型最为散列表的key值。针对这两个问题,ES6新增的WeakMap可以良好的解决。
4. 基于WeakMap的实现方式
WeakMap有以下特点:
- 支持使用对象类型作为key值;
- 弱引用。
根据WeakMap的特点,便不必为每个实例都创建一个唯一标识符,因为实例本身便可以作为WeakMap的key。改进后的代码如下:
var Person = (function() {
var privateData = new WeakMap();
function Person(name) {
privateData.set(this, { name: name });
}
Person.prototype.getName = function() {
return privateData.get(this).name;
};
return Person;
}());
改进的代码不仅仅干净了很多,而且WeakMap是一种弱引用散列表, 这意味着,如果没有其他引用和该键引用同一个对象,这个对象将会被当作垃圾回收掉。解决了内存泄露的问题。
不幸的是,目前浏览器对WeakMap的支持率并不理想,投入生产环境仍然需要等待。
参考文献:
- WeakMap - MDN;
- WeakMap Objects - ES6;
- Private_Properties - MDN;
- Private instance members with weakmaps in JavaScript by Nicholas C. Zakas
JavaScript实现私有属性的更多相关文章
- JavaScript中的私有属性
一.使用构造函数获得私有属性: function Gadget(){ var name = 'iPod'; this.getName = function(){ return name; }; }; ...
- javaScript在私有的属性和方法
javaScript并没有什么特别的语法来代表私人.保.或公共的属性和方法,在这一点上与 java或其他语言是不同的.JavaScript大家是共同的所有对象: var myobj={ mypop:1 ...
- JavaScript 新语法详解:Class 的私有属性与私有方法
译者按: 为什么偏要用 # 符号? 原文:JavaScript's new #private class fields 译者:Fundebug 本文采用意译,版权归原作者所有 proposal-cla ...
- javascript私有属性失效及解决方案
1.js创建私有属性的方法 在 javascript 中所有对象的成员是公有的 构造函数也是如此: function Gadget ( ) { this.name = ' jack '; this.p ...
- JavaScript基础对象创建模式之私有属性和方法(024)
JavaScript没有特殊的语法来表示对象的私有属性和方法,默认的情况下,所有的属性和方法都是公有的.如下面用字面声明的对象: var myobj = { myprop: 1, getProp: f ...
- JavaScript 公有 私有 静态属性和方法
1.公有属性和公有方法 这里的 name age 都是参数传递进去 可以在外面直接实例化调用. 2.私有属性和方法 私有的只能在函数内部使用 作用域的原因 3.静态属性和静态方法 这里我首先 创建 ...
- JS面向对象(3) -- Object类,静态属性,闭包,私有属性, call和apply的使用,继承的三种实现方法
相关链接: JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式 JS面向对象(2) -- this的使用,对 ...
- [转] JavaScript中的属性:如何遍历属性
在JavaScript中,遍历一个对象的属性往往没有在其他语言中遍历一个哈希(有些语言称为字典)的键那么简单.这主要有两个方面的原因:一个是,JavaScript中的对象通常都处在某个原型链中,它会从 ...
- javascript中类的属性研究
原文:javascript中类的属性研究 本篇文章主要针对javascript的属性进行分析,由于javascript是一种基于对象的语言,本身没有类的概念,所以对于javascript的类的定义有很 ...
随机推荐
- abiword Namespace List
abiword Namespace List Here is a list of all namespaces with brief descriptions: abicollab 这个命名空间以及 ...
- Document原来可以这样来获取DOM
images集合(页面中的图象) a)通过集合引用 代码 document.images //对应页面上的<img>标签 document.images.leng ...
- Qt的十六进制的控件
Qt没有这样的Widget,自己写一个吧.我曾经用MFC写过一个,代码不多,不到2000行,估计用Qt写不到1000行就够了. 可以参考这个qhexedit2 - QHexEdit is a Bina ...
- setTimeout()使用
Basic setTimeout() Example setTimeout(function() { // Do something after 5 seconds }, ); Tip: ...
- BorderLayout布局,修改各个区域大小办法
摘自http://blog.csdn.net/zcsearching/article/details/50808446 BorderLayout控件大小的设置 使用BorderLayout时,中间的面 ...
- GTID复制报错处理:Last_Error: Error 'Can't drop database 'test'; database doesn't exist' on query
创建GTID主从连接: mysql, master_user; 报错显示: Slave_IO_Running: Yes Slave_SQL_Running: No Last_Error: Error ...
- Java使用默认浏览器打开指定URL
直接贴代码: 方法一: Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler http://www.baidu.co ...
- java-随学随记之基础篇
一.Java是一门高级语言,具有跨平台性(一次编译到处运行) 1.javac.exe命令,编译器 javac 源文件名.java 2.java.exe命令,启动虚拟机 java 类名 二.环境变量 ...
- 简单实现div+css页面自适应
Step1.在<head>添加如下代码<meta name="viewport" content="width=device-width, initia ...
- AjaxHelper创建的ajax无效,JQuery直接方法post有效,原来是Microsoft.jQuery.Unobtrusive.Ajax错误,NuGet解决
Get-Package -ListAvailable -Filter Microsoft.JQuery Microsoft.jQuery.Unobtrusive.Ajax –Version 3.2.0