• Что бы вступить в ряды "Принятый кодер" Вам нужно:
    Написать 10 полезных сообщений или тем и Получить 10 симпатий.
    Для того кто не хочет терять время,может пожертвовать средства для поддержки сервеса, и вступить в ряды VIP на месяц, дополнительная информация в лс.

  • Пользаватели которые будут спамить, уходят в бан без предупреждения. Спам сообщения определяется администрацией и модератором.

  • Гость, Что бы Вы хотели увидеть на нашем Форуме? Изложить свои идеи и пожелания по улучшению форума Вы можете поделиться с нами здесь. ----> Перейдите сюда
  • Все пользователи не прошедшие проверку электронной почты будут заблокированы. Все вопросы с разблокировкой обращайтесь по адресу электронной почте : info@guardianelinks.com . Не пришло сообщение о проверке или о сбросе также сообщите нам.

Factory vs Constructor Functions: When Prototypes Don't Behave as Expected

Lomanu4 Оффлайн

Lomanu4

Команда форума
Администратор
Регистрация
1 Мар 2015
Сообщения
1,481
Баллы
155
Have you ever found yourself scratching your head while debugging JavaScript prototype behavior? I recently encountered a fascinating quirk that highlights an important distinction between factory functions and constructor functions. Let's explore why the following code behaves the way it does:


function purr() {
console.log(`${this.name} is purring`)
}

// FACTORY FUNCTION
function createCat(name, breed) {
return {
name,
breed,
greet() {
console.log(`Hello, my name is ${this.name}`)
}
};
}
createCat.prototype.purr = purr
const oreo = createCat("Oreo", "Persian")
oreo.greet() // Hello, my name is Oreo
// oreo.purr() // TypeError: oreo.purr is not a function

//CONSTRUCTOR FUNCTION
function Cat(name, breed){
this.name = name
this.breed = breed
this.greet = function(){
console.log(`Hello, my name is ${this.name}`)
}
}
Cat.prototype.purr = purr
const frosty = new Cat("Frosty", "Tabby")
frosty.greet() // Hello, my name is Frosty
frosty.purr() // Frosty is purring
The Mystery: Why Can't Oreo Purr?


Oreo and Frosty are both cats who can greet us, but only Frosty can purr! When we try to make Oreo purr, JavaScript throws a TypeError. What's going on here?

Understanding the Root Cause


The difference comes down to how prototypal inheritance works for objects created using different patterns:

Factory Functions: Plain Objects with No Prototype Connection


When we use a factory function like createCat(), it simply returns a plain object literal. This object's prototype is linked to Object.prototype, not to createCat.prototype.

In our example:

  1. We added purr() to createCat.prototype
  2. But oreo doesn't inherit from createCat.prototype at all!
  3. The prototype chain for oreo looks like: oreo → Object.prototype → null
Constructor Functions: The Magic of new


When we use the new keyword with a constructor function like Cat(), something special happens:

  1. A new object is created
  2. This object's prototype is set to Cat.prototype
  3. The constructor function is executed with this bound to the new object
  4. The new object is returned (unless the constructor returns another object)

So for frosty:

  1. We added purr() to Cat.prototype
  2. frosty inherits from Cat.prototype
  3. The prototype chain looks like: frosty → Cat.prototype → Object.prototype → null
How to Fix Oreo's Inability to Purr


If we want objects created by factory functions to share methods, we have a few options:

Option 1: Include the method directly in each object


function createCat(name, breed) {
return {
name,
breed,
greet() {
console.log(`Hello, my name is ${this.name}`)
},
purr // Add purr method to each object
};
}
Option 2: Use Object.create for proper prototype inheritance


const catMethods = {
greet() {
console.log(`Hello, my name is ${this.name}`)
},
purr() {
console.log(`${this.name} is purring`)
}
};

function createCat(name, breed) {
const cat = Object.create(catMethods);
cat.name = name;
cat.breed = breed;
return cat;
}
Option 3: Use classes (which are syntactic sugar for constructor functions)


class Cat {
constructor(name, breed) {
this.name = name;
this.breed = breed;
}

greet() {
console.log(`Hello, my name is ${this.name}`)
}

purr() {
console.log(`${this.name} is purring`)
}
}
Memory Efficiency Considerations


One advantage of the prototype approach is memory efficiency. When we add methods to prototypes rather than directly to each object:

  • With constructor functions, methods like purr() exist once in memory (on the prototype)
  • With our original factory function, greet() is recreated for each object, wasting memory
The Big Takeaways

  1. Factory functions create objects from scratch with no inherent prototype linking
  2. Constructor functions with new automatically link the created object to the constructor's prototype
  3. If you're using factory functions and want shared methods, you need to:
    • Include them in each object (memory inefficient)
    • Use Object.create() to establish proper inheritance
    • Or switch to classes/constructors
Which Should You Use?


Both patterns have their place in modern JavaScript:

  • Factory functions are great for creating objects with private data using closures
  • Constructor functions and classes provide a cleaner inheritance model for shared behavior

The key is understanding how JavaScript's prototype system works under the hood, so you don't get caught by surprising behaviors like our poor Oreo who couldn't purr!

What's your preferred way of creating objects in JavaScript? Have you encountered other prototype quirks? Share in the comments below!

Follow me for more JavaScript insights and explanations of those weird behaviors that make you go "wait, what?!"


Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

 
Вверх Снизу