2016-06-08 7 views
14

Я пытался продлить прокси, например, так:Могу ли я продлить прокси с классом ES2015?

class ObservableObject extends Proxy {} 

Я использовал Бабеля, чтобы transpile его ES5, и я получил эту ошибку в браузере:

app.js:15 Uncaught TypeError: Object prototype may only be an Object or null: undefined 

Я смотрел на строку кода он указал на. Вот та часть кода со стрелками, указывающими на ошибочную строку кода:

var ObservableObject = exports.ObservableObject = function (_Proxy) { 
    _inherits(ObservableObject, _Proxy); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 

    function ObservableObject() { 
     _classCallCheck(this, ObservableObject); 

     return _possibleConstructorReturn(this, Object.getPrototypeOf(ObservableObject).apply(this, arguments)); 
    } 

    return ObservableObject; 
}(Proxy); 

Кто-нибудь знает, почему я мог бы получить эту ошибку? Это ошибка в Вавилоне? Что должно произойти, когда вы пытаетесь расширить прокси?

ответ

18

Нет, класс ES2015 не может распространяться Proxy-.

Объекты прокси имеют очень атипичную семантику и считаются «экзотическими объектами» в ES2015, что означает, что они «не имеют поведения по умолчанию для одного или нескольких основных внутренних методов, которые должны поддерживаться всеми объектами». У них нет прототипа, в котором вы обычно получаете большую часть поведения для типа, который вы расширяете. Из section 26.2.2: "Properties of the Proxy Constructor" in the specification:

The Proxy constructor does not have a prototype property because proxy exotic objects do not have a [[Prototype]] internal slot that requires initialization.

Это не является ограничением Бабеля. Если вы пытаетесь расширить Proxy в Chrome, где он и синтаксис класса оба изначально поддерживается, вы все равно получите подобную ошибку:

Uncaught TypeError: Class extends value does not have valid prototype property undefined

«Нет» является практическим ответом. Однако Alexander O'Mara указал, что если вы присвоите значение Proxy.prototype (брутто!), Становится возможным расширение, по крайней мере, в некоторых браузерах. We experimented with this a little. Из-за поведения экзотических экземпляров Proxy это невозможно использовать для выполнения гораздо большего, чем вы могли бы сделать с помощью функции, обертывающей конструктор, и некоторое поведение не кажется согласованным между браузерами (я не уверен, что ожидает спецификация если вы это сделаете). Пожалуйста, не пытайтесь делать что-либо подобное в серьезном коде.

+0

На стороне записки, заплат в 'prototype' собственности будет обойти этот' TypeError' в но не в стиле babel, но способ «Proxy», по-видимому, реализуется, перезаписывает любые дочерние классы, оставляя вам только пронумерованный псевдоним для 'Proxy'. –

+0

@ AlexanderO'Mara Это очень интересно. Я недостаточно знаком с внутренней семантикой, чтобы понять, четко ли это указано или детали реализации. Я установил '.prototype = null' и немного протестировал это. Конструктор вашего подкласса может принимать разные аргументы, и если он возвращает объект, который будет правильно создан Объект, вместо экземпляра Proxy, поэтому конструктор подкласса используется должным образом. Я думаю, что мы можем успешно создать подкласс Proxy, но подкласс не может реально изменять поведение его экземпляров из-за экзотических ситуаций. –

+0

Я * думаю * это связано с тем, как конструктор JavaScript может возвращать объект, отличный от неявного 'this'. Если родительский конструктор делает это, то этот объект заменяет 'this', когда подкласс вызывает супер и наследование в основном выбрасывается. Поэтому я подозреваю, что Proxy в основном работает под капотом. –

0

Вавилон не поддерживает прокси, просто потому, что он не может. Поэтому, пока браузеры не поддерживают поддержку, ее не существует.

Из документов Бабеля: «неподдерживаемой особенности Из-за ограничения ES5, доверенные лица не может быть transpiled или polyfilled»

0
class c1 { 
    constructor(){ 
     this.__proto__ = new Proxy({}, { 
      set: function (t, k, v) { 
       t[k] = v; 
       console.log(t, k, v); 
      } 
     }); 
    } 
} 

d = новый c1(); d.a = 123;

+0

это может сработать, но немного объясните пожалуйста – cske

+0

Должен ли быть Object.setPrototypeOf вместо этого .__ proto__? –

17

Хорошо, я забыл об этом вопросе, но кто-то недавно поддержал его. Даже если вы технически не можете расширять прокси-сервер, есть способ заставить класс создавать экземпляр в качестве прокси-сервера и заставить все его подклассы создавать экземпляры в качестве прокси-сервера с теми же функциями дескриптора свойств (я тестировал это только в Chrome):

class ExtendableProxy { 
    constructor() { 
     return new Proxy(this, { 
      set: (object, key, value, proxy) => { 
       object[key] = value; 
       console.log('PROXY SET'); 
       return true; 
      } 
     }); 
    } 
} 

class ChildProxyClass extends ExtendableProxy {} 

let myProxy = new ChildProxyClass(); 

// Should set myProxy.a to 3 and print 'PROXY SET' to the console: 
myProxy.a = 3; 
+0

Я пробовал это, и он (сейчас) работает. Однако я не понимаю, почему, можете ли вы объяснить, как и почему это работает?Это похоже на умное использование ошибки, но если это так, я подозреваю, что это не продолжится работать бесконечно ... Пожалуйста, расширьте это! –

+0

@Wim Barelds Я не эксперт по стандартам ecma, но вот догадка: если вы не определяете конструктор для дочернего класса, конструктор становится 'constructor() {super(); } '. 'super()' является вызовом конструктора родительского класса. Если конструктор что-то возвращает, то 'new ()' возвращает значение, возвращаемое '()'. Такое поведение было некоторое время в старомодных конструкторах javascript. Итак, когда вызывается 'super', построенный объект становится прокси. –

+1

@WimBarelds Спецификация ECMAScript 6/7 (я забываю) утверждает, что в иерархии классов базовый класс отвечает за выделение объекта. Таким образом, в иерархии класс, который не наследует (кроме неявно от объекта), отвечает за распределение. В этой ситуации ExtendableProxy выделяет новые экземпляры как экземпляры Proxy. Так что это работает. И я не думаю, что это ошибка. –

0
class A { } 

class MyProxy { 
    constructor(value, handler){ 
    this.__proto__.__proto__ = new Proxy(value, handler); 
    } 
} 


let p = new MyProxy(new A(), { 
    set: (target, prop, value) => { 
    target[prop] = value; 
    return true; 
    }, 
    get: (target, prop) => { 
    return target[prop]; 
    } 
}); 

console.log("p instanceof MyProxy", p instanceof MyProxy); // true 
console.log("p instanceof A", p instanceof A); // true 

р вид MyProxy и он был продлен A класса одновременно. A не оригинальный прототип, он был проксимирован, вроде.

0

Из ответа @John L. self:
Внутри конструктора мы можем использовать прокси, чтобы обернуть вновь созданный экземпляр. Не нужно расширять прокси.

Пример, обеспечить наблюдаемую точку из существующего класса Point:

class Point { 

    constructor(x, y) { 
     this.x = x 
     this.y = y 
    } 

    get length() { 
     let { x, y } = this 
     return Math.sqrt(x * x + y * y) 
    } 

} 

class ObservedPoint extends Point { 

    constructor(x, y) { 

     super(x, y) 

     return new Proxy(this, { 
      set(object, key, value, proxy) { 
       if (object[key] === value) 
        return 
       console.log('Point is modified') 
       object[key] = value 
      } 
     }) 
    } 
} 

тест:

p = new ObservedPoint(3, 4) 

console.log(p instanceof Point) // true 
console.log(p instanceof ObservedPoint) // true 

console.log(p.length) // 5 

p.x = 10 // "Point is modified" 

console.log(p.length) // 5 

p.x = 10 // nothing (skip) 

 Смежные вопросы

  • Нет связанных вопросов^_^