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
.
Этот второй подход - "наследование прототипов".
Ответ 4
Я думаю, что есть 2 статьи, которые могут дать вам больше ясности. Они мне очень помогли: