Когда использовать слово "prototype" при добавлении новых свойств к объекту в javascript?

Я не понимаю в JavaScript, когда использовать слово "prototype" против использования простой "точечной" нотации без слова "prototype". Может кто-нибудь посмотреть на эти блоки кода и помочь мне понять, когда вы хотите использовать один над другим?

с "prototype":

function employee(name,jobtitle)
{
  this.name=name;
  this.jobtitle=jobtitle;
}

var fred=new employee("Fred Flintstone","Caveman");
employee.prototype.salary=null;
fred.salary=20000;
console.log(fred.salary);

без "prototype":

function employee(name,jobtitle,salary)
{
  this.name=name;
  this.jobtitle=jobtitle;
  this.salary=salary;
}

var fred=new employee("Fred Flintstone","Caveman", 20000);
console.log(fred.salary);

Ответ 1

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

function Employee(name){
  this.name = name;
}

Employee.prototype.company = "IBM";

Employee.prototype.who = function(){
  console.log("My name is", this.name, "I work for", this.company);
}

var bob = new Employee('Bob');
var jim = new Employee('Jim');

// bob and jim are seperate objects, but each is linked to the same 'prototype' object.

jim.who(); // jim doesn't have a property called 'who', so it falls back to it 'prototype', where who exists
// My name is Jim I work for IBM

bob.who();
// My name is Bob I work for IBM

// Bob leaves IBM for Microsoft
bob.company = "Microsoft"; // bob now has a property called 'company'. The value of which is 'Microsoft', which overrides bob prototype property of the same name.

bob.who();
// My name is Bob I work for Microsoft

Employee.prototype.company = 'Facebook';

jim.who(); 
// My name is Jim I work for Facebook

bob.who(); // Bob is not affected by the change.
// My name is Bob I work for Microsoft

delete bob.company;

bob.who(); // bob no longer has it own property 'company', so like jim, it drops down to the prototype object.
// My name is Bob I work for Facebook

Ответ 2

Вопросы вокруг JS и наследования могут быть сложными, но ответ на ваш вопрос относительно прост. Рассмотрим этот код:

 function Klass() { }
 var obj1 = new Klass();
 var obj2 = new Klass();

Теперь, если вы добавите свойство в obj1, это свойство существует только на obj1. Аналогично obj2.

Если вы добавите свойство в Klass, это свойство также существует только на Klass (объект функции). Он не влияет на obj1 и obj2 вообще.

Но если вы добавите свойство в класс Klass.prototype, это свойство будет присутствовать как на obj1, так и на obj2, а также на любых будущих объектах, созданных с помощью "нового класса". Если вы затем измените значение свойства на прототипе, измененное значение будет тем, что вы видите на всех этих объектах.

Вы можете добавить код внутри тела функции Klass, чтобы добавить свойства к this; что приведет к тому, что любые будущие объекты Klass получат эти свойства. Но у каждого объекта будет своя копия, которая может складываться с учетом памяти, особенно когда свойства являются методами - и эти копии не будут затронуты будущими изменениями в теле Klass.

Ответ 3

ES5 Object.create почти устраняет необходимость в более удобной работе с .prototype.

Итак, чтобы подобрать пример @Gerry, вы можете пойти как

var Mammal = {
    walk: function() {}
};

var Dog = Object.create(Mammal, {
    bark: {
        value: function() {}
    }
}); // create a new object which [[prototype]] refers to Mammal

Dog.walk();
Dog.bark();

Ответ 4

Объект prototype немного сложнее понять; однако эта статья о JavaScript OOP может помочь пролить некоторый свет.

В двух словах объект prototype предоставляет схему для объекта "получателя" - все, что вам нужно сделать, - указать свойство получателя prototype на ваш объект чертежа. Обратите внимание, что у вас может быть столько же получателей прототипа, как вам нравится (так что автомобиль и поезд могут указывать на общий объект прототипа транспортного средства).

Вы можете определять как свойства, так и функции в объекте прототипа, который будут использовать любые объекты-получатели, например:

var vehiclePrototype = {
    // A property which will be supplied to the recipient
    cost: 0,

    // A method which will be supplied the recipient
    move: function () { 
        // Your prototype can refer to 'this' still.
        console.log("Moving " + this.name);
    };
}

Теперь вы можете создать Car, который использует vechiclePrototype:

// Factory method for creating new car instances.
function createCar(name) {
    // Define the Car constructor function
    function Car(name) {
        this.name = name;
    }

    // Point the car prototype at the vechiclePrototype object
    Car.prototype = vechiclePrototype;

    // Return a new Car instance 
    return new Car(name);
}

// Create a car instance and make use of the Prototype methods and properties
var mustang = createCar(mustang);
mustang.cost = 5000;
mustang.move();

Новый объект Train может быть создан аналогичным образом:

function createTrain(whilstleSound) {
    // Define the Train constructor function
    function Train(name) {
        this.whilstleSound = whilstleSound;
    }

    // Point the train prototype at the vechiclePrototype object
    Train.prototype = vechiclePrototype;

    // Return a new Train instance 
    return new Train(name);
}

var ic125 = new Train("pooop pooop");
ic125.move();

Одним из больших преимуществ использования Prototypical inheritance все экземпляры как Car, так и Train совместно используют ту же самую функцию move (вместо создания нескольких экземпляров одной и той же функции), что приводит к значительному сохранению памяти, если существует много экземпляров этих объектов.

Ответ 5

Игнорировать new, игнорировать .prototype, они просто путают понятия. Если вы действительно хотите, чтобы прототипное наследование использовало Object.create, но большая часть времени наследования полностью переборщила. (прототипное наследование следует использовать только как метод оптимизации).

При создании классов просто создавайте объекты и расширяйте их.

var Walker = {
    walk: function() {}
}

var Eater = {
    eat: function () {}
}

var Dog = extend({}, Eater, Walker, {
    bark: function () {},
    sniffBehind: function () {}
})

function dog(dogName) {
    return extend({}, Dog, {
        name: dogName
    })
}

var steveTheDog = dog("steve")
console.log(steveTheDog.name === "steve")

Используйте любую произвольную функцию расширения arity, которую вы хотите, _.extend, jQuery.extend, pd.extend и т.д.

pd реализует extend следующим образом

function extend(target) {
    [].slice.call(arguments, 1).forEach(function(source) {
        Object.getOwnPropertyNames(source).forEach(function (name) {
            target[name] = source[name]
        })
    })
    return target
}

Ответ 6

С прототипом вы можете расшириться "чище", потому что вы отделяете логику внутри своей конструкторской функции от свойств и методов, которые определяют ваш объект.

var Mammal = function() { ... };
Mammal.prototype = {
  walk: function() { ... }
};

var Dog = function() { ... };

for (var prop in Mammal.prototype) {
  Dog.prototype[prop] = Mammal.prototype[prop];
}

Dog.prototype.bark = function() { ... };

Однако вышеописанный без прототипа может выглядеть так:

var Mammal = function() {
  this.walk = function() { ... };
};

var Dog = function() {
  Mammal.apply(this);
  this.bark = function() { ... };
};

Этот способ расширения объектов, однако, не имеет отношения к современным библиотекам JavaScript, таким как Underscore.js, или может быть написан гораздо более чистым с помощью, например, jQuery.