2013-07-02 4 views
0

читает What techniques can be used to define a class in JavaScript, and what are their trade-offs? на StackOverflow я понимаю, что я могу определить класс с помощьюдобавления прототипа в определении класса в JavaScript

Способ 1:

function Person(name, gender){ 
    this.name = name; 
    this.gender = gender; 
} 

и добавлять функции в прототипе, чтобы избежать члена функции воссозданы каждый раз при его создании. как

Person.prototype.speak = function(){ 
    alert("my name is" + this.name); 
} 

и создавать свои экземпляры через

var person = new Person("Bob", "M"); 

Я думаю, что создание того же объекта можно с вне новое ключевое слово, как

Способ 2:

var Person = function (name, gender) { 

    return {name:name, gender:gender}; 

} 

person = Person("Bob", "M"); 

Второй метод делает то же самое по первому? Также, если да, то как бы я объединил функции с помощью прототипа (как мы видим в разговоре 1-го уровня) во втором подходе?

ответ

3

Как объяснил Шон Виейра, первый метод не совпадает со вторым методом. В первом методе экземпляр наследует от Person.prototype. Во втором методе экземпляр наследует непосредственно от Object.prototype.

Метод 1:

 null 
     ^
     | 
     | __proto__ 
     | 
+------------------+ 
| Object.prototype | 
+------------------+ 
     ^
     | 
     | __proto__ 
     | 
+------------------+ 
| Person.prototype | 
+------------------+ 
     ^
     | 
     | __proto__ 
     | 
+------------------+ 
|  person  | 
+------------------+ 

Метод 2:

 null 
     ^
     | 
     | __proto__ 
     | 
+------------------+ 
| Object.prototype | 
+------------------+ 
     ^
     | 
     | __proto__ 
     | 
+------------------+ 
|  person  | 
+------------------+ 

Интересно, что выше диаграммы показывают, что объекты наследуют от других объектов в JavaScript, а не от конструкторов. Следовательно, при создании new Person экземпляр наследуется от Person.prototype, а не от Person.

Как это релевантная информация? Для начала он демонстрирует, что вам не нужно создавать конструктор для создания экземпляров объекта. Вместо этого вы непосредственно создать прототип объекта следующим образом:

var person = { 
    create: function (name, gender) { 
     var person = Object.create(this); 
     person.gender = gender; 
     person.name = name; 
     return person; 
    }, 
    speak: function() { 
     alert("My name is " + this.name + "."); 
    } 
}; 

В приведенном выше примере person эквивалентно Person.prototype. Теперь вы можете создать экземпляр person следующим образом:

var bob = person.create("Bob", "M"); 

Прототип цепи bob будет выглядеть следующим образом:

 null 
     ^
     | 
     | __proto__ 
     | 
+------------------+ 
| Object.prototype | 
+------------------+ 
     ^
     | 
     | __proto__ 
     | 
+------------------+ 
|  person  | 
+------------------+ 
     ^
     | 
     | __proto__ 
     | 
+------------------+ 
|  bob  | 
+------------------+ 

Так почему вы должны создавать объекты, как это вместо этого?

  1. Он выглядит чище. Все инкапсулируется в одном объектном литерале.
  2. Легче понять, что объекты наследуются от объектов. Никаких конструкторов не требуется.
  3. Чтобы создать экземпляр, вам не нужно использовать new. Это solves a lot от problems.

Для получения дополнительной информации об этом шаблоне прочитайте мой пост в блоге на "Why Prototypal Inheritance Matters".

+0

Интересный блог, мне интересно, если вы могли бы объяснить 3 причины, по которым прототипные шаблоны лучше, чем функции конструктора: 1: технически возможно, но вам не нравится синтаксис (проясняется в комментариях). 2.То же, что и 1, не нравится синтаксис и подвержен ошибкам. 3. Не нравится синтаксис, слишком запутанный (такой же, как 1 и 2). То, что мне не хватает, - это техническая основа, которая лучше расширяется или будет работать лучше. В какой-то момент я согласен с тем, что функции конструктора могут быть подвержены ошибкам, но даже до использования компилятора замыкания я почти никогда не злоупотребляю ими (забыв о новых) – HMR

+0

@HMR Я уже ответил на преимущества прототипного наследования по классическому в следующем ответе: http: // stackoverflow .com/а/16872315/783743. Тем не менее я должен признать, что оба эти шаблона эквивалентны по силе, и выбор между одним из них - это только вопрос предпочтения. Однако вы должны заметить, что 'new' намного быстрее, чем' Object.create'. Следовательно, если вы хотите производительность, тогда придерживайтесь 'new'. Я создал небольшую, быструю библиотеку, которая упрощает работу с конструкторами и прототипами (особенно для тех, кто этого не понимает): https://github.com/javascript/augment –

+0

Спасибо за ваш ответ, может быть, это является интересным для вас. Майкл Болин работал над компилятором закрытия и защищает конструкторскую функцию над функциональным шаблоном. Он старый и не упоминает Object.create (прототипный шаблон?). Http://bolinfest.com/javascript/inheritance.php В какой-то момент JavaScript будут иметь классы (Ecma 6) – HMR

0

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

function Person(name, gender){ 
    return { 
    name: name, 
    gender: gender, 
    speak: function(){ 
     alert("my name is" + this.name); 
    } 
    }; 
} 

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

function Person(name, gender){ 
    return { 
    speak: function(){ 
     alert("my name is" + name); 
    } 
    }; 
} 
+2

они не эквивалентны - в методе 2, 'person instanceof Person === false' – Alnitak

+0

Я верю, что вы предлагаете, похоже на' this.speak = function() {..} 'не в протипе .. – mithunsatheesh

+0

@Alnitak Fair Point. Я предполагаю «эквивалент»! == «делать то же самое», в зависимости от того, что вам нужно. – Tim

5

Нет, Метод 2 = 1. Способ второй метод создает новый анонимный объект! с прототипом, который указывает на Object.prototype, а первый создает новый объект с прототипом, который указывает на Person.prototype.

В псевдо-коде:

// Method #1 
function Person(name, gender) { 
    // The magic JS does *for us* (but *only* when invoked with `new`) 
    var this = {}; 
    // __proto__ is the *internal* prototype reference 
    // It's not required to be accessible unless you're in an ES6 environment. 
    this.__proto__ = Person.prototype; 

    // Person.prototype is also created by JS for us 
    // and provided with a reference (among other things) 
    // to this function as prototype.constructor. 

    // Our stuff 
    this.name = name; 
    this.gender = gender; 

    // More JS magic 
    return this; 
} 

// Method #2 
function Person(name, gender) { 
    // Just our stuff - no magic JS goodness here 
    return { 
     name: name, gender: gender 
    }; 
} 
+0

«+1» удивительный .. это помогло .. :) – mithunsatheesh

+2

JS не делает 'var this = {}; this.prototype = Person.prototype; this.constructor = Person; '. Это совершенно неправильно. Вместо этого он выполняет команду var this = Object.create (Person.prototype); '. Когда вы исправите свою ошибку, я сниму нижний план. Также прототип второго объекта (буквальный объект) указывает на «Object.prototype», а не «Object». –

+0

@AaditMShah - хорошие моменты, оба :-) Я обновил свой ответ, чтобы исправить ошибку и дать понять, что это свойство * internal * prototype, которое устанавливается (не обязательно доступное свойство с именем 'prototype'). Благодаря! –

0

Возможно, этот ответ может объяснить немного больше о конструкторах функций, наследование, частные переменные и преобладающих методов: Prototypical inheritance - writing up

Другие ответы уже на Ваш вопрос; создание объекта как литерала объекта {prop:val,method:function(){}} и создание объекта с использованием функций-конструкторов: var MyObject=function(){this.prop=val};MyObject.prototype.method=function(){};

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