6

I'm using a Modular Pattern in JavaScript. I wonder if we can prevent the public modules to be overridden. For example, in the code below function1, function2, function3 and function4 can be accessed outside but I don't want to override. If these functions are overridden then I want the compiler to generate an error message

"use strict";

var $ = (function(){
return{
      function1 : function(){
          alert("this is Function1");
      },
      function2 : function(){
          alert("this is Function2");
      },
      function3 : function(){
          alert("this is Function3");
      },
      function4 : function(){
          alert("this is Function4");
      }
    };
}());


$.function1(); //will alert - this is Function1
$.function2(); //will alert - this is Function2

/* 
  I don't want to do this, If I do, then I want the compiler to generate an   
  error message
*/
$.function3=function(){
    alert('function 3 is overridden');

};
$.function3(); //will alert - function 3 is overridden
iLearn
  • 991
  • 1
  • 13
  • 27
  • For more in-depth discussion on this issue on SO: http://stackoverflow.com/questions/366047/can-read-only-properties-be-implemented-in-pure-javascript – msiadak Jan 13 '17 at 16:51

4 Answers4

9

You can use Object.freeze(obj) to set the entire returned object to be immutable. Additionally, note that you can use const instead of var to avoid the object being reassigned.

'use strict';

const $ = (function() {
  return Object.freeze({
    function1: function() {
      alert('this is Function1');
    },
    function2: function() {
      alert('this is Function2');
    },
    function3: function() {
      alert('this is Function3');
    },
    function4: function() {
      alert('this is Function4');
    }
  });
})();


$.function1(); //will alert - this is Function1
$.function2(); //will alert - this is Function2

// This will now error
$.function3 = function() {
  alert('function 3 is overridden');
};
$.function3(); // will not run
BCDeWitt
  • 4,540
  • 2
  • 21
  • 34
  • Awesome & Thanks ! This is what I need. – iLearn Jan 13 '17 at 16:54
  • There is still one problem. When I try to write the code like this, it will still override the function (by using Anonymous function). When I run it it will display the alert and then only it will give the error message "Uncaught TypeError: Cannot assign to read only property". And later when I try to call the function, it will not work at all because of the anonymous overridden function. $.function3=(function(){ alert('function 3 is overridden'); }()); – iLearn Jan 13 '17 at 17:00
  • @greencheese Your example is a self-invoking function/IIFE. The override doesn't actually succeed but the function does get run before the error happens. See if this helps with understanding: https://jsfiddle.net/0oku2tah/1/ – BCDeWitt Jan 13 '17 at 17:20
  • @greencheese If you want to catch this kind of error at compile-time, you'll need to run a third-party type checker during your development process. That will *not* prevent people who use your code from modifying it though. – Mike Cluck Jan 13 '17 at 17:20
8

Using Object.defineProperty you can declare the property as read-only.

// Make sure an error is thrown when attempting to overwrite it
// Without strict-mode, re-assigning will fail silently
'use strict';

var API = {};
Object.defineProperty(API, 'function1', {
  writable: false,
  value: function() {
    console.log('Called function1');
  }
});

API.function1();
API.function1 = null;
Mike Cluck
  • 31,869
  • 13
  • 80
  • 91
3

Yes, it can be prevented by using the Object.defineProperty method. Just set the writable and configurable attribute to false. You can find more info about that here

In your case, it should look like this:

var $ = (function(){

var newObject = {};

Object.defineProperty (newObject, "function1", {value: function() {  alert("this is Function1");}, writable:false, configurable:false});

// etc.

return object;
}());

Please note that setting the writable and configurable attributes to false just keeps the function1 property from overwriting. However, since every function in the JavaScript is a Function object, that Function object is not protected by using writable:false. So, for instance, the prototype of the function1 can still be changed.

If you want to completely save your functions from modifying in any way, you should use the Object.freeze method. Please read this very useful article.

d.popov
  • 4,175
  • 1
  • 36
  • 47
mgajic
  • 109
  • 6
  • 1
    This is the compatibility table for the Object.freeze method: http://kangax.github.io/compat-table/es5/#test-Object.freeze and this is the table for the Objact.defineProperty: http://kangax.github.io/compat-table/es5/#test-Object.defineProperty. Those tables are identical. – mgajic Jan 13 '17 at 17:34
0

There are serveral ways to do that. If you want to handle the message when overriding, you can use getter and setter:

var $ = (function(){
  var obj = {
      function1 : function(){
          alert("this is Function1");
      },
      function2 : function(){
          alert("this is Function2");
      },
      get function3() {
         alert("this is Function3");
      },
      set function3(value) {
         console.warn('You cannot override this function');
      },
      function4 : function(){
          alert("this is Function4");
      }
  };
  
  return obj
}());

// throw warning
$.function3 = 'foo';

Since you're using IIFE, you can define the source outside the property content and refer it.

Example with ES6 syntax:

let $ = (function () {
  let func3 = function () {
    alert('this is Function3');
  };

  class $ {
    static set function3(value) {
      return func3;
    }
    static get function3() {
      return func3;
    }
  }
  
  return $;
}());

$.function3 = 'foo';
$.function3();
Tân
  • 1
  • 15
  • 56
  • 102