5.7: A JavaScript Object Model that Includes Encapsulation and Data Hiding
- Page ID
- 29149
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)The problem with the object model in the previous section is that the constructed object was returned to the main program where all of its properties were visible. This is why the principal of encapsulation is broken. The easiest way to prevent the leaking of the object is to make the object be defined not by this, but by a function variable. Functions variables are only scoped as visible in the function itself, and this in effect makes the object itself private.
To implement this new object model, the strategy from the last section needs to be tweaked a little. First, the Constructor Function will not construct a this
object, but will create a function local variable and construct the object inside of this local variable. Now that the object is a local variable that cannot be accessed outside of the Constructor Function (which is the outer function), inner functions need to be defined that can access the properties of the object.
Note that the object will still want to use prototype functions so that only one instance of the function needs to be defined, but these prototype functions must now be moved so they are inner functions of the Constructor function. This gives the prototype functions access to the variables defined in the outer function.
The strategy for creating this type of object is as follows:
- The Constructor Function is defined, and it will take one argument, which is an options object of properties to be included and set in this object.
- A function local variable, named
__properties
, is defined of type object. - The
__cfName
variable is assigned to the name of the Constructor Function in the __properties object. This is so that object can later be reconstructed if it loses its constructor (e.g. it is written and then read from a JSON data source). - The property default values will be defined in the
__properties
object. - All properties passed into the Constructor Function will be moved to properties in the newly constructed
__properties
object. The properties will include all properties in the options object, including those that do not have default values. The reason for this will be explained in the section on Unstructured Data later in this chapter. - All methods that will act on this object will be included in the prototype object for this Constructor Function. These prototype functions will be defined inside of the Constructor Function, so they have access to the function local variable.
- A stringify method needs to be defined to allow the internal object (the __properties) object can be accessed and converted into a string.
An example of implementing and using this definition for a Map object is presented below.
Program 105 - Object example with encapsulation and data hiding <script> function Map(options) { // Set Defaults let __properties = new Object; __properties.title = "Please change the title" __properties.recenter = true; __properties.center = [-77, 32]; __properties.__cfName = "Map" // Get properties from parameter outerloop: for (i in options) { for (j in __properties) { if (i == j) { __properties[i]= options[j]; continue outerloop; } } console.log("Property " + i + " is not a default Map property"); __properties[i] = options[i]; } // Set accessor function Map.prototype.setTitle = function (title) { __properties.title = title; } Map.prototype.getTitle = function () { return __properties.title; } Map.prototype.setRecenter = function (recenter) { __properties.recenter = recenter; } Map.prototype.getRecenter = function () { return __properties.recenter; } Map.prototype.setCenter = function (center) { __properties.center = center; } Map.prototype.getCenter = function () { return __properties.center; } Map.prototype.stringify = function() { return JSON.stringify(__properties); } Map.prototype.toString = function() { return ("title: " + __properties.title + " recenter: " + __properties.recenter + " center: " + __properties.center); } } //End of Constructor Function function getObjectFromJSON(string) { let parsedObject = JSON.parse(string); let cf = parsedObject["__cfName"]; return new window[cf](parsedObject); } let m1 = new Map({title: "newMap"}) let m2 = getObjectFromJSON(m1.stringify()); console.log(m2.toString()); </script>