Javascript "классы" прототипа против объявления внутренней функции vs и т.д.

Я знаю, что на это был дан ответ раньше, но я все еще запутался (что не совсем я виноват, поскольку я замечаю, что ответы могут радикально отличаться друг от друга).

Я исхожу из фона Java, поэтому, если вы можете определить что-либо как статическое, частное, общедоступное и т.д., то это должно помочь мне понять.

В принципе, я хочу создать совершенно собственный класс, но я не уверен в прототипе /etc. Пример (с использованием одного типа функций):

function myClass()
{
    var a;
    var b;

    var helper = function()
    {
        this.a += this.b;
    }

    var helper2 = function(a,b)
    {
        return(a + b);
    }

    var getA = function()
    {
        return(this.a);
    {

    var staticMethodThatNeedsToBePublic = function()
    {}
}

var myInstance = new myClass();

myClass.prototype.example1 = function(){};
myClass.example2 = function(){};

Итак, как это должно было быть написано? (Я попытался включить все основные типы функций, но если я пропустил любой, не стесняйтесь добавлять) (примечание: я не особо забочусь об этом конкретном примере, я просто подумал, что это может быть полезно для разговора, но не стесняйтесь просто ответьте на мой общий вопрос]

Ответ 1

Summery:

function MyClass(){
    //You can access everything from in here (and from all sub functions) including prototypes and statics that are defined afterwards.
    var privateVariable = "PriTest"; //Pair cannot by seen outside of MyClass
    function privateFunction(){
    }

    this.publicVariable = "pubTest"; //Pair can be seen by everything except static functions
    function publiFunction(){
    }this.publiFunction = publiFunction;

    this.prototypeFunction();      //This group could of been called like this from anywhere in this object
    alert(this.prototypeVariable);
    MyClass.staticFunction();
    alert(MyClass.staticVariable);
}

MyClass.prototype.prototypeFunction = function(){
    //Can access other prototypes, statics, and public functions and variables
    this.publicFunction();
    this.prototypeVariable;
    MyClass.staticFunction();
}
MyClass.prototype.prototypeVariable = "proTest"

MyClass.staticFunction = function(){
    //Can access other statics only
    alert(MyClass.staticVariable);
}
MyClass.staticVariable = "staTest"

Скажите, пожалуйста, если у меня что-то не так в следующем.

Частный (доступный внутри себя): [То же, что и java] var variableName || function functionName внутри объекта. Доступ к ним могут получить только другие частные или привилегированные функции.

Public и Privileged (доступный извне {все еще может работать со всеми внутренними объектами}): [То же, что и java public] this.variableName || this.functionName = function(){ ... } внутри объекта.

Прототип (доступ к другим прототипам): [почти вне класса и доступен только для общедоступных объектов] Class.prototype.variableName || Class.prototype.functionName Функции, объявленные таким образом, будут иметь доступ к любым общедоступным или прототипным переменным. Попытки изменить переменную, созданную таким образом, вместо этого создадут новую общедоступную переменную для объекта, а переменная прототипа будет недоступна.

Static: [То же, что и java?] Class.variableName || Class.functionName Может быть изменен любой функцией или методом.

function MyClass(){
    //public, privileged, and private
    //Everything in here can see each other
    //Everything in here can see everything outside
}
//Prototype and Static
//Prototype, Basically a template that is used on an instance of a class (And therefore does not have access to any of the non public fields)

Пример прототипа:

MyClass.prototype.proExample = function(){
    this.doSomething;
}
//Is basically equivalent to
function proExample(instanceOfMyClass){
    instanceOfMyClass.doSoemthing;
}

Добавьте к этому после нескольких тестов.

Ответ 2

Короткий ответ на ваш вопрос: используйте прототипы. Всегда используйте прототипы.

Основное отличие состоит в том, что если вы присоединяете функцию с помощью this.foo = function(){}, функция получает повторное объявление для каждого экземпляра класса. Прикрепление с помощью func.prototype.foo = function(){} означает, что функция только когда-либо объявляется один раз, а свойство this изменяется при вызове в экземпляр класса, к которому он должен ссылаться.

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

function myClass(){
    // constructor
}

myClass.prototype   = new superClass();
myClass.constructor = myClass;
myClass.prototype = {
    helper  : function(){},
    helper2 : function(){}
};

var myInstance = new myClass();

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

http://www.htmlgoodies.com/primers/jsp/article.php/3600451/Javascript-Basics-Part-8.htm

function Cat(name, color){
    /*
    Constructor: any code in here is run when the object is created
    */
    Cat.cats++;

    /*
    Private variables and functions - may only be accessed by private or privileged functions.

    Note that 'name' and 'color', passed into the Class, are already private variables.
    */
    var age  = 0;
    var legs = 4;
    function growOlder(){
        age++;
    }

    /*
    Public variables - may be accessed publicly or privately
    */
    this.weight = 1;
    this.length = 5;

    /*
    Privileged functions - may be accessed publicly or privately
    May access Private variables.

    Can NOT be changed, only replaced with public versions
    */
    this.age = function(){
        if(age==0) this.length+=20;

        growOlder();
        this.weight++;
    }
}

/*
Prototyped Functions - may be accessed publicly
*/
Cat.prototype = {
    talk:     function(){ alert('Meow!'); },
    callOver: function(){ alert(this.name+' ignores you'); },
    pet:      function(){ alert('Pet!'); }
}

/*
Prototyped Variables - may be accessed publicly.
May not be overridden, only replaced with a public version
*/
Cat.prototype.species = 'Cat';

/*
Static variables and functions - may be accessed publicly
*/
Cat.cats = 0;

Ответ 3

Это смущает много людей, потому что Javascript использует совсем другое понятие наследования и класса. В Javascript все, включая классы, является просто объектом. Все методы и такие, которые составляют часть класса, содержатся в объекте с именем prototype. Частью этого является функция, называемая init. Когда вы выполняете new Foo(), вы создаете новый объект, а некоторый метод init копирует соответствующее содержимое, включая прототип, на этот новый объект.

Итак, когда вы добавляете функцию в prototype через

 myClass.prototype.example1 = function(){};

вы фактически добавляете новый метод в класс, который затем будет унаследован в любом экземпляре myClass, который вы создаете. Во втором случае

 myClass.example2 = function(){};

вы просто добавляете новый метод к исходному объекту myClass. В терминах Java, которые в основном меняют это как новый объект типа, который действует как myClass, за исключением того, что теперь он имеет метод example2().

Обновление

Еще один ответ - примечание Крокфорда. Я бы рекомендовал прочитать Прототипное наследование.

Другое обновление

Хорошо, вернемся к секунде. Во-первых, какой объект? В принципе, это структура, которая сочетает в себе состояние и поведение.

Мы имеем идею Number, у которого есть метод add, и у нас есть вид Number, называемый Integer, который "ведет себя как" число, но ограничен значениями в определенном диапазоне. На языке, таком как Java, вы можете иметь

abstract class Number {
    public void addTo(Number arg);
}

а затем

class Integer extends Number {
    int val;
    public void addTo(Integer arg){ val += arg; }
}

(И не беспокойтесь о деталях Java, я пытаюсь сделать точку.)

То, что вы сказали здесь, состоит в том, что существует потенциально много объектов, которые являются числами, и все они имеют поведение, называемое addTo. Математически набор вещей, которые идентифицируются общим свойством, иногда называют "классом эквивалентности" и как мы получаем имя "класс".

Мы также идентифицировали специальный тип Number, называемый Integer, который имеет ограниченный диапазон значений между -32767 и 32768. (Quiz: почему эти значения?) Тем не менее, он действует во всех отношениях подобно Number: вы можете addTo целые числа. Это утверждение "действует во всех отношениях подобно - но с этими ограничениями" обычно сокращается до "есть", и это то, что мы подразумеваем под "наследованием".

Итак, теперь вы пишете

Integer a, b;
// they get initial values somehow, left as an exercise
a.addTo(b);

и компилятор вычисляет для нахождения объекта a, находит свое конкретное поведение addTo и знает, как подключить все, чтобы заставить его работать. Иногда - как в ранних версиях С++ - это нужно было делать во время компиляции; в более позднем С++ и на Java, также есть способ сделать соединение во время выполнения ( "позднее связывание" ), которое в основном сводится к тому, что таблица имеет место где-то, где говорится

Если у меня есть целое число, и мне нужен метод addTo, вот его адрес; используйте это.

Javascript и некоторые предыдущие языки, начинающиеся с Self, делают это немного по-другому. Теперь объект - это просто структура, которая содержит... материал. Некоторые из этих материалов могут быть данными, а некоторые могут быть функциями. Идею "класса" можно полностью отвлечь; "класс" - это всего лишь совокупность всех объектов, имеющих то же самое содержимое. Таким образом, в Javascript мы могли бы сделать наш класс "Integer" таким, как

 var Integer = {  // we're defining an object as a literal
     int val,
     addTo : function(b){ val += b ; }
 }

(Опять же, не волнуйтесь, если это действительно идеальный javascript, нужно пояснить концепцию.)

Если мы скопируем этот объект с именем Integer, скажем, Integer2, то оба содержат val и функцию с именем addTo. Мы можем сказать, что они оба "одного класса", потому что они имеют точно такое же состояние и методы.

Вопрос в том, как мы можем реализовать это копирование? Вы можете просто пропустить все это пополам, но у нас есть другие проблемы, поэтому мы определили специальный объект в содержимом каждого объекта javascript с именем prototype, и мы поместили все методы и прочее - все, что мы хотели бы хотите скопировать для создания другого объекта в том же классе - в этом. Теперь у нас будет что-то вроде

  var Integer = {
      prototype : {
        int val,
        addTo : function(b){ val += b; }
      }
  }

Добавим к языку оператор new, который в основном просто копирует объект-прототип. Когда мы пишем

  var a = new Integer();

a теперь является объектом, который имеет тот же прототип, что и все остальные объекты Integer. Когда мы пишем

  a.addTo(b);

все, что должен сделать интерпретатор, это посмотреть в объекте prototype, содержащемся в a, и найти его метод с именем addTo.

Зачем? Поскольку теперь вся сложность компиляции в классах, добавление синтаксиса и определение времени привязки во время компиляции или времени выполнения, а также управление таблицами времени выполнения и т.д. Превращаются в две простые операции:

  • знать, как сделать глубокую копию prototype
  • как искать что-то по имени в prototype.

Этот второй подход - "наследование прототипов".