Accessing private member variables from prototype-defined functions

前端 未结 25 1241
孤城傲影
孤城傲影 2020-11-22 14:48

Is there any way to make “private” variables (those defined in the constructor), available to prototype-defined methods?

TestClass = function(){
    var priv         


        
25条回答
  •  挽巷
    挽巷 (楼主)
    2020-11-22 15:21

    Was playing around with this today and this was the only solution I could find without using Symbols. Best thing about this is it can actually all be completely private.

    The solution is based around a homegrown module loader which basically becomes the mediator for a private storage cache (using a weak map).

       const loader = (function() {
            function ModuleLoader() {}
    
        //Static, accessible only if truly needed through obj.constructor.modules
        //Can also be made completely private by removing the ModuleLoader prefix.
        ModuleLoader.modulesLoaded = 0;
        ModuleLoader.modules = {}
    
        ModuleLoader.prototype.define = function(moduleName, dModule) {
            if (moduleName in ModuleLoader.modules) throw new Error('Error, duplicate module');
    
            const module = ModuleLoader.modules[moduleName] = {}
    
            module.context = {
                __moduleName: moduleName,
                exports: {}
            }
    
            //Weak map with instance as the key, when the created instance is garbage collected or goes out of scope this will be cleaned up.
            module._private = {
                private_sections: new WeakMap(),
                instances: []
            };
    
            function private(action, instance) {
                switch (action) {
                    case "create":
                        if (module._private.private_sections.has(instance)) throw new Error('Cannot create private store twice on the same instance! check calls to create.')
                        module._private.instances.push(instance);
                        module._private.private_sections.set(instance, {});
                        break;
                    case "delete":
                        const index = module._private.instances.indexOf(instance);
                        if (index == -1) throw new Error('Invalid state');
                        module._private.instances.slice(index, 1);
                        return module._private.private_sections.delete(instance);
                        break;
                    case "get":
                        return module._private.private_sections.get(instance);
                        break;
                    default:
                        throw new Error('Invalid action');
                        break;
                }
            }
    
            dModule.call(module.context, private);
            ModuleLoader.modulesLoaded++;
        }
    
        ModuleLoader.prototype.remove = function(moduleName) {
            if (!moduleName in (ModuleLoader.modules)) return;
    
            /*
                Clean up as best we can.
            */
            const module = ModuleLoader.modules[moduleName];
            module.context.__moduleName = null;
            module.context.exports = null;
            module.cotext = null;
            module._private.instances.forEach(function(instance) { module._private.private_sections.delete(instance) });
            for (let i = 0; i < module._private.instances.length; i++) {
                module._private.instances[i] = undefined;
            }
            module._private.instances = undefined;
            module._private = null;
            delete ModuleLoader.modules[moduleName];
            ModuleLoader.modulesLoaded -= 1;
        }
    
    
        ModuleLoader.prototype.require = function(moduleName) {
            if (!(moduleName in ModuleLoader.modules)) throw new Error('Module does not exist');
    
            return ModuleLoader.modules[moduleName].context.exports;
        }
    
    
    
         return new ModuleLoader();
        })();
    
        loader.define('MyModule', function(private_store) {
            function MyClass() {
                //Creates the private storage facility. Called once in constructor.
                private_store("create", this);
    
    
                //Retrieve the private storage object from the storage facility.
                private_store("get", this).no = 1;
            }
    
            MyClass.prototype.incrementPrivateVar = function() {
                private_store("get", this).no += 1;
            }
    
            MyClass.prototype.getPrivateVar = function() {
                return private_store("get", this).no;
            }
    
            this.exports = MyClass;
        })
    
        //Get whatever is exported from MyModule
        const MyClass = loader.require('MyModule');
    
        //Create a new instance of `MyClass`
        const myClass = new MyClass();
    
        //Create another instance of `MyClass`
        const myClass2 = new MyClass();
    
        //print out current private vars
        console.log('pVar = ' + myClass.getPrivateVar())
        console.log('pVar2 = ' + myClass2.getPrivateVar())
    
        //Increment it
        myClass.incrementPrivateVar()
    
        //Print out to see if one affected the other or shared
        console.log('pVar after increment = ' + myClass.getPrivateVar())
        console.log('pVar after increment on other class = ' + myClass2.getPrivateVar())
    
        //Clean up.
        loader.remove('MyModule')
    

提交回复
热议问题