Подтвердить что ты не робот

Javascript - доступ к объекту объекта

Можно ли зафиксировать, когда к какому-либо (объекту) объекту обращается доступ или к нему обращаются?

Пример:

Я создал пользовательский объект Foo

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

Затем есть какое-то действие против Foo - кто-то пытается получить доступ к свойству bar

Foo.bar

Есть ли способ (прототип, возможно), чтобы зафиксировать это? bar может быть undefined на Foo. Мне было бы достаточно захватить любой попытку доступа к свойствам undefined.

Например, если bar - undefined на 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;
}
4b9b3361

Ответ 1

Да, это возможно в ES2015+, используя прокси-сервер Proxy. Это невозможно в ES5 и более ранних версиях, даже с полифиллами.

Это заняло у меня некоторое время, но я наконец нашел мой предыдущий ответ на этот вопрос. См. этот ответ для всех деталей о прокси и т.д.

Вот пример прокси из этого ответа:

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);

Live Copy:

"use strict";

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);

Ответ 2

Я напишу это в предположении, что вы пытаетесь отладить что-то. Как сказал Кроудер, это доступно только в новых браузерах; поэтому он очень полезен для тестирования кода, который делает то, чего вы не хотите. Но я удаляю его для производственного кода.

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.
  }
});

Похоже, megawac избил меня. Вы также можете найти документацию Mozilla по функциям здесь.

Ответ 3

Как уже было сказано, это возможно только с помощью Proxy объекта в ECMAScript6. Между тем, в зависимости от ваших потребностей и общего дизайна, вы все равно можете достичь этого, реализовав что-то подобное.

например.

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;

Ответ 4

При добавлении новых defineProperties, defineGetter и 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

Ответ 5

Как упоминалось в других ответах, на данный момент невозможно перехватить свойства undefined.

Будет ли это приемлемо, хотя?

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