Skip to main content
Ben Nadel at InVision In Real Life (IRL) 2019 (Phoenix, AZ) with: Dennis Field
Ben Nadel at InVision In Real Life (IRL) 2019 (Phoenix, AZ) with: Dennis Field

Creating A Private Scope By Extending The Public THIS Scope In JavaScript

By
Published in Comments (7)

In JavaScript, we create objects. These objects don't really have a sense of public and private variables (in a classical object-oriented sense); but, thanks to the lexical binding of Closures in JavaScript, we can finagle private variables that can be accessed via public methods. Last week, I got the idea to use lexical binding in order to create a private scope that would extended the public scope. This would give the private scope access to both the private variables and, by way of the Prototype chain, the public variables as well.

In a JavaScript method, the "this" scope defines the execution context of the method. When defining an object and its methods, you can think of the "this" scope as the "public" scope of an object. That is, any methods and properties defined as part of the "this" scope can be accessed by code external to the given object.

Conversely, in a JavaScript method, variables that are declared using a "var" statement are accessible only to the context of the current method. Var-scoped variables can be thought of as belonging to the "private" scope of a method or object.

In a classical object-oriented world, this public / private separation exists; however, internally to the object definition, there is no syntactic difference between public and private variables. That is, in many object oriented languages, from within an object's methods, the "this" scope is used to access both the public and the private variables.

In JavaScript, I wonder if we can bridge this gap slightly by taking advantage of the Prototype chain. What if we create a private variable that, using the prototype chain, extends the "this" scope of a given object. In doing so, we could create a private collection of values that aggregates both the private and public properties while still maintaining a strong public / private separation.

To explore this concept, I'm going to use the Object.create() method to define a private "my" scope that extends the public "this" scope. This should give the "my" scope access to both the private and public properties while maintaining "this" scope integrity.

<!DOCTYPE html>
<html>
<head>
	<title>Creating A Private Scope By Extending The Public Scope In JavaScript</title>

	<script type="text/javascript">


		// Create a singleton for testing purposes.
		var sarah = (function(){

			// The THIS scope is already public. Let's make a private
			// scope by extending the THIS scope such that the
			// private will have access to BOTH private AND public
			// properties (ie. methods and values).
			var my = Object.create( this );


			// Store a private value.
			my.weight = 145;

			// Store a private weight offset.
			my.weightOffset = -7;


			// ---------------------------------------------- //
			// ---------------------------------------------- //


			// Create a PUBLIC method for getting the true weight.
			this.getAbsoluteWeight = function(){

				return( my.weight );

			};

			// Create a PUBLIC method for getting the "adjusted"
			// weight.
			this.getWeight = function(){

				// Return the weight adjusted by the offset. Notice
				// that BOTH methods are accessed via "MY" - the
				// PRIVATE scope.
				return(
					my.getAbsoluteWeight() + my.getWeightOffset()
				);

			};

			// Create a PRIVATE accessor for getting the weight
			// offset for weight calculations.
			my.getWeightOffset = function(){

				return( my.weightOffset );

			};

			// Create a PUBLIC method for going on a diet.
			this.diet = function(){

				// Lose 2 pounds. Notice that ALL interactions here
				// are executed through the PRIVATE scope.
				my.setWeight( my.getAbsoluteWeight() - 2 );

			};

			// Create a PUBLIC method for setting weight.
			this.setWeight = function( weight ){

				// Store the new weight in the PRIVATE scope.
				my.weight = weight;

			};


			// ---------------------------------------------- //
			// ---------------------------------------------- //


			// Return a reference to this object.
			return( this );

		}).call( {} );


		// -------------------------------------------------- //
		// -------------------------------------------------- //
		// -------------------------------------------------- //
		// -------------------------------------------------- //


		// Get the weight.
		console.log(
			"Weight:",
			sarah.getWeight()
		);

		// Go on a diet.
		sarah.diet();

		// Log new weight.
		console.log(
			"Diet Weight:",
			sarah.getWeight()
		);


		// Log the PUBLIC API of the sarah singleton.
		console.log( "SARAH:", sarah );


	</script>

</head>
<body>
	<!-- Left intentionally blank. -->
</body>
</html>

As you can see, I'm using JavaScript's call() method to define the execution context of the singleton module. Then, I'm using the Object.create() method to define the private scope as an extension of the public scope. Once this is done, I then use either the "this" scope or the "my" scope to define properties (both values and methods).

This creates a syntactic difference between public and private property definitions; but, when it comes to runtime and actually invoking properties, you can see that the "my" scope has now unified all internal access to object properties, both public and private.

When we run the above code, we get the following console output:

Weight: 138
Diet Weight: 136

And, when we log the "sarah" object, we can see the following public methods:

  • diet function()
  • getAbsoluteWeight function()
  • getWeight function()
  • setWeight function()

Notice that none of the private methods or values are accessible to the outside world; and yet, from within the object, both public and private variables can be accessed via the "my" scope.

Creating a private scope in JavaScript by extending the public (THIS) scope of a JavaScript method context.

When using an approach like this, you have to be very aware of how JavaScript gets and sets values within the prototype chain. This demo works because we never tried to SET a public value using the "my" scope. And, we can't. Any attempt to set a value using the "my" scope will make the value private (even if it has a public counterpart).

While this was a fun experiment, it really only works with singleton objects. That is, it only works in situations where there is a one-to-one relationship between function definitions and object instances. If you needed to create transient objects, where you define a prototype object and a true constructor function, you wouldn't be able to take advantage of the lexically-scoped private variables. So, while this is certainly interesting, realize that it has limitations in its functionality.

Want to use code from this post? Check out the license.

Reader Comments

1 Comments

Just thought I'd make your day!

Good stuff this - though I must admit it's more interesting than something I'm necessarily going to use. I did wonder what you make of Dart / coffeescript etc? Do you think you'll stick with raw js or start climbing the ladders of abstraction?

15,848 Comments

@Johnny,

Thanks :) I've only heard of Dart and I've seen one presentation (last weekend) on CoffeeScript. I'm not ready to go up a layer just yet. And, as far as this stuff, I might start using it. I do like the idea of a unified, internal access layer. Though, as I alluded to at the end, this is really only relevant for singletons; so, if I have an app that has more transient domain objects, I'll have to fall back on the Prototype object and constructor functions (that won't play well with this approach).

2 Comments

Hey Ben, have a query to pose that is only vaguely tangentially related to this topic, but it sparked my memory of recent question I posed on StackOverflow which went unanswered.

First off, I love you. You so often write about the kind of things I do just for sheer curiosity. That is: trying to feel out the edges and the limits of the space to explore what only might be possible. Things like your previous articles on dynamic local scope via with and other tricks, as an example. It doesn't matter if it may only have limited applicability, or ultimately not be feasible. It pushes the boundaries and sometimes something does get discovered by this type of exploration that is truly useful.

On that, topic, my question on StackOverflow can basically be summed up as: is there ANY way to gain access to the unnamed [[local]] variable on which variables in side a function/closure are defined. The basis of the question if, if you define a variable in a function scope which has no other references and has no parent object that you know of in your scope then two things become impossible: 1.) accessing the object by name, as you could a sub-object like parent["child_name"], 2.) using Object.defineProperty to create getter/setters on the object to prevent it being otherwise tainted. I can create getter/setters all day long on properties (sub objects if I wish) of an object, and prevent them from being tainted, or register all access to them. But then someone could come along and sweep away my top level object because there was no way to create a getter/setter for it.

As it stands thus far, the only solution is ES6(Harmony) Proxies which allow you to create traps on every kind of interaction with a facade object. Still, it just seems like a glaring oversight that you can do all of this for anything in the global context and anything that has a container object, but not locally defined top level objects, simply out of a gap in the language syntax.

My question for this is on http://stackoverflow.com/questions/7400250/defining-setter-getter-for-an-unparented-local-variable-impossible.

I don't really think there is an answer aside from Proxies, but I figured you might be interested in it. This is one of the few things in JavaScript I have simply failed to find a way to do. As you most definitely are aware, based on the stuff you try, it's hard to find things that JavaScript can't be bent some way or another to accomplish.

15,848 Comments

@Arthur,

Ha ha, I try to keep a list of things that I want to investigate. When ideas pop into my head, I'll grab my phone and email myself a note. But, I haven't had very much to say lately (the last two or so weeks have been very light on posts - mostly philosophy stuff).

@Brandon,

Thanks my man! I think we can start to generate some really interesting ideas and solutions when we push the limits of what is possible. Sometimes, you get the most elegant solutions when you try something and you're like, "weird, I didn't realize that would work."

I think they say science works in the same way - isn't there a quote that say most breakthroughs start with, "That's odd...".

As far as the implicit local scope, I haven't seen anything in JavaScript. I have not read up too much on Harmony to see the latest in JavaScript features. From the little that I have read about property descriptors and what not, it seems rather interesting!

In ColdFusion (my server-side language of choice), there are some miscellaneous methods like getActiveLocalScope() that allude to the kind of behavior that you are talking about; but, I've not seen any equivalents in the JavaScript side.

I'm not sure what Proxies are in your context. I'll have to do some reading and look at your link.

1 Comments

Ben,
I came across your blog because I was looking for some information about closures (not really related to this, but this was interesting enough to get me to read it). What I'm having trouble understanding, though, is why you'd go this route.

Object.create() is an ES5 method, which means that users of older browsers won't be able to execute your code. And, without that line, you could accomplish everything else with a simple closure containing your private properties and methods:

var sarah = (function() {
	var myWeight = 145;
	var weightOffset = -7;
	 
	var getAbsoluteWeight = function() { return myWeight; };
	var getWeight = function() { return myWeight + weightOffset; };
 
	// return public API
	return {
		getWeight: getWeight,
		getAbsoluteWeight: getAbsoluteWeight
	};
})();

This code works perfectly fine on ES3 browsers, and is the "traditional" way for creating private variables in JavaScript, on singleton objects or otherwise. Isn't it basically what you're doing with Object.create(), just another way?

15,848 Comments

@Rob,

More than anything, this was just an experiment. In reality, if I make "private" variables, I tend to go with "convention" over actual functionality - leading with an underscore:

_privateVar: ""
publicVar: ""

That said, Object.create() is supported by most modern browsers. And those that don't support it can use a Shim to re-create the functionality. I just use it to make the code a bit more simple.

As far as Singletons go, I tend to like to create constructors and then invoke them. This way, I can pass-in any required dependencies that the singleton may need.

I'm still trying to wrap my head around real modular JavaScript application architecture, though. I'll definitely be making a lot of mistakes, and then recalculating my route... like a GPS :D

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel