1

Referring to Javascript: The Good parts, was trying to build an sort of class prototype object which I could then use to create instances. But using the pattern suggested for creating objects and information hiding via closures, I find I have created a private static member rather than an private instance member.

In this case I am attempting create an "account" object which has a private value (balance) variable which is returned by the balance() method:

<head>
    <script>
        if (typeof Object.create !== 'function') {
            Object.create = function (o) {
                var F = function () {
                };
                F.prototype = o;
                return new F;
            };
        };
        var account = function() {
            var value = 0;
            return {
                "account_name": "",
                deposit: function (amount) {
                    value = value + amount;
                },
                withdrawal: function (amount) {
                    value = value - amount;
                },
                balance: function( ) {
                    return value;
                }
            }
        }();
    </script>
</head>
<body>
    <script>
        var account1 = Object.create(account);
        var account2 = Object.create(account);
        account1.account_name = "Mario Incandenza";
        account1.deposit(100);
        account1.withdrawal(25);
        account2.account_name = "Hal Incandenza";
        account2.deposit(5100);
        account2.withdrawal(2000);
        document.writeln("<p>Account name = " + account1.account_name + "  Balance: " + account1.balance() + "</p>");
        document.writeln("<p>Account name = " + account2.account_name + "  Balance: " + account2.balance() + "</p>");
    </script>
</body>

Here is the result:

Account name = Mario Incandenza Balance: 3175

Account name = Hal Incandenza Balance: 3175

So seeing the sum of all withdrawals and deposits to both accounts obvious that I've created a static 'class' variable (in javaspeak)

So how to create var value at instance level and keep it hidden/private?

Krupesh Kotecha
  • 2,396
  • 3
  • 21
  • 40
GGizmos
  • 3,443
  • 4
  • 27
  • 72
  • JavaJavaScript has been done before https://www.gnu.org/software/easejs/, not worth it IMO, better just do JavaScript. – elclanrs Oct 29 '15 at 03:57
  • 2
    remove the calling `()` at the end of your function, and call them separately – webdeb Oct 29 '15 at 03:58
  • http://jsfiddle.net/jul101/tcyztz68/ is this what you want? – Lin Yuan Oct 29 '15 at 03:59
  • Please note that Douglas Crockford has of yet not been able to produce documentation or a presentation where he uses constructor functions correctly. He claims the parent constructor cannot be re used, breaks encapsulation by changing Function.prototype https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#Bad_practice_Extension_of_native_prototypes yet wants private members to prevent other users from doing the same to his code. And in his "helper" functions he creates an instance of Parent to be used as Child.prototype – HMR Oct 29 '15 at 06:07
  • You may be better off learning about JavaScript here: https://github.com/getify/You-Dont-Know-JS and some informatioin about prototype can be found here: http://stackoverflow.com/a/16063711/1641941 – HMR Oct 29 '15 at 06:08

2 Answers2

3

The () on the end of your var account = declaration means you are calling that initialization function only once and assigning its return value to a single object that you name account. You are then making multiple objects using that single object as a prototype, meaning they're all using the same closure, with its single balance value.

What you want to do instead is make account the function, and call it separately for each new object. You can do that by just moving the parentheses:

var account = function() {
    var value = 0;
    return {
        account_name: "",
        deposit: function (amount) {
            value = value + amount;
        },
        withdrawal: function (amount) {
            value = value - amount;
        },
        balance: function( ) {
            return value;
        }
    }
};

var account1 = Object.create(account());
var account2 = Object.create(account());

But once you make account a function, there's no reason to use Object.create; you can just use the return value of the function directly:

var account1 = account();
var account2 = account();

Alternatively, you could do things in a slightly more traditional way with new (even though Crockford says new is evil):

var Account = function() {
  var value = 0;
  this.deposit = function (amount) {
    value = value + amount;
  };
  this.withdrawal = function (amount) {
    value = value - amount;
  };
  this.balance = function( ) {
    return value;
  }
}

var account1 = new Account();
var account2 = new Account();

That's still not quite traditional classly Javascript, which would define the methods only once on Account.prototype. In order to accomplish the closure encapsulation, this version has to create a new copy of the methods on each instance.

Mark Reed
  • 91,912
  • 16
  • 138
  • 175
  • 5
    What's the point of using `Object.create()` then, when your `account() ` function already returns a new object each time? – nnnnnn Oct 29 '15 at 03:59
  • Very sensible. Thanks. I didn't really love the Object.create thing to begin with. Not exactly sure I understand what Mr. Crockford's allergy to 'new' is all about. – GGizmos Oct 30 '15 at 01:05
  • I understand his allergy. In general, if you approach Javascript like Java and start by building a class hierarchy, you're probably doing it wrong; it's not that kind of language. The fact that it has `new` gives the illusion that it is that kind of language, and can lead you down the wrong path. Not everything has to be an instance of a custom class with its own constructor function. But hiding information inside a single thing that manipulates it for you is exactly what objects are for. Also, since it creates new copies of the methods on every object, this is not traditional `new`. – Mark Reed Oct 30 '15 at 14:52
  • I guess I'm still vague on what would be the javascript way to build an object like "account", where clearly you don't wnat the internal state of the object to be subject manipulation outside of the its "deposit" and "withdrawal" methods. If new is evil and closures don't give me any instance level private variables, then what is the approach that is more consistent with spirit of the language? Or are you saying that Crockford's objection to "new" is only when you are using it as part of an attempt to build java-esque class hierarchies so the use of 'new' in this case isn't really evil? – GGizmos Oct 30 '15 at 18:41
  • Yes, your last sentence is what I'm saying. I don't think any of my answer is evil. Although the first version with Object.create is kind of silly. :) – Mark Reed Oct 30 '15 at 19:14
-2

Actually it would be better to write something like:

var account = function(/** name, id **/) {
  this.value = 0;
  //this.name = name;
  //this.id = id;
}

account.prototype.deposit = function(amount) {
  this.value += amount;
}

...

you are returning the functions for every instance, so it cost more memory then prototyping.. ;)

webdeb
  • 12,993
  • 5
  • 28
  • 44
  • But the goal is to make the value inaccessible. Regular object properties can be modified from anywhere; closed-over variables exist only within the body of the functions in the returned object. – Mark Reed Oct 29 '15 at 04:03
  • @webdeb on what memory leak? There is no memory leak in the original code. "more memory consumed" !========== memory leak – zerkms Oct 29 '15 at 04:07
  • @zerkms very simple.. prototyped methods are shared across all instances.. by returning an object for each instance you are creating new objects, clear now? – webdeb Oct 29 '15 at 04:10
  • @webdeb what you explained is not what the "memory leak" term means https://en.wikipedia.org/wiki/Memory_leak – zerkms Oct 29 '15 at 04:16