数据字典在Javascript
语言里面随处可见,对象本身就可以看成一个数据字典,通过给对象设置属性与方法,达到一个字典的目的。
var dict = new Dict();
obj.pro1 = "hello world!!!";
obj.pro2 = function () {
console.log("I'am pro2");
};
先来看一个常见的数据字典的设计:
function Dict() {
}
Dict.prototype.count = function() {
let n = 0;
for(let k in this) {
n++;
};
return n;
};
//
let dict = new Dict();
dict.paul = 3;
dict.lebron = 23;
dict.count() // 3
为什么给设置了两个属性,通过count
计算得出的结果是3
呢?这里比较容易看出来,因为for in
会遍历原型链上的可枚举的属性,例如上面的count
方法。为了避免这种由于遍历原型链导致的错误结果,实现一个改进版本:
var dict = {};
dict.diwy = 3;
dict.love = 10;
let n = 0;
for(let k in dict) {
n++;
}
console.log(n) //2
通过这种改进虽然能够得出正确的结果,但是我们把这一切能够正常运行的希望寄托于Object.prototype
原型没有受到污染
。这种假设通常伴随一定的风险,如果和其他人协同开发产品,你不能保证其他人也和你一样遵循相应的规范。
了解到潜在的原型污染问题之后,可以想到的做法是,构造一个对象,不依赖于常规的原型对象:
function C() {}
C.prototype = null;
来测试一下:
var c = new C();
Object.getPropertyOf(c) === null;//false
Object.getPropertyOf(c) === Object.prototype; //true
遗憾的是c
对象的原型依然是Object.prototype
。ES5
提供了一个方法来创建一个没有原型的对象:
var dict = Object.create(null);
Object.getPropertyOf(dict) === null; // true
通过创建一个没有原型的对象,可以很好的规避原型污染问题。但是如果你既需要对象原型,又想保证字典枚举的安全性,那就得改造一些字典的设计。
使用hasOwnProperty
来判断对象的实体属性,而不是原型熟悉。
var dict = {};
dict.name = 'lebron';
dict.hasOwnPropertyOf("name") //true;
dict.hasOwnPropertyOf("valueOf") //false
"valudeOf" in dict //true
为了避免设置hasOwnPropertyOf
这样奇怪的属性,我们需要在任何安全的位置提取出hasOwnPropertyOf
方法。
var hasOwnProperty = Object.prototype.hasOwnProperty;
//或者
var hasOwnProperty = {}.hasOwnProperty;
hasOwnProperty.call(dict, "name"); //true
一个安全的数据字典实现:
function Dict(elements) {
this.elements = elements || {};
}
Dict.property.has = function(key) {
return {}.hasOwnPropertyOf(key);
};
Dict.property.get = function(key) {
return this.has(key) ? this.elements[key] : undefined;
};
Dict.property.set = function(key, value) {
this.elements[key] = value;
};
Dict.property.remove = function(key) {
delete this.elements[key];
};
在一些javascript
环境中,特殊的属性名__proto__
可能会导致自身的污染问题(修改对象的原型)。我们必须把它优化掉:
function Dict(elements) {
this.elements = elements || {};
this.specialPropValue = undefined;
this.specialProp = "__proto__";
this.hasSpecialProp = false;
}
Dict.prototype.has = function(key) {
if(key === this.specialProp) {
return this.hasSpecialProp;
}
return {}.hasOwnPropertyOf.call(this.elements, key);
};
Dict.prototype.get = function(key) {
if(key === this.specialProp) {
return this.specialProp;
}
return this.has(key) ? this.elements[key] : undefined;
};
Dict.prototype.set = function(key, value) {
if(key === this.specialProp) {
this.specialPropValue = value;
}
else {
this.elements[key] = value;
}
};
这样javascript
环境是否处理__proto__
属性,该实现均能保证是可工作的,以上也算是数据字典比较好的一个设计吧!。