Javascript - 捕获对对象属性的访问

IT技术 javascript object-properties
2021-02-22 00:34:54

是否可以在访问或尝试访问对象的(任何)属性时进行捕获?

例子:

我创建了自定义对象 Foo

var Foo = (function(){
    var self = {};
    //... set a few properties
    return self;
})();

然后有一些行动反对Foo- 有人试图访问财产bar

Foo.bar

有没有办法(也许是原型)来捕捉这个?bar上可能未定义Foo我可以捕获对未定义属性的任何尝试访问。

例如,如果bar在 上未定义Foo,并Foo.bar尝试使用,例如:

Foo.prototype.undefined = function(){
    var name = this.name; //name of property they attempted to access (bar)
    Foo[name] = function(){
        //...do something
    };
    return Foo[name];
}

但功能,不像我的例子。

概念

Foo.* = function(){
}

背景

如果我有一个自定义函数,我可以在每次调用这个函数时监听(见下文)。只是想知道是否可以访问属性。

Foo = function(){};
Foo.prototype.call = function(thisArg){
    console.log(this, thisArg);
    return this;
}
5个回答

是的,这在 ES2015+ 中是可能的,使用Proxy这在 ES5 及更早版本中是不可能的,甚至在 polyfills 中也不可能。

我花了一段时间,但我终于找到了我以前对这个问题的答案有关代理等的所有详细信息,请参阅该答案。

这是该答案中的代理示例:

const obj = new Proxy({}, {
    get: function(target, name, receiver) {
        if (!(name in target)) {
            console.log("Getting non-existant property '" + name + "'");
            return undefined;
        }
        return Reflect.get(target, name, receiver);
    },
    set: function(target, name, value, receiver) {
        if (!(name in target)) {
            console.log("Setting non-existant property '" + name + "', initial value: " + value);
        }
        return Reflect.set(target, name, value, receiver);
    }
});

console.log("[before] obj.foo = " + obj.foo);
obj.foo = "bar";
console.log("[after] obj.foo = " + obj.foo);
obj.foo = "baz";
console.log("[after] obj.foo = " + obj.foo);

实时复制:

运行时,输出:

获取不存在的属性 'foo'
[之前] obj.foo = 未定义
设置不存在的属性 'foo',初始值:bar
[之后] obj.foo = bar
[之后] obj.foo = baz
既然你似乎明白了我要去哪里...... Javascript 尝试找到一个属性,如果找不到,则尝试在原型中找到该属性(根据我的理解) - 可能类似于Foo.prototype.prototype 工作吗?我可以通过原型层捕捉 javascript 嗅探并对此做出反应吗?
2021-04-23 00:34:54
@RandyHall 您可以查看 Google Dart,它编译为 JS 并以跨浏览器的方式支持代理,或者使用我的回答中描述的方法。然而,我所描述的方法意味着设计的改变并且会对性能产生影响。
2021-04-28 00:34:54
我倾向于同意,这是很好的信息,但在接受这一点之前我会坚持一段时间。有时我对人们想出的巫术巫术感到惊讶。
2021-05-07 00:34:54
@RandyHall:可悲的是,在这种情况下,我很确定没有巫毒教可用。:-) 请参阅上面链接的上一个答案(最初来自 2011 年)。
2021-05-16 00:34:54
@RandyHall:很遗憾,没有。今天根本不可能拦截该操作(Firefox 除外)。就是将代理添加到下一版标准的原因。:-)
2021-05-19 00:34:54

我将在假设您正在尝试调试某些内容的情况下编写此内容。正如 Crowder 所说,这仅适用于较新的浏览器;因此它对于测试执行您不希望它执行的操作的代码非常有用。但是,我将其删除以用于生产代码。

Object.defineProperty(Foo, 'bar', {
  set: function() {
    debugger; // Here is where I'll take a look in the developer console, figure out what's
    // gone wrong, and then remove this whole block.
  }
});

看起来 megavac 打败了我。您还可以在此处找到有关这些功能的一些 Mozilla 文档

就像已经回答的那样,只能使用ProxyECMAScript6 中对象。同时,根据您的需求和整体设计,您仍然可以通过实现类似的东西来实现这一点。

例如

function WrappingProxy(object, noSuchMember) {
    if (!this instanceof WrappingProxy) return new WrappingProxy(object);

    this._object = object;

    if (noSuchMember) this.noSuchMember = noSuchMember;
}

WrappingProxy.prototype = {
    constructor: WrappingProxy,

    get: function (propertyName) {
        var obj = this._object;

        if (propertyName in obj) return obj[propertyName];

        if (this.noSuchMember) this.noSuchMember(propertyName, 'property');
    },

    set: function (propertyName, value) {
        return this._object[propertyName] = value;
    },

    invoke: function (functionName) {
        var obj = this._object, 
            args = Array.prototype.slice.call(arguments, 1);

        if (functionName in obj) return obj[functionName].apply(obj, args);

        if (this.noSuchMember) {
            this.noSuchMember.apply(obj, [functionName, 'function'].concat(args));
        }
    },

    object: function() { return this._object },

    noSuchMember: null
};

var obj = new WrappingProxy({
        testProp: 'test',
        testFunc: function (v) {
            return v;
        }
    },
    //noSuchMember handler
    function (name, type) {
        console.log(name, type, arguments[2]);
    }
);

obj.get('testProp'); //test
obj.get('nonExistingProperty'); //undefined, call noSuchMember
obj.invoke('testFunc', 'test'); //test
obj.invoke('nonExistingFunction', 'test'); //undefined, call noSuchMember

//accesing properties directly on the wrapped object is not monitored
obj.object().nonExistingProperty;
啊好吧,我知道你在那里做什么。
2021-05-03 00:34:54
我很好奇。您可以为此添加一个小示例用法吗?
2021-05-10 00:34:54
@RandyHall 你看过代码的最后吗?有一个使用示例。基本上,您将任何对象包装到 a 中WrappingProxy,然后通过方法调用(包括属性访问)执行所有交互。基本上,如果您希望保持一致,则必须避免使用对象的非包装版本。如果您有一个非常具体的用例来捕获不存在的属性,则可以使用这种方法。
2021-05-12 00:34:54

随着新的definePropertiesdefineGetter并且defineSetter被添加到JavaScript中,你可以做有点类似。__properties__然而,仍然没有真正的方法来隐藏对象的 。我建议你看看这篇文章

var obj = {
    __properties__: {
        a: 4
    }
}
Object.defineProperties(obj, {
    "b": { get: function () { return this.__properties__.a + 1; } },
    "c": { get: function (x) { this.__properties__.a = x / 2; } }
});

obj.b // 2
obj.c // .5

这是应该在任何环境中工作的经典模型

//lame example of a model
var Model = function(a) {
    this.__properties__ = {a: a};
}

Model.prototype.get = function(item) {
    //do processing
    return this.__properties__[item];
}

Model.prototype.set = function(item, val) {
    //do processing
    this.__properties__[item] = val;
    return this;
}

var model = new Model(5);
model.get("a") // => 5
很好的信息,但并没有完全实现我的目标。我试图通过在访问时定义每个属性来防止“未定义”。每次有人尝试访问对象的未定义属性时,我都想调用一个函数,使用对象本身内部的函数。
2021-05-17 00:34:54

正如其他答案所提到的,目前无法拦截未定义的属性。

不过,这可以接受吗?

var myObj = (function() {
  var props = {
    foo : 'foo'
  }
  return {
    getProp : function(propName) { return (propName in props) ? props[propName] : 'Nuh-uh!' }
  }
}());

console.log(myObj.getProp('foo')); // foo
console.log(myObj.getProp('bar')); // Nuh-uh
您的想法非常适用于 React.js 中的自定义表单,谢谢!
2021-05-07 00:34:54