2016-06-08 109 views
14

我試圖擴大代理,像這樣:我可以使用ES2015類來擴展Proxy嗎?

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中被認爲是「異域對象」,意味着它們「不具有一個或多個必須由所有對象支持的基本內部方法的默認行爲」。他們沒有任何原型,這是您通常會爲您正在擴展的類型獲取大部分行爲的原型。 From 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.

這不是巴別塔的限制。如果試圖在Chrome中,它和類的語法都原生支持擴展Proxy,你仍然會得到類似的錯誤:

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

「否」是實際的答案。但是,Alexander O'Mara指出,如果您爲Proxy.prototype(總數!)指定值,則至少在某些瀏覽器中可以擴展。 We experimented with this a little。由於異常代理實例的行爲,這不能用於完成比使用包裝構造函數的函數更多的功能,並且某些行爲在瀏覽器之間看起來並不一致(我不確定規範期望的結果如果你這樣做)。請不要以嚴肅的代碼嘗試這樣的事情。

+0

在一個側面說明,修補在'prototype'屬性將繞過此'TypeError'在本地的,非babel的實現,但'代理'的方式顯然實現覆蓋任何子類,給你留下一個令人費解的'代理'別名。 –

+0

@ AlexanderO'Mara這非常有趣。我對內部語義不夠熟悉,無法理解這是明確指定的還是實現細節。我設置了'.prototype = null'並測試了一下。你的子類構造函數可以採用不同的參數,並且如果它返回一個Object,該Object將被正確地生成而不是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 = new 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規範(我忘記了)指出,在類層次結構中,基類負責分配對象。因此,在層次結構中,不會繼承的類(除了隱式地從Object)將負責分配。在這種情況下,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 

p是種MyProxy,它是由同時類的A延長。 A不是原始的原型,而是代理的,有點。

0

來自@John L.自我回應:
在構造函數內部,我們可以使用Proxy來包裝新創建的實例。無需擴展代理。

實施例,提供從現有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)