0

For example:

function foo() {

  var bar = "", obj = {};

  obj.change = function(key, val){
    // how change bar?
    return obj;
  }

  return obj;
}

foo().change("bar", "foo");

If bar will be obj.bar answer is obj[key] = val; but then bar will be public. I want to have ability to set bar in jQuery-like style, but not to make it public.

Artem P
  • 5,198
  • 5
  • 40
  • 44

4 Answers4

2

You could encapsulate your code in an IIFE (immediately invoked function expression), and create two objects, one for public members, and another for private members. By using a closure you keep private private, and expose what you need.

var foo = (function() {

  var public = {},
      private = {
        name: 'John'
      };

  public.change = function(key, val) {
    private[key] = val;
  };

  public.say = function() {
    return 'Hello '+ private.name;
  };

  return public;
}());

console.log(foo.say()); //=> "Hello John"

foo.change('name', 'Mike');
console.log(foo.say()); //=> "Hello Mike"

Read about the Revealing Module Pattern for more info.

elclanrs
  • 92,861
  • 21
  • 134
  • 171
  • Few questions. Is this way common? How to make `foo()` global `F()` (for example), like `$()` in jQuery? `this` for this question is unusable? – Artem P Sep 02 '13 at 20:54
  • Yes it is a common pattern, but jQuery uses a constructor and a `prototype` AFAIK. The module pattern like above is typically used for singletons. Check the [jQuery source](http://code.jquery.com/jquery-1.10.2.js) to see how it is structured. – elclanrs Sep 02 '13 at 21:16
0

You cannot change variable with arbitrary name from closure scope, thouh. Need to put them in some object for reference:

function foo() {
  var data = {},
      obj = {};
  obj.change = function(key, val){
    if (arguments.length === 2) {
      data[key] = val;
      return obj
    } else if (arguments.length === 1) {
      return data[key];
    }
  }
  return obj;
}

foo().change("bar", "foo")
kirilloid
  • 14,011
  • 6
  • 38
  • 52
0

currently bar is private because its within the foo function's scope. Cant be accessed from outside. A simple solution will be to keep a private array list with all the keys.

function foo() {
 //private variables. Thanks to closure.
 var _privateKeys = [],
     obj = {};

 obj.change = function(key, val){
 if(arguments.length == 2)
   _privateKeys[key]=val;
 return obj;
 }

 return obj;
}

foo().change("bar", "foo");
Selvam Palanimalai
  • 1,550
  • 1
  • 10
  • 13
0

Final code inspired by elclanrs answer, this answer and this answer:

(function(window) {
    var private = {
        name: 'John'
    };
    function foo() {
      // if without `new` foo().change() will not work :(
      // but foo.change() will
      if (!(this instanceof foo))
          return new foo();
      return this;
    }
    foo.change = function(key, val) {
      private[key] = val;
      foo.say();
      return this;
    };
    foo.say = function() {
      console.log('Hello '+ private.name);
      return this;
    }; // prototype used for foo().say() and foo().change()
    foo.prototype.change = function(key, val) {
      return foo.change(key, val);
    };
    foo.prototype.say = function() {
      return foo.say();
    };
    window.foo = foo;
})(window);

foo.say().change('name', 'Ben'); 
// => "Hello John" => "Hello Ben"
foo().change('name', 'Mike').change('name', 'Sam');
// => "Hello Mike" => "Hello Sam"

jsfiddle.net

Community
  • 1
  • 1
Artem P
  • 5,198
  • 5
  • 40
  • 44