Recently, I developed a project Angular Cloud Data Connector to help Angular developers use cloud data, especially Azure mobile services, using WEB standards, such as indexed DB. I'm trying to create a way for JavaScript developers to embed private members into an object.
My technique for solving this problem uses what I named closurespace. In this introductory article, I want to share how to use it in your projects and its impact on performance and memory in major browsers.
Before we study in depth, let’s first talk about why you need to use private members (private members), and there is an alternative way to simulate private members.
If you want to comment on this article, feel free to tweet me: @deltakosh.
When you create an object in JavaScript, you can declare value members. If you plan to control read/write access to them, you can declare it as follows:
var entity = {}; entity._property = "hello world"; Object.defineProperty(entity, "property", { get: function () { return this._property; }, set: function (value) { this._property = value; }, enumerable: true, configurable: true });
In this way, you have full control over read and write operations. The problem is that the _property member can still be accessed and modified directly.
This is why we need a more stable and reliable way to declare private members, which can be accessed through object methods.
The solution is to use closure space. Whenever an inner function accesses a variable from the outer function's scope, the browser allocates a memory space for you. Sometimes it's tricky, but for our problem, this is a perfect solution.
我们在上个代码版本中添加这个特性: var createProperty = function (obj, prop, currentValue) { Object.defineProperty(obj, prop, { get: function () { return currentValue; }, set: function (value) { currentValue = value; }, enumerable: true, configurable: true }); } var entity = {}; var myVar = "hello world";createProperty(entity, "property", myVar);
In the example, the createProperty function has a currentValue variable, and there are get and set methods. This variable is saved into the closure space of the get and set functions. Now, only these two functions can see and update the currentValue variable! Mission accomplished!
The only caveat, warning, attention) is that the source value (myVar) is still accessible. Another more robust version (protecting the myVar variable) is given below:
var createProperty = function (obj, prop) { var currentValue = obj[prop]; Object.defineProperty(obj, prop, { get: function () { return currentValue; }, set: function (value) { currentValue = value; }, enumerable: true, configurable: true }); } var entity = { property: "hello world" }; createProperty(entity, "property");
Using this function, even the source values are destroyed (destructed, note: it means that they cannot be assigned directly). That’s it!
Now let’s look at performance.
Obviously, compared to a simple variable, closure space, or even (object) property, it is much slower and consumes more resources. This is why this article focuses more on the differences between ordinary methods and closure space mechanisms.
To prove that the closure space mechanism does not consume more resources than the standard method, I wrote the following code as a benchmark test:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> </head> <style> html { font-family: "Helvetica Neue", Helvetica; } </style> <body> <p id="results">Computing...</p> <script> var results = document.getElementById("results"); var sampleSize = 1000000; var opCounts = 1000000; var entities = []; setTimeout(function () { // Creating entities for (var index = 0; index < sampleSize; index++) { entities.push({ property: "hello world (" + index + ")" }); } // Random reads var start = new Date().getTime(); for (index = 0; index < opCounts; index++) { var position = Math.floor(Math.random() * entities.length); var temp = entities[position].property; } var end = new Date().getTime(); results.innerHTML = "<strong>Results:</strong><br>Using member access: <strong>" + (end - start) + "</strong> ms"; }, 0); setTimeout(function () { // Closure space ======================================= var createProperty = function (obj, prop, currentValue) { Object.defineProperty(obj, prop, { get: function () { return currentValue; }, set: function (value) { currentValue = value; }, enumerable: true, configurable: true }); } // Adding property and using closure space to save private value for (var index = 0; index < sampleSize; index++) { var entity = entities[index]; var currentValue = entity.property; createProperty(entity, "property", currentValue); } // Random reads var start = new Date().getTime(); for (index = 0; index < opCounts; index++) { var position = Math.floor(Math.random() * entities.length); var temp = entities[position].property; } var end = new Date().getTime(); results.innerHTML += "<br>Using closure space: <strong>" + (end - start) + "</strong> ms"; }, 0); setTimeout(function () { // Using local member ======================================= // Adding property and using local member to save private value for (var index = 0; index < sampleSize; index++) { var entity = entities[index]; entity._property = entity.property; Object.defineProperty(entity, "property", { get: function () { return this._property; }, set: function (value) { this._property = value; }, enumerable: true, configurable: true }); } // Random reads var start = new Date().getTime(); for (index = 0; index < opCounts; index++) { var position = Math.floor(Math.random() * entities.length); var temp = entities[position].property; } var end = new Date().getTime(); results.innerHTML += "<br>Using local member: <strong>" + (end - start) + "</strong> ms"; }, 0); </script> </body> </html>
I created one million objects, all with attribute members . To complete the following three tests:
Perform 1 million random accesses to attributes.
Performs 1 million random access closure space implementation versions.
Performs 1 million random access regular get/set implementations.
The test results are shown in the following tables and charts:
We found that the closure space implementation is always faster than the conventional one Implementation, depending on the browser, can also make further performance optimizations.
Performance on Chrome is lower than expected. There may be a bug, so to confirm, I contacted the Google project team and described the symptoms that occurred. Also, if you want to test performance in Microsoft Edge—Microsoft’s newly released browser that is installed by default in Windows 10—you can click to download .
However, if you study carefully, you will find that using closure space or attributes is about 10 times longer than directly accessing variable members. Therefore, use it appropriately and with caution.
We also have to verify that the technology does not consume too much memory. To test the memory usage baseline, I wrote the following code snippet:
var sampleSize = 1000000; var entities = []; // Creating entities for (var index = 0; index < sampleSize; index++) { entities.push({ property: "hello world (" + index + ")" });}
var sampleSize = 1000000; var entities = []; // Adding property and using local member to save private value for (var index = 0; index < sampleSize; index++) { var entity = {}; entity._property = "hello world (" + index + ")"; Object.defineProperty(entity, "property", { get: function () { return this._property; }, set: function (value) { this._property = value; }, enumerable: true, configurable: true }); entities.push(entity); }
var sampleSize = 1000000; var entities = []; var createProperty = function (obj, prop, currentValue) { Object.defineProperty(obj, prop, { get: function () { return currentValue; }, set: function (value) { currentValue = value; }, enumerable: true, configurable: true }); } // Adding property and using closure space to save private value for (var index = 0; index < sampleSize; index++) { var entity = {}; var currentValue = "hello world (" + index + ")"; createProperty(entity, "property", currentValue); entities.push(entity); }
After that, I ran all three pieces of code (on three major browsers) and launched the (browser) embedded memory profiler (The F12 toolbar is used in this example):
The results of running on my computer are as follows:
Regarding closure space and conventional methods, only on Chrome, the closure space (memory usage) performs slightly better. On IE11 and Firefox, the memory usage increases, but the comparison results of browsers are e-for modern browsers, users Most likely you won't notice the difference.
You may be surprised to learn that Microsoft has provided a batch of free learning materials on open source Javascript topics, and we are launching a mission to create more Microsoft Edge Coming series. Check out my article:
Developing WebGL 3D Basics based on HTML5 and Babylon.JS
Building a single-page application based on ASP.NET and AngularJS
HTML Advanced Image Technology
Or our team series:
HTML/JavaScript Performance Optimization Usage Tips (This series has 7 parts, from responsive design to performance optimization of casual games)
Quick Start with Modern Web Platform (HTML, CSS, and JS Basics)
Develop universal Windows Apps using HTML and JavaScript to get started quickly (build apps with your own JS)
And some free tools: Visual Studio Community, Azure trials and cross-browser testing tools for Mac, Linux, or Windows.
As you can see, the closure space attribute (mechanism) is a great way to create truly private data. You may have to face a small increase in memory consumption (problem), but in my opinion it is very reasonable (this price can be exchanged for a higher performance increase compared to the conventional method).
As a side note, if you want to try it yourself, the code can be downloaded here. Recommend a good article, “how-to” on Azure Mobile Services here.
The above is the detailed content of Example code of how to embed private members in JavaScript objects (picture). For more information, please follow other related articles on the PHP Chinese website!