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

Определение Setter/Getter для нечеткой локальной переменной: невозможно?

Есть несколько предыдущих вопросов о том, как StackOverflow задает вопрос о доступе к локальным переменным через цепочку областей видимости, например, если вы хотите ссылаться на локальные переменные, используя нотацию в виде скобок и строку, вам нужно что-то вроде __local__["varName"]. До сих пор я не нашел даже самого хакерского метода для достижения этого и не придумал метод после нескольких часов использования каждого трюка, который я знаю.

Цель состоит в том, чтобы реализовать геттеры/сеттеры на произвольных неравномерных переменных. Object.defineProperties или __defineGet/Setter__ требуют вызова контекста. Для свойств в глобальном или оконном контексте вы можете выполнить задачу установки сеттера/получателя для прямых ссылок на объект.

Object.defineProperty(this, "glob", {get: function(){return "direct access"})
console.log(glob); //"direct access"

Даже в моих тестах с пользовательским расширением я скомпилирован в модифицированный Chromium, который выполняется до создания любого окна, где контекст является фактическим глобальным контекстом, и даже попытка вызвать this непосредственно в глобальном контексте приводит к сбой моей программы, Я могу снять это без помех:

Object.defineProperty(Object.prototype, "define", {
    value: function(name, descriptor){
        Object.defineProperty(this, name, descriptor);
    }
};
define("REALLYglobal", {get: function(){ return "above window context"; }});

И тогда он доступен во всех фреймах, созданных позже, в качестве глобального маршрутизируемого через указанный getter/setter. Старый __defineGet/Setter__ также работает в этом контексте, не указывая, на что его можно называть (в Firefox он не работает, но метод выше).

Таким образом, в основном можно определить get/set защиты для любой переменной на объекте, включая контекст окна/глобального контекста с прямым вызовом объекта (вам не нужно window.propname, просто propname). Это проблема в том, что вы не можете ссылаться на неграмотные скопированные переменные, являясь единственным типом, который может находиться в доступной области, но не имеет адресного контейнера. Конечно, они также наиболее часто используются, так что это не край. Эта проблема также превосходит текущую реализацию Proxies в ES6/Harmony, поскольку проблема связана с тем, что не может обращаться к локальному контейнеру объектов с синтаксисом языка.

Причина, по которой я хочу быть в состоянии сделать это, - это единственный барьер, позволяющий перегрузить большинство математических операторов для использования в сложных объектах, таких как массивы и хэши, и получить сложное итоговое значение. Мне нужно иметь возможность подключаться к сеттеру в случаях, когда значение задается для типа объекта, который я настроил для перегрузки. Нет проблем, если объект может быть глобальным или может содержаться в родительском объекте, что, вероятно, связано с тем, с чем я буду работать. Он по-прежнему полезен с a.myObject, но цель состоит в том, чтобы сделать его максимально прозрачным.

Не только это, но было бы очень полезно выполнить что-то вроде этого:

var point3d = function(){
    var x, y, z;
    return {
        get: function(){ return [x, y, z]; },
        set: function(vals){ x=vals[0]; y=vals[1]; z=vals[2]; }
    };
};

(Это похоже на деструктурирование ES6, но имеет более общие приложения для реализации функциональных возможностей, связанных с получением/настройкой, а не только для передачи сложных значений). Даже этот базовый код полностью провалится:

var x = {myname: "intercept valueOf and :set: to overload math ops!", index: 5};
x++; //x is now NaN if you don't implement a setter somehow

Мне все равно, насколько хриплым является решение, на данный момент это просто интенсивное любопытство для меня относительно того, можно ли это сделать, даже если это требует нарушения любой лучшей практики, которая существует. Я разбил Firefox и Chrome несколько сотен раз, преследуя это до сих пор, делая такие вещи, как переопределение/перехват/изменение Object.prototype.valueOf/toString, Function.prototype Function.prototype.constructor, Function.prototype.call/apply, arguments.callee.caller и т.д. С бесконечной рекурсией ошибок и еще чего-то в попытках задним числом приспособить условия бурения. Единственное, что я смог сделать, это обернуть в основном все это с помощью eval и динамического построения кусков кода, что является мостом слишком далеко для меня когда-либо на самом деле. Единственным удаленным удаленным маршрутом было использование with в сочетании с предварительным определением всех локальных переменных в контейнере, но это, очевидно, очень навязчиво над проблемами с использованием with.

4b9b3361

Ответ 1

В настоящее время это возможно в средах с Proxies. Это будет node > 0,6 работать как node --harmony_proxies или > 0,7 с node --harmony. Chromium Canary (не уверен, что это еще не так) примерно: флаги внизу, экспериментальный javascript. В Firefox некоторое время не было флагов.

Так что это, вероятно, не сработает, когда ES6 станет более официальным, но теперь он работает до некоторой степени.

  var target = (function(){
    var handler = Proxy.create(Proxy.create({
      get: function(r, trap){
        return function(name,val,c,d){
          if (trap === 'get' || trap === 'set') {
            name = val;
            val = c;
          }
          console.log('"'+trap + '" invoked on property "'+name+'" ' + (val?' with value "'+val+'"':''));
          switch (trap) {
            case 'get': return target[name];
            case 'set': return target[name] = val;
            case 'has': return name in target;
            case 'delete': return delete target;
            case 'keys': return Object.keys(target);
            case 'hasOwn': return Object.hasOwnProperty.call(target, name);
            case 'getPropertyDescriptor':
            case 'getOwnPropertyDescriptor': return Object.getOwnPropertyDescriptor(target, name);
            case 'getPropertyNames':
            case 'getOwnPropertyNames': return Object.getOwnPropertyNames(target);
            case 'defineProperty': return Object.defineProperty(target, name, val);
          }
        }
      }
    }))

    var target = {
      x: 'stuff',
      f: { works: 'sure did' },
      z: ['overwritten?']
    };


    with (handler){
      var z = 'yes/no';
      if (x) {
        //x
      } else {
        x = true;
      }
      console.log(f.works);
      if (f.works) {
        f.works = true;
        delete f;
      }

    }
    return target
  })()
   // "getPropertyDescriptor" invoked on property "z" 
   // "getPropertyDescriptor" invoked on property "z" 
   // "getPropertyDescriptor" invoked on property "x" 
   // "get" invoked on property "x" 
   // "getPropertyDescriptor" invoked on property "console" 
   // "getPropertyDescriptor" invoked on property "f" 
   // "get" invoked on property "f" 
   // sure did
   // "getPropertyDescriptor" invoked on property "f" 
   // "get" invoked on property "f" 
   // "getPropertyDescriptor" invoked on property "f" 
   // "get" invoked on property "f" 
   // "getPropertyDescriptor" invoked on property "f" 

   target: { x: 'Stuff', f: { works: true },  z: ['overwritten?'] }

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