2014-12-20 5 views
8

Как вызвать публичную функцию из частной функции в шаблоне модуля JavaScript?Как вызвать публичную функцию из частной функции в шаблоне модуля JavaScript

Например, в следующем коде,

var myModule = (function() { 
    var private1 = function(){ 
     // How to call public1() here? 
     // this.public1() won't work 
    } 

    return { 
     public1: function(){ /* do something */} 
    } 
})(); 

Этот вопрос был задан twicebefore, с другим общепринятом ответом для каждого.

  1. Сохраните ссылку на объект возврата перед его возвратом, а затем используйте эту ссылку для доступа к общедоступному методу. См. answer.
  2. Сохраните ссылку на открытый метод в закрытии и используйте это для доступа к общедоступному методу. См. answer.

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

снеговика 1: Сохранить ссылку для возврата объект

var snowman1 = (function(){ 
    var _sayHello = function(){ 
    console.log("Hello, my name is " + public.name()); 
    }; 

    var public = { 
    name: function(){ return "Olaf"}, 
    greet: function(){ 
     _sayHello(); 
    } 
    }; 
    return public; 
})() 

Снеговик 2: Сохранить ссылку на публичную функцию

var snowman2 = (function(){ 
    var _sayHello = function(){ 
    console.log("Hello, my name is " + name()); 
    }; 
    var name = function(){ return "Olaf"}; 

    var public = { 
    name: name, 
    greet: function(){ 
     _sayHello(); 
    } 
    }; 
    return public; 
})() 

снеговика 3: литерал объекта

var snowman3 = { 
    name: function(){ return "Olaf"}, 
    greet: function(){ 
     console.log("Hello, my name is " + this.name()); 
    } 
} 

Мы можем видеть, что три идентичны по функциональности и имеют одинаковые общедоступные методы.

Если мы запустим тест простого переопределения, однако

var snowman = // snowman1, snowman2, or snowman3 
snowman.name = function(){ return "Frosty";} 
snowman.greet(); // Expecting "Hello, my name is Frosty" 
       // but snowman2 says "Hello, my name is Olaf" 

мы видим, что # 2 не удается.

Если мы запустим тест прототип переопределения,

var snowman = {}; 
snowman.__proto__ = // snowman1, snowman2, or snowman3 
snowman.name = function(){ return "Frosty";} 
snowman.greet(); // Expecting "Hello, my name is Frosty" 
       // but #1 and #2 both reply "Hello, my name is Olaf" 

мы видим, что и # 1 и # 2 терпят неудачу.

Это действительно уродливая ситуация. Просто потому, что я решил так или иначе реорганизовать свой код, пользователь возвращаемого объекта должен внимательно изучить, как я реализовал все, чтобы выяснить, может ли он переопределить методы моего объекта и ожидать его работы ! Хотя мнения здесь различны, мое собственное мнение состоит в том, что правильное поведение переопределения - это поведение простого литерала объекта.

Таким образом, это реальный вопрос:

Есть ли способ вызвать публичный метод из частной один, так что в результате объект действует как литерал объекта относительно переопределить поведение?

+2

Важное замечание в обсуждаемых ловушках: мое понимание шаблона модуля заключается в том, что оно действительно является альтернативой методу политомизма прототипа/ООП JS. Как таковой, я думаю, что для вас разумно избегать использования двух вместе - я не вижу в этом ни малейшей ошибки *. – EyasSH

+0

@ EyasSH - его можно использовать таким образом, однако я думаю, что это скорее артефакт, который можно использовать для своего рода наследования, а не для преднамеренной функции. Он, по-видимому, используется в первую очередь для подражания частным членам (или не подвергая действию вещи, с которыми другие не должны играть), а также для повышения производительности. – RobG

+1

@RobG, но вы можете иметь частные функции, объявленные в закрытии, придерживаясь шаблона прототипа. В этом случае использование '' 'this''' в порядке. Я хочу сказать, что если вы используете шаблон модуля, я думаю, что при взгляде на модуль <-> странность взаимодействия прототипа не является реальной причиной не использовать подход. Это может быть поводом для того, чтобы придерживаться прототипов вообще, если вы думаете, что тот, кто использует ваш код, сделает это. – EyasSH

ответ

2

Вы можете использовать this, чтобы получить объект, на который был вызван ваш привилегированный метод: greet.

Затем вы можете передать это значение вашему личным методам _sayHello, например. используя call, apply, или в качестве аргумента:

var snowman4 = (function() { 
    var _sayHello = function() { 
     console.log("Hello, my name is " + this.name); 
    }; 
    return { 
     name: "Olaf", 
     greet: function() { 
      _sayHello.call(this); 
     } 
    }; 
})(); 

Теперь вы можете сделать

var snowman = Object.create(snowman4); 
snowman.greet(); // "Hello, my name is Olaf" 
snowman.name = "Frosty"; 
snowman.greet(); // "Hello, my name is Frosty" 

А также

snowman4.greet(); // "Hello, my name is Olaf" 
snowman4.name = "Frosty"; 
snowman4.greet(); // "Hello, my name is Frosty" 
+0

Этот ответ, похоже, не затрагивает вопрос о том, как вызвать публичные функции из частных функций в шаблоне модуля - в частности, как следует имя_sayHello() вызывать имя(). Обтекание литерала объекта в IIFE не делает приветствие() привилегированным. –

+0

@ I-LinKuo Да, это привилегия. Он может получить доступ к конфиденциальным данным, объявленным внутри функции самоисполнения. – Oriol

+0

Да, было привилегировано: он мог получить доступ к конфиденциальным данным, объявленным внутри функции самоисполнения. Я сделал это с привилегированным методом вместо частного, чтобы упростить, но это правда, что он не ответил на вопрос напрямую, поэтому я его исправил. – Oriol

1

С модулем шаблона, скрыть все innates объекта в локальные переменные/функции и обычно используют функции в ваших публичных функциях. Каждый раз, когда создается новый объект с шаблоном модуля, создается новый набор открытых функций - со своим собственным облачным состоянием.

С образцом прототипа у вас есть тот же набор методов, доступных для всех объектов определенного типа. Какими изменениями для этих методов является объект this - другими словами, это их состояние. Но this никогда не скрывается.

Излишне говорить, что их сложно смешивать. Одним из возможных способов является извлечение методов, используемых частными лицами, в прототип полученного объекта модуля с помощью Object.create. Например:

var guardian = function() { 
    var proto = { 
     greet: function() { 
      console.log('I am ' + this.name()); 
     }, 
     name: function() { 
      return 'Groot'; 
     } 
    }; 
    var public = Object.create(proto); 
    public.argue = function() { 
     privateGreeting(); 
    }; 

    var privateGreeting = public.greet.bind(public); 
    return public; 
}; 

var guardian1 = guardian(); 
guardian1.argue(); // I am Groot 
var guardian2 = guardian(); 
guardian2.name = function() { 
    return 'Rocket'; 
}; 
guardian2.argue(); // I am Rocket 
var guardian3 = guardian(); 
guardian3.__proto__.name = function() { 
    return 'Star-Lord'; 
}; 
guardian3.argue(); // I am Star-Lord