The previous 43 and 44 discussed the enumeration of attributes, but none of them completely solved the problem of property lookup for Zhongyuan pollution. Take a look at some of the following dictionary operations
‘zhangsan‘ in dict;dict.zhangsan;dict.zhangsan=22;
The object operation of JS always works in an inherited way. Even an empty object literal inherits the Object.protoype attribute.
var dict={};‘zhangsan‘ in dict;//false‘lisi‘ in dict;//false‘wangwu‘ in dict;//false‘toString‘ in dict;//true‘valueOf‘ in dict;//true
You cannot avoid inheriting methods from Object.prototype objects.
Removal of prototype contamination
Object.prototype provides the hasOwnProperty method. When you test a dictionary entry, it avoids prototype contamination, which can solve the problem before. Look at a piece of code
dict.hasOwnProperty(‘zhangsan‘);//falsedict.hasOwnProperty(‘toString‘);//falsedict.hasOwnProperty(‘valueOf‘);//false
You can use the attributes of the above code to find property lookups to avoid being contaminated by the prototype.
dict.hasOwnProperty(‘zhangsan‘)?dict.hasOwnProperty(‘zhangsan‘):undefined;dict.hasOwnProperty(‘x‘)?dict.hasOwnProperty(‘x‘):undefined;
Here is another problem, we are using the Dict object hasOwnProperty method, but in fact, it does not have this method, but it inherits from the Object.prototype object. If the Dict Dictionary object has a property of the same name as "hasOwnProperty", then the hasOwnProperty method in the prototype is not accessed. This will take precedence over the properties it contains, and it will not be found in the prototype chain.
dict.hasOwnProperty=10;dict.hasOwnProperty(‘zhangsan‘);//这里会产生一个错误
Although dictionaries rarely store such property names. But small probability events also occur because you do not know whether the processed data is coming from a third party. Here's a safe way to do this without making any assumptions. There is no use of a Dictionary object to access the hasOwnProperty method, which may be rewritten.
Using Object.prototpye.hasOwnProperty
The hasOwnProperty method in Object.prototype is used directly, and then the function's call method is used to bind the receiver of the function to the Dictionary object.
First, we first extract the hasOwnProperty method
var hasOwn=Object.prototype.hasOwnProperty;
Or
var hasOwn={}.hasOwnProperty;
Then, determine the recipient of the function run, using the call method to specify
hasOwn.call(dict,‘zhangsan‘);
Here it is safe to call the Object.prototype.hasOwnProperty method to detect the property name of the Dictionary object.
var dict={};dict.zhangsan=12;hasOwn.call(dict,‘hasOwnProperty‘);//falsehasOwn.call(dict,‘zhangsan‘);//truedict.hasOwnProperty=10;hasOwn.call(dict,‘hasOwnProperty‘);//truehasOwn.call(dict,‘zhangsan‘);//true
In order to avoid the insertion of the above detection code, the pattern can be abstracted as a dict method.
Dict Primary Edition
The Dict constructor encapsulates all the technical details of writing a robust dictionary in a single data type definition. The code is good.
function Dict(elements){ this.elements=elements||{};}Dict.prototype.has=function(key){ return {}.prototype.hasOwnProperty.call(this.elements,key);};Dict.prototype.get=function(key){ return this.has(key)?this.elements[key]:undefined;};Dict.prototype.set=function(key,val){ this.elements[key]=val;};Dict.prototype.remove=function(key){ delete this.elements[key];};
This implementation is more robust than using the JS default object syntax and is equally easy to use.
var dict=new Dict({ zhangsan:12, lisi:23, wangwu:40});dict.has(‘zhangsang‘);//truedict.has(‘lisi‘);//truedict.has(‘toString‘);//false
__proto__ Self-contamination
Previously mentioned in the 44 article, in some special JS environment, special attribute name __proto__ may lead to its own pollution problem. In some environments, __proto__ simply inherits from Object.prototype, so empty objects are really empty objects.
Under some circumstances
var empty=Object.create(null);‘__proto__‘ in empty;//falsevar hasOwn={}.hasOwnProperty;hasOwn.call(empty,‘__proto__‘);//false
In other environments
var empty=Object.create(null);‘__proto__‘ in empty;//truevar hasOwn={}.hasOwnProperty;hasOwn.call(empty,‘__proto__‘);//false
Under certain circumstances
Because there is an instance attribute __proto__ and permanently polluting all objects
var empty=Object.create(null);‘__proto__‘ in empty;//truevar hasOwn={}.hasOwnProperty;hasOwn.call(empty,‘__proto__‘);//true
In different environments, the value of __proto__ cannot be determined
var dict=new Dict();dict.has(‘__proto__‘);//无法确定
To achieve the portability and security of the code, you can only add some action to the __PROTO__ keyword.
Dict final version
Here's a more secure, more complex final implementation of Dict.
function Dict(elements){ this.elements=elements||{}; this.hasSpecialProto=false; this.specialProto=undefined;}Dict.prototype.has=function(key){ if(key === ‘__proto__‘){ return this.hasSpecialProto; } return {}.prototype.hasOwnProperty.call(this.elements,key);};Dict.prototype.get=function(key){ if(key === ‘__proto__‘){ return this.specialProto; } return this.has(key)?this.elements[key]:undefined;};Dict.prototype.set=function(key,val){ if(key === ‘__proto__‘){ this.hasSpecialProto=true; this.specialProto=val; }else{ this.elements[key]=val; }};Dict.prototype.remove=function(key){ if(key === ‘__proto__‘){ this.hasSpecialProto=false; this.specialProto=undefined; }else{ delete this.elements[key]; } };
The above code works regardless of the environment where the __proto__ attribute is not handled.
var dict=new Dict();dict.has(‘__proto__‘);//false
Tips
Use the hasOwnProperty method to avoid prototype contamination
Using lexical scopes and call methods to avoid overwriting hasownproperty methods
Consider implementing a dictionary operation in a class that encapsulates hasOwnProperty test boilerplate code
Use the dictionary class to avoid using ' __proto__' as key
[Effective JavaScript note] 45th: Use the hasOwnProperty method to avoid prototype contamination