2016-08-03 2 views
1

Предположим, что мы имеем:Как сделать класс ES6 окончательный (не-subclassible)

class FinalClass { 
    ... 
} 

Как изменить его, чтобы сделать

class WrongClass extends FinalClass { 
    ... 
} 

или

new WrongClass(...) 

генерировать исключение ? Возможно, наиболее очевидным решением является следующее в конструкторе FinalClass в:

if (this.constructor !== FinalClass) { 
    throw new Error('Subclassing is not allowed'); 
} 

Кто-нибудь есть более чистое решение, а не повторять эти строки в каждом классе, которые должны быть окончательными (вероятно, с декоратор)?

+3

Интересный вопрос. Есть ли причина, по которой вы пытаетесь это сделать? – gcampbell

+0

Так как в конечном итоге все это скомпилировано до Javascript (<5), где таких вещей не существует, и все это просто объект, который бесконечно гибкий во время выполнения, я думаю, что шансы на это плохие. – deceze

+0

Чтобы более ярко выражать точку, на которую указывает @gcampbell, это выглядит мне плохой идеей, граничащей с ужасным. Например, строки в Java сосать в немалой степени, потому что класс не имеет много полезных методов и был «последним» как преждевременный взлом производительности. –

ответ

6

Осмотрите this.constructor в конструкторе FinalClass и выбросьте, если он не является самим собой. (Заимствование осмотр this.constructor вместо this.constructor.name от @Patrick Робертса.)

class FinalClass { 
 
    constructor() { 
 
    if (this.constructor !== FinalClass) { 
 
     throw new Error('Subclassing is not allowed') 
 
    } 
 
    console.log('Hooray!') 
 
    } 
 
} 
 

 
class WrongClass extends FinalClass {} 
 

 
new FinalClass() //=> Hooray! 
 

 
new WrongClass() //=> Uncaught Error: Subclassing is not allowed

В качестве альтернативы, при поддержке, использовать new.target. Спасибо @loganfsmyth.

class FinalClass { 
 
    constructor() { 
 
    if (new.target !== FinalClass) { 
 
     throw new Error('Subclassing is not allowed') 
 
    } 
 
    console.log('Hooray!') 
 
    } 
 
} 
 

 
class WrongClass extends FinalClass {} 
 

 
new FinalClass() //=> Hooray! 
 

 
new WrongClass() //=> Uncaught Error: Subclassing is not allowed

______

Как вы говорите, вы можете также достичь такого поведения с декоратором.

function final() { 
 
    return (target) => class { 
 
    constructor() { 
 
     if (this.constructor !== target) { 
 
     throw new Error('Subclassing is not allowed') 
 
     } 
 
    } 
 
    } 
 
} 
 

 
const Final = final(class A {})() 
 

 
class B extends Final {} 
 

 
new B() //=> Uncaught Error: Subclassing is not allowed

Как поделился в комментариях Патрик Робертс декоратор синтаксис @final еще в предложении. Он доступен с Babel и babel-plugin-transform-decorators-legacy.

+2

Или даже лучше, если вы не транслируете и не поддерживаете его, создайте 'new.target! == target', и вы избежите случая с краем, который может быть переназначен' this.constructor'. – loganfsmyth

+1

@loganfsmyth 'new.target' не является безупречным, например, 'Reflect.construct (WrongClass, [], FinalClass)' – Oriol

3

constructor.name достаточно легко обмануть. Просто сделайте подкласс такое же имя, как суперкласса:

class FinalClass { 
 
    constructor() { 
 
    if (this.constructor.name !== 'FinalClass') { 
 
     throw new Error('Subclassing is not allowed') 
 
    } 
 
    console.log('Hooray!') 
 
    } 
 
} 
 

 
const OopsClass = FinalClass 
 

 
;(function() { 
 
    class FinalClass extends OopsClass {} 
 

 
    const WrongClass = FinalClass 
 

 
    new OopsClass //=> Hooray! 
 

 
    new WrongClass //=> Hooray! 
 
}())

Лучше проверить сам constructor:

class FinalClass { 
 
    constructor() { 
 
    if (this.constructor !== FinalClass) { 
 
     throw new Error('Subclassing is not allowed') 
 
    } 
 
    console.log('Hooray!') 
 
    } 
 
} 
 

 
const OopsClass = FinalClass 
 

 
;(function() { 
 
    class FinalClass extends OopsClass {} 
 

 
    const WrongClass = FinalClass 
 

 
    new OopsClass //=> Hooray! 
 

 
    new WrongClass //=> Uncaught Error: Subclassing is not allowed 
 
}())

+0

'// => Ура! :-) ' – sdgluck

+0

Хотя это технически правильно, неуместные точки с запятой и отсутствие парнеров с« новыми »на самом деле не являются практикой, которую следует поощрять. Оба делают код двусмысленным в лучшем случае и легко ломаются, если вы перегруппируете его. – ssube

+2

@ssube Отсутствие parens с 'new' вызывает раздражение (' new Foo.bar() 'vs' new Foo(). Bar() '), но стиль с точкой с запятой я не исправляю; (предназначенный для каламбура) он используется в больших проектах (например, нрм), и есть 3300000000000004 других вещей, на которые жаловаться в JS. (горит пламя) – gcampbell

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