删除对对象方法的公共访问

发布于 2024-10-18 07:01:02 字数 1167 浏览 1 评论 0原文

我想获取一个对象并从中删除一些方法。

即我内部有一个带有 getter/setter 的对象,并且我想让外部用户访问它。我不希望他们有权访问设置器功能。

我不想通过删除原始对象引用来更改原始对象引用,而是创建一个指向同一对象但具有较少方法的新对象引用。

  • 我该怎么做呢?
  • 这是一种设计模式吗?
  • 对于此类问题是否有众所周知的解决方案?

我有这个功能的实现

var readOnly = function(obj, publicData) {
    // create a new object so that obj isn't effected
    var object = new obj.constructor;
    // remove all its public keys
    _.each(object, function(val, key) {
        delete object[key];    
    });
    // bind all references to obj
    _.bindAll(obj);
    // for each public method give access to it
    _.each(publicData, function(val) {
        object[val] = obj[val];    
    });
    return object;
};

请参阅现场示例_.each _.bindAll

对于所有预期目的,返回的对象应该与原始对象相同,除了某些方法不再存在。内部 this 引用不应在任何函数中中断。原型链不应该断裂。

  • 这样的函数的直观名称是什么?
  • 我当前的实施是否存在任何我应该注意的陷阱?

I would like to take an object and remove some methods from it.

i.e. I have internally have an object with getter/setters on it and I want to give external users access to it. I don't want them to have access to the setter functions.

I don't want to change original object reference by removing methods from it but create a new object reference that points to the same object but has less methods on it.

  • How would I go about doing this?
  • Is this a design-pattern?
  • Are there well known solutions for these kinds of problems?

I have an implementation of this function

var readOnly = function(obj, publicData) {
    // create a new object so that obj isn't effected
    var object = new obj.constructor;
    // remove all its public keys
    _.each(object, function(val, key) {
        delete object[key];    
    });
    // bind all references to obj
    _.bindAll(obj);
    // for each public method give access to it
    _.each(publicData, function(val) {
        object[val] = obj[val];    
    });
    return object;
};

See live example, _.each _.bindAll

For all intended purposes the object returned should be the same as the original object except some of the methods aren't there anymore. The internal this reference should not break in any of the functions. The prototype chains should not break.

  • What would be an intuitive name for such a function?
  • Are there any pitfalls with my current implementation that I should be aware of?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

離人涙 2024-10-25 07:01:02

你应该做的是使用外观模式,它只包含你想要保留的方法和委托模式将这些方法委托给原始对象的实例。借助 Javascript 提供的丰富反射,您应该能够相当轻松地以编程方式生成这些外观。

What you should do is use a Facade Pattern that only has the methods you want to keep and Delegation Pattern to delegate those methods to an instance of the original object. With the rich reflection that is available with Javascript you should be able to generate these Facades programmaticlly fairly easily.

甜宝宝 2024-10-25 07:01:02

您的实现至少有一个问题是对 obj.constructor 的依赖。众所周知,constructor 属性不是只读的,而且很容易搞乱。考虑以下模式,这是在 Javascript 中定义类的一种非常常见的方法:

function Foo() {};
Foo.prototype = {
    myProperty: 1,
    myFunction: function() {
        return 2;
    }
};
// make an instance
var foo = new Foo();
foo instanceof Foo; // true
foo.constructor == Foo;  // false! The constructor is now Object
(new foo.constructor()) instanceof Foo; // false

我认为执行此操作的方法是以您的 obj 实例作为原型创建一个新类。然后,您可以通过在新类的对象上添加空键来阻止对旧函数的访问:(

function getRestricted(obj, publicProperties) {
    function RestrictedObject() {};
    RestrictedObject.prototype = obj;
    var ro = new RestrictedObject();
    // add undefined keys to block access
    for (var key in obj) {
        // note: use _.indexOf instead -- this was just easier to test
        if (publicProperties.indexOf(key) < 0) {
            ro[key] = null;
        } else {
            // again, use _.isFunction if you prefer
            if (typeof obj[key]=='function') {
                (function(key) {
                    // wrap functions to use original scope
                    ro[key] = function() {
                        // basically the same as _.bind
                        return obj[key].apply(obj, arguments);
                    }
                })(key);
            }
        }
    }
    return ro;
}

function Foo() {
    var a=0;
    this.getA = function() {
        this.setA(a+1);
        return a;
    };
    this.setA = function(newa) {
        a = newa;
    }
};

// make an instance
var foo = new Foo();
foo.setA(1);
foo.getA(); // 2

// make a restricted instance
var restrictedFoo = getRestricted(foo, ['getA']);
restrictedFoo.getA(); // 3
restrictedFoo instanceof Foo; // true
try {
    restrictedFoo.setA(2); // TypeError: Property 'setA' is not a function
} catch(e) {
    "not a function";
}
// one bump here:
"setA" in restrictedFoo; // true - just set to undefined

// foo is unaffected
foo.setA(4);
foo.getA(); // 5

这部分基于 Crockford 的强大构造函数,此处讨论。)


编辑:我更新了上面的代码以解决您的评论。它现在看起来与您的实现有些相似,但它更简单并且避免了构造函数问题。正如您所看到的,对公共函数的引用现在引用旧对象。

At least one issue with your implementation is the reliance on obj.constructor. The constructor property is notoriously not read-only, and can be easily messed up. Consider the following pattern, which is a pretty common way to define classes in Javascript:

function Foo() {};
Foo.prototype = {
    myProperty: 1,
    myFunction: function() {
        return 2;
    }
};
// make an instance
var foo = new Foo();
foo instanceof Foo; // true
foo.constructor == Foo;  // false! The constructor is now Object
(new foo.constructor()) instanceof Foo; // false

I think the way to do this is to create a new class with your obj instance as the prototype. You can then block access to the old functions by adding empty keys on an object of the new class:

function getRestricted(obj, publicProperties) {
    function RestrictedObject() {};
    RestrictedObject.prototype = obj;
    var ro = new RestrictedObject();
    // add undefined keys to block access
    for (var key in obj) {
        // note: use _.indexOf instead -- this was just easier to test
        if (publicProperties.indexOf(key) < 0) {
            ro[key] = null;
        } else {
            // again, use _.isFunction if you prefer
            if (typeof obj[key]=='function') {
                (function(key) {
                    // wrap functions to use original scope
                    ro[key] = function() {
                        // basically the same as _.bind
                        return obj[key].apply(obj, arguments);
                    }
                })(key);
            }
        }
    }
    return ro;
}

function Foo() {
    var a=0;
    this.getA = function() {
        this.setA(a+1);
        return a;
    };
    this.setA = function(newa) {
        a = newa;
    }
};

// make an instance
var foo = new Foo();
foo.setA(1);
foo.getA(); // 2

// make a restricted instance
var restrictedFoo = getRestricted(foo, ['getA']);
restrictedFoo.getA(); // 3
restrictedFoo instanceof Foo; // true
try {
    restrictedFoo.setA(2); // TypeError: Property 'setA' is not a function
} catch(e) {
    "not a function";
}
// one bump here:
"setA" in restrictedFoo; // true - just set to undefined

// foo is unaffected
foo.setA(4);
foo.getA(); // 5

(This is partially based on Crockford's power constructor functions, discussed here.)


EDIT: I updated the code above to address your comments. It now looks somewhat similar to your implementation, but it's simpler and avoids the constructor issue. As you can see, references to in public functions now refer to the old object.

同展鸳鸯锦 2024-10-25 07:01:02

如果你想让 instanceof 工作,我们需要使用继承,就像 @nrabinowitz 的解决方案。在该解决方案中,不需要的方法被隐藏,键设置为空,并且这些键可供用户访问,因此用户可能会重置它们。我们可以通过将这些键隐藏在中间对象中来防止这种情况,并且因为它是从中间类实例化的,所以继承不会中断。

function restrict(original, whitelist) {
    /* create intermediate class and instantiate */
    var intermediateClass = function() {};
    intermediateClass.prototype = original;
    var intermediateObject = new intermediateClass();

    /* create restricted class and fix constructor reference after prototype replacement */
    var restrictedClass = function() {};
    restrictedClass.prototype = intermediateObject;
    restrictedClass.prototype.constructor = original.constructor;
    if (restrictedClass.prototype.constructor == Object.prototype.constructor) {
        restrictedClass.prototype.constructor = restrictedClass;
    }

    for (var key in original) {
        var found = false;
        for (var i = 0; i < whitelist.length; i++) {
            if (key == whitelist[i]) {
                if (original[key] instanceof Function) {
                    /* bind intermediate method to original method */
                    (function(key) {
                        intermediateObject[key] = function() {
                            return original[key].apply(original, arguments);
                        }
                    })(key);
                }
                found = true;
                break;
            }
        }
        if (!found) {
            /* black out key not in the whitelist */
            intermediateObject[key] = undefined;
        }
    }

    return new restrictedClass();
}

在以下示例中,ij 表示实现成员值的两种方法。一个是闭包中的私有成员,另一个是类的公共成员。

var originalClass = function() {
    var i = 0;
    this.j = 0;

    this.getI = function() {
        return i;
    };
    this.setI = function(val) {
        i = val;
    };
}
originalClass.prototype.increaseI = function() {
    this.setI(this.getI() + 1);
};
originalClass.prototype.decreaseI = function() {
    this.setI(this.getI() - 1);
};
originalClass.prototype.getJ = function() {
    return this.j;
};
originalClass.prototype.setJ = function(val) {
    this.j = val;
};
originalClass.prototype.increaseJ = function() {
    this.setJ(this.getJ() + 1);
};
originalClass.prototype.decreaseJ = function() {
    this.setJ(this.getJ() - 1);
};

var originalObject = new originalClass();
var restrictedObject = restrict(originalObject, ["getI", "increaseI", "getJ", "increaseJ"]);

restrictedObject.increaseI();
restrictedObject.increaseJ();
console.log(originalObject.getI()); // 1
console.log(originalObject.getJ()); // 1
console.log(restrictedObject instanceof originalClass); // true

正如您所看到的,所有setter和reduce方法都隐藏在受限对象中。用户只能使用getter或增加ij的值。

If you want instanceof works, we need to use inheritance, like @nrabinowitz's solution. In that solution, unwanted methods are hidden with keys set to null, and those keys are accessible to user so they might be reset by user. We can prevent this by hiding those keys in an intermediate object, and because it's instantiated from an intermediate class so inheritance won't break.

function restrict(original, whitelist) {
    /* create intermediate class and instantiate */
    var intermediateClass = function() {};
    intermediateClass.prototype = original;
    var intermediateObject = new intermediateClass();

    /* create restricted class and fix constructor reference after prototype replacement */
    var restrictedClass = function() {};
    restrictedClass.prototype = intermediateObject;
    restrictedClass.prototype.constructor = original.constructor;
    if (restrictedClass.prototype.constructor == Object.prototype.constructor) {
        restrictedClass.prototype.constructor = restrictedClass;
    }

    for (var key in original) {
        var found = false;
        for (var i = 0; i < whitelist.length; i++) {
            if (key == whitelist[i]) {
                if (original[key] instanceof Function) {
                    /* bind intermediate method to original method */
                    (function(key) {
                        intermediateObject[key] = function() {
                            return original[key].apply(original, arguments);
                        }
                    })(key);
                }
                found = true;
                break;
            }
        }
        if (!found) {
            /* black out key not in the whitelist */
            intermediateObject[key] = undefined;
        }
    }

    return new restrictedClass();
}

In the following example, i and j represent two ways to implement member values. One is private member in closure and the other is public member of the class.

var originalClass = function() {
    var i = 0;
    this.j = 0;

    this.getI = function() {
        return i;
    };
    this.setI = function(val) {
        i = val;
    };
}
originalClass.prototype.increaseI = function() {
    this.setI(this.getI() + 1);
};
originalClass.prototype.decreaseI = function() {
    this.setI(this.getI() - 1);
};
originalClass.prototype.getJ = function() {
    return this.j;
};
originalClass.prototype.setJ = function(val) {
    this.j = val;
};
originalClass.prototype.increaseJ = function() {
    this.setJ(this.getJ() + 1);
};
originalClass.prototype.decreaseJ = function() {
    this.setJ(this.getJ() - 1);
};

var originalObject = new originalClass();
var restrictedObject = restrict(originalObject, ["getI", "increaseI", "getJ", "increaseJ"]);

restrictedObject.increaseI();
restrictedObject.increaseJ();
console.log(originalObject.getI()); // 1
console.log(originalObject.getJ()); // 1
console.log(restrictedObject instanceof originalClass); // true

As you can see, all setters and decrease methods are hidden in the restricted object. User can only use getter or increase the value of i and j.

猫卆 2024-10-25 07:01:02

如果您想要做的只是将对象中的某些方法公开为公共,并将其余方法保留为私有,您可以执行类似以下操作(在下面的示例中,privateMethod2 永远不会在返回给用户的新“只读”对象中显示):

function MyObject() {

  // Private member
  var name = "Bob"; 

  // Private methods
  function setName(n) { name = n; }
  function privateMethod2() { ... }
  function privateMethod3() { ... }

  // Expose certain methods in a new object
  this.readOnly() { 
    return {
      publicMethod1: setName,
      publicMethod2: privateMethod3
    }; 
  }

}

If all you want to do is expose certain methods in your object as public and keep the rest private, you can do something like this (in the example below privateMethod2 is never revealed in the new "read only" object returned to the user):

function MyObject() {

  // Private member
  var name = "Bob"; 

  // Private methods
  function setName(n) { name = n; }
  function privateMethod2() { ... }
  function privateMethod3() { ... }

  // Expose certain methods in a new object
  this.readOnly() { 
    return {
      publicMethod1: setName,
      publicMethod2: privateMethod3
    }; 
  }

}
澉约 2024-10-25 07:01:02

您正在寻找的称为 JavaScript 中的模块模式。它与 Gobhi 所描述的非常接近,但通常它是一个自执行函数。

详细信息可以在这里找到:

http://yuiblog.com/blog/2007/ 06/12/module-pattern/

和:

http ://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth

What you are looking for is called the module pattern in JavaScript. It's pretty close to what Gobhi describes but generally it is a self executing function.

Details can be found here :

http://yuiblog.com/blog/2007/06/12/module-pattern/

and :

http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth

痴情换悲伤 2024-10-25 07:01:02

您正在选择非常复杂的方法。首先提供更多细节:

  • 是否真的有必要扩展原始对象原型(也就是在运行时更改对象原型多少次)?
  • 您要创建多少个不同的对象?我所说的不同是指:从相同的对象类型创建,但使用不同的公共方法。
  • 您计划锁定多少种对象类型?
  • 您计划多久更新一次代码?
  • 您是否使用任何类型的压缩器/编译器?

对评论 1 的回应

  • 如果您锁定所有对象,那么处理性能问题(尤其是 IE)只是时间问题。
  • 到目前为止,您有 2-3 种想要锁定的对象类型。在委托模式中准备这2-3个对象不是更容易吗?请记住,您必须为灵活性付出代价;
  • 尝试在高级模式下使用闭包编译器。当您利用这个工具时,您将学习如何混淆对象属性并保留公共接口。另外,每次代码更改和重新编译都会产生新的隐藏变量/函数名称,因此祝你好运,尝试猜测是 setA 或 getA 函数。
  • 最后但并非最不重要的一点。尝试使您的公共界面尽可能整洁和小。这意味着:用匿名函数包装所有代码,并公开尽可能少的对象/方法/属性。

更多关于 Closure 编译器混淆工作的信息。

  1. 编译将所有对象属性重命名为:aa、ab、ac等;
  2. 您公开公共方法/属性:a.prototype.getA = a.prototype.aa; (在内部您使用 a.prototype.aa 方法。这意味着公共方法可以替换为任何值 - 这对您的代码没有影响);
  3. 最后你公开了对象: window['foo'] = new a;

Google 使用以下方法(GMaps、GMail 等)。

You are choosing very complicated approach. Provide more details first:

  • Does it really really necessary to extend original object prototype (aka how many times you change object prototype in run time)?
  • How many different object are you going to create? By saying different I mean: created from the same object type, but with different public methods.
  • How many object types are you planing to lock?
  • How often are you planing to update your code?
  • Are you using any kind of minifier/compiler?

Response to comment 1:

  • If you are lock all objects then it is a matter of time when you deal performance problems (especially with IE).
  • To date you have 2-3 object types you want to lock. Wouldn't it be easier to prepare these 2-3 objects in Delegation pattern? Remember that you have to pay the price for flexibility;
  • Try using Closure compiler in advanced mode. When you leverage this tool you gonna learn how to obfuscate object properties and leave public interface. Plus every code change and recompile results in new hidden variable/function names so good luck trying to guess witch one is setA or getA function.
  • And the last but no least. Try to keep your public interface as neat and small as possible. This means: wrap up all your code with anonymous function and disclose as few objects/methods/properties as you can.

A little bit more about Closure compiler obfuscation works.

  1. Compile renames all object properties to: aa, ab, ac, etc.;
  2. You disclose public methods/properties: a.prototype.getA = a.prototype.aa; (Internally you use a.prototype.aa method. This means that public method can be replaced with any value - this has no effect to your code);
  3. And finally You disclose object: window['foo'] = new a;

The following method is used by Google (GMaps, GMail and etc.).

凶凌 2024-10-25 07:01:02

我认为 @nrabinowitz 的 getRestricted (link) 函数几乎就是您正在寻找的答案。

我不太热衷于将 GOF 模式应用到 JavaScript(本身就有一个完整的讨论)。但这对我来说就像一个 装饰器 因为我们正在改变某些对象的运行时行为 - 但是反过来 - 如果你喜欢的话,可以使用 De-Decorator :)

I thought @nrabinowitz's getRestricted (link) function was pretty much the answer you were looking for.

I'm not a big fan of GOF pattern application to JavaScript (there's a whole discussion in itself). But this smells to me like a Decorator since we're changing runtime behaviour on certain objects - but in reverse - a De-Decorator if you like :)

乖乖 2024-10-25 07:01:02

或许:

var yourfunction = function() {
   var that = {};

   var constructor = function() {
   };

   //private methods
   var privateMethod = function() {
   };

   constructor();

   //public methods   
   that.publicMethod = function() {
   };

   return that;
}

maybe:

var yourfunction = function() {
   var that = {};

   var constructor = function() {
   };

   //private methods
   var privateMethod = function() {
   };

   constructor();

   //public methods   
   that.publicMethod = function() {
   };

   return that;
}
起风了 2024-10-25 07:01:02

我想说,与您的情况非常匹配的模式是代理

更新:正如注释所示,代理应该支持与真实对象相同的接口,因此与问题中所述的问题不太匹配。在关于代理的维基百科文章的底部,我找到了一篇有趣文章的链接,该文章比较了 代理、适配器和外观模式。

I'd say that the pattern that matches your situation quite closely is the Proxy.

UPDATE: As the comments indicate, a Proxy should support the same interface as the real object and therefore is not a good match to the problem stated in the question. At the bottom of the wikipedia article on Proxy I found a link to an interesting article that compares Proxy, Adapter and Facade patterns.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文