2013-05-31 2 views
3

Вот пример:JavaScript кластерные методы в суб-объекты

var Bidule = function() { 
    this.value = 8 ; 

    this.calc = { 
     plus : function(x) { return this.value + x ; }, 
     minus : function(x) { return this.value - x ; }, 
     square : function() { return this.value * this.value ; } 
    } 

    this.fn = { 
     random : { 
      set : function() { return this.value = random() ; }, 
      add : function() { return this.value += random() ; } 
     } 
    } 
} ; 

var b = new Bidule() ; 
console.log(b.value) ;   // 8 
console.log(b.calc.plus(4)) ;  // Error : 'this.value' is undefined. 
console.log(b.fn.random.add()) ; // Error : 'this.value' is undefined. 

Дело в том, что кластерный методы, как она выглядит более элегантно, чем мне «b.fn_random_add()».

Это легко исправить «это» ссылка:

var _plus = b.calc.plus ; 
b.calc.plus = function() { 
    return _plus.apply(b, arguments) ; 
} ; 

console.log(b.calc.plus(4)) ;  // 12 

Однако ни предыдущий «this.calc.plus», ни новый набор один находятся в прототипе Bidule в.

Я думал о том, подобъектах со своим прототипом, как:

this.calc = new Bidule_calc ; 

Но я не был бы в состоянии установить главный объект «это» ссылки.

Есть ли другой способ, чем ...:

b.calc.plus.call(b, 4) ; 

... установить и вызвать кластерные методы?


Как я пишу это, я только что узнал, возможное решение:

var context = this ; 
Object.defineProperty(this.calc, 'value', { 
    get : function() { 
     return context.value ; 
    } 
}) ; 

Однако, есть еще проблема бесцельно дублирующих функций, как «this.calc» не в прототип и «Object.defineProperty» будут вызываться для каждого экземпляра Bidule, поэтому будут созданы дублированные функции для переопределения кластеризованных методов.


Edit: я должен точено, что мы должны использовать прототип для всех методов:

var Bidule = function() { 
    this.value = 8 ; 
} ; 
Bidule.prototype = { 
    getValue : function() { return this.value ; } 
} ; 

Хотя конструктор Bidule и прототип имеет два разделенных областей. Это означает, что мы не можем делиться никаким «var» в конструкторе, который будет использоваться для методов.

+1

Быстрое «исправление» заключается в том, чтобы объявить «var that = this;» в верхней части вашей основной функции, а затем ссылаться на «это» во всех подмодулях, когда вам нужно ссылаться на владельца «Bidule». Во-вторых, нет метода 'random()', поэтому вы не можете просто делать '= random();', поэтому ваши 'fn.random.set' и' fn.random.add' не будут работать – Ian

+0

Is есть причина, по которой вы хотите, чтобы ваши методы были вложены так? Это не типичная идиома JavaScript из-за проблемы «этого». Почему бы не поставить их прямо на «Bidule.prototype»? Тогда вы можете просто сделать b.plus (4) '. –

+0

@ Lan: Я знаю, что 'random()' не существует, просто нужно просто привести пример. И хм ... Я знал, что настройка 'this' в переменной будет работать, но прототип не получит доступ к частной переменной' that'. @ Crazy Train: Я знаю, что это не типично, но я нашел и эту проблему, и способ реализации этого интересного. Я хотел получить ответы других и точки зрения. – Tot

ответ

3

Просто сохраните область действия, которую вы хотите использовать для переменной. Протестировано в JSFiddle.

var Bidule = function() { 
    var self = this; 
    this.value = 8 ; 

    this.calc = { 
     plus : function(x) { return self.value + x ; }, 
     minus : function(x) { return self.value - x ; }, 
     square : function() { return self.value * self.value ; } 
    } 

    this.fn = { 
     random : { 
      set : function() { return self.value = random() ; }, 
      add : function() { return self.value += random() ; } 
     } 
    } 
} ; 
+0

Да, конечно, это сработает. Однако ваш конструктор не использует прототип: 'var Bidule = function() {var self = this; ...}; Bidule.prototype = {}; '. Здесь прототип не получит доступ к 'var self'. Мы могли бы поставить прототип следующим образом: 'var Bidule = function() {var self = this; this.prototype = {...}; } ', но это означает, что прототип будет задаваться каждый раз при вызове конструктора, так что это тоже нехорошо. – Tot

+0

Единственный способ, которым я могу думать, - сделать fn функцией, возвращающей объект. См. Мой jsfiddle http://jsfiddle.net/txANC/ – Fallexe

+0

Да, действительно, это очень хороший способ. Однако мы должны улучшить его, как в вашем примере, 'fn()' будет создавать эти методы каждый раз, когда он вызывается. Но, возможно, есть способ экспортировать кластерные методы в 'fn' снаружи и возвращать только ссылку. – Tot

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