我想取一个对象并从中删除一些方法。
即。我内部有一个带有getter / setter的对象,我想让外部用户访问它。我不希望他们有权访问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;
};
请参阅live example,_.each
_.bindAll
出于所有预期目的,返回的对象应该与原始对象相同,除了某些方法不再存在。内部this
引用不应该在任何函数中断。原型链不应该破坏。
答案 0 :(得分:5)
你应该做的是使用只有你想要保留的方法的Facade Pattern和Delegation Pattern将这些方法委托给原始对象的实例。借助Javascript提供的丰富反射,您应该可以非常轻松地以编程方式生成这些Facades。
答案 1 :(得分:2)
您的实施至少有一个问题是依赖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的幂构造函数discussed here。)
编辑:我更新了上面的代码以解决您的意见。它现在看起来有点类似于您的实现,但它更简单并且避免了constructor
问题。如您所见,公共函数中的引用现在引用旧对象。
答案 2 :(得分:1)
如果你希望instanceof
有效,我们需要使用继承,就像@nrabinowitz的解决方案一样。在该解决方案中,隐藏了不需要的方法,其中键设置为null,并且用户可以访问这些键,因此用户可以重置这些键。我们可以通过将这些键隐藏在中间对象中来防止这种情况,并且因为它是从中间类实例化的,所以继承不会中断。
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();
}
在以下示例中,i
和j
表示实现成员值的两种方法。一个是关闭的私人成员,另一个是该类的公共成员。
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或增加i
和j
的值。
答案 3 :(得分:0)
如果您要做的只是将对象中的某些方法公开为public并将其余方法保密,那么您可以执行以下操作(在下面的示例中,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
};
}
}
答案 4 :(得分:0)
您正在寻找的是JavaScript中的模块模式。它与Gobhi描述的非常接近,但通常它是一个自动执行的功能。
详情可在此处找到:
http://yuiblog.com/blog/2007/06/12/module-pattern/
和:
http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth
答案 5 :(得分:0)
您选择的是非常复杂的方法。首先提供更多详细信息:
对评论1的回复:
关于Closure编译器混淆的更多信息。
Google使用以下方法(GMaps,GMail等)。
答案 6 :(得分:0)
我认为@ nrabinowitz的getRestricted
(link)功能几乎就是你要找的答案。
我不是JavaScript的GOF模式应用程序的忠实粉丝(本身就有一个完整的讨论)。但是这对我来说就像一个Decorator,因为我们正在改变某些对象的运行时行为 - 但反过来 - 如果你愿意的话,还可以使用De-Decorator:)
答案 7 :(得分:-1)
也许:
var yourfunction = function() {
var that = {};
var constructor = function() {
};
//private methods
var privateMethod = function() {
};
constructor();
//public methods
that.publicMethod = function() {
};
return that;
}
答案 8 :(得分:-1)
我会说与你的情况非常接近的模式是Proxy。
更新:正如评论所示,代理应该支持与真实对象相同的接口,因此不能很好地匹配问题中陈述的问题。在维基百科关于代理的文章的底部,我找到了一篇链接到一篇比较Proxy, Adapter and Facade模式的有趣文章。