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

Существует ли эквивалент функции __noSuchMethod__ для свойств или способ ее реализации в JS?

В некоторых реализациях JavaScript (Rhino, SpiderMonkey) есть функция noSuchMethod

proxy = {
    __noSuchMethod__: function(methodName, args){
        return "The " + methodName + " method isn't implemented yet. HINT: I accept cash and beer bribes" ;
    },

    realMethod: function(){
     return "implemented" ;   
    }
}

js> proxy.realMethod()
implemented
js> proxy.newIPod()
The newIPod method isn't implemented yet. HINT: I accept cash and beer bribes
js>

Мне было интересно, есть ли способ сделать что-то подобное для свойств? Я бы хотел написать классы прокси, которые могут отправлять как свойства, так и методы.

4b9b3361

Ответ 1

На данный момент существует только одна вещь, которая может делать то, что вы хотите, но, к сожалению, не получила широкого распространения:

В настоящее время доступны только две рабочие реализации: в последних бета-версиях Firefox 4 (это было со времен предварительных выпусков FF3.7) и в node-proxy для серверного JavaScript -Chrome и Safari в настоящее время работают над it-.

Это одно из ранних предложений для следующей версии ECMAScript, это API, который позволяет вам реализовывать виртуализированные объекты (прокси), где вы можете назначать различные ловушки - обратные вызовы - которые выполняются в разных ситуациях, вы получаете полный контроль над тем, что в настоящее время -in ECMAScript 3/5- могут выполнять только хост-объекты.

Чтобы построить прокси-объект, вы должны использовать метод Proxy.create, так как вас интересуют ловушки set и get, я оставлю вам очень простой пример:

var p = Proxy.create({
  get: function(proxy, name) {        // intercepts property access
    return 'Hello, '+ name;
  },
  set: function(proxy, name, value) { // intercepts property assignments
    alert(name +'='+ value);
    return true;
  }
});

alert(p.world); // alerts 'Hello, world'
p.foo = 'bar';  // alerts foo=bar

Попробуйте здесь.

Proxy API настолько нов, что даже не документирован в Центре разработчиков Mozilla, но, как я уже сказал, рабочая версия была включена с предварительных выпусков Firefox 3.7.

Объект Proxy доступен в глобальной области видимости, а метод create может принимать два аргумента: объект handler, который представляет собой просто объект, содержащий свойства, названные в качестве ловушек, которые вы хотите реализовать, и необязательный proto ], который позволяет вам указать объект, от которого наследуется ваш прокси.

Доступны следующие ловушки:

// TrapName(args)                          Triggered by
// Fundamental traps
getOwnPropertyDescriptor(name):           // Object.getOwnPropertyDescriptor(proxy, name)
getPropertyDescriptor(name):              // Object.getPropertyDescriptor(proxy, name) [currently inexistent in ES5]
defineProperty(name, propertyDescriptor): // Object.defineProperty(proxy,name,pd)
getOwnPropertyNames():                    // Object.getOwnPropertyNames(proxy) 
getPropertyNames():                       // Object.getPropertyNames(proxy) 
delete(name):                             // delete proxy.name
enumerate():                              // for (name in proxy)
fix():                                    // Object.{freeze|seal|preventExtensions}(proxy)

// Derived traps
has(name):                                // name in proxy
hasOwn(name):                             // ({}).hasOwnProperty.call(proxy, name)
get(receiver, name):                      // receiver.name
set(receiver, name, val):                 // receiver.name = val
keys():                                   // Object.keys(proxy)

Единственный ресурс, который я видел, кроме самого предложения, это следующий учебник:

Изменить: Появляется дополнительная информация. Брендан Айх недавно выступил с докладом на конференции JSConf.eu, его слайды можно найти здесь:

ОБНОВЛЕНИЕ (2019-08-14):

Эмуляция __noSuchMethod__ с прокси ES6

Реализуя ловушки при доступе к свойству, вы можете эмулировать поведение нестандартной ловушки __noSuchMethod__:

function enableNoSuchMethod(obj) {
  return new Proxy(obj, {
    get(target, p) {
      if (p in target) {
        return target[p];
      } else if (typeof target.__noSuchMethod__ == "function") {
        return function(...args) {
          return target.__noSuchMethod__.call(target, p, args);
        };
      }
    }
  });
}

// Example usage:

function Dummy() {
  this.ownProp1 = "value1";
  return enableNoSuchMethod(this);
}

Dummy.prototype.test = function() {
  console.log("Test called");
};

Dummy.prototype.__noSuchMethod__ = function(name, args) {
  console.log('No such method ${name} called with ${args}');
  return;
};

var instance = new Dummy();
console.log(instance.ownProp1);
instance.test();
instance.someName(1, 2);
instance.xyz(3, 4);
instance.doesNotExist("a", "b");

Ответ 2

Здесь, как получить поведение, подобное __noSuchMethod __

Прежде всего, здесь простой объект с одним методом:

var myObject = {
    existingMethod: function (param) {
        console.log('existing method was called', param);
    }
}

Теперь создайте прокси-сервер, который поймает доступ к свойствам/методу и добавит ваш существующий объект в качестве первого параметра.

var myObjectProxy = new Proxy(myObject, {
   get: function (func, name) {
       // if property or method exists, return it
       if( name in myObject ) {
           return myObject[name];
       }
       // if it doesn't exists handle non-existing name however you choose
       return function (args) {
           console.log(name, args);
       }
    }
});

Теперь попробуйте:

myObjectProxy.existingMethod('was called here');
myObjectProxy.nonExistingMethod('with a parameter');

Работает в Chrome/Firefox/Opera. Не работает в IE (но уже работает в Edge). Также протестирован на мобильных устройствах Chrome.

Создание прокси-сервера может быть автоматическим и невидимым, т.е. если вы используете шаблон Factory для создания ваших объектов. Я сделал это, чтобы создать рабочих, внутренние функции которых можно вызывать непосредственно из основного потока. Использование рабочих может быть настолько простым благодаря этой классной новой функции Proxy. Простейшая реализация работ:

var testWorker = createWorker('pathTo/testWorker.js');
testWorker.aFunctionInsideWorker(params, function (result) {
    console.log('results from worker: ', result);
});

Ответ 3

Я не считаю, что этот тип метапрограммирования возможен (пока) в javascript. Вместо этого попробуйте использовать функциональность __noSuchMethod__ для достижения эффекта с помощью свойств getters. Не кросс-браузер, а расширение Mozilla.

var proxy = {
    __noSuchMethod__: function(methodName, args) {
       if(methodName.substr(0,3)=="get") {
          var property = methodName.substr(3).toLowerCase();                             
          if (property in this) {
              return this[property];
          }
       }
    }, color: "red"
 };
 alert(proxy.getColor());           

Ответ 4

В SpiderMonkey есть __defineGetter__, __defineSetter__, __lookupGetter__ и __lookupSetter__ в дополнение к __noSuchMethod__.

Ответ 5

Вы можете использовать прокси-класс.

var myObj = {
    someAttr: 'foo'
};

var p = new Proxy(myObj, {
    get: function (target, propName) {
        // target is the first argument passed into new Proxy,
        // in this case target === myObj
        return 'myObj with someAttr:"' + target.someAttr 
               + '" had "' + propName 
               + '" called on it.';
    }
});
console.log(p.nonExsistantProperty);
// outputs: 
// myObj with someAttr:"foo" had "nonExsistantProperty" called on it