«JavaScript: хорошие детали» - способ реализации прототипа? - PullRequest
1 голос
/ 06 апреля 2020

Прочитав эту статью https://www.toptal.com/javascript/es6-class-chaos-keeps-js-developer-up и впоследствии "JavaScript: The Good Parts", я буду впредь стремиться стать лучшим JavaScript разработчиком. Однако один вопрос остался для меня. Я обычно реализовывал методы, подобные этому:

function MyClass(){
    this.myData = 43;
    this.getDataFromObject = function(){
        return this.myData;
    }
}

MyClass.prototype.getDataFromPrototype = function(){
    return this.myData;
}

var myObject = new MyClass();
console.log(myObject.getDataFromObject());
console.log(myObject.getDataFromPrototype());

Мое предположение, которое лежит в основе всего этого поста, заключается в том, что getDataFromObject быстрее (во время вызова, а не во время создания объекта), потому что он сохраняет косвенное отношение к прототипу, но также меньше эффективное использование памяти, потому что каждый объект получает собственный экземпляр объекта функции. Если это уже не так, пожалуйста, исправьте меня, и вы, вероятно, можете прекратить читать здесь.

Иначе: и в статье, и в книге рекомендуется такой стиль:

function secretFactory() {
    const secret = "Favor composition over inheritance [...]!"
    const spillTheBeans = () => console.log(secret)

    return {
        spillTheBeans
    }
}

const leaker = secretFactory()
leaker.spillTheBeans()

(цитата из статьи, в книге еще не было ES6, но идеи похожи)

Моя проблема такова:

const leaker1 = secretFactory()
const leaker2 = secretFactory()

console.log(leaker1.spillTheBeans === leaker2.spillTheBeans) // false

Не хочу ли я в основном избегать того, чтобы каждый объект получал собственный экземпляр каждого метод? Это может быть незначительным, но если spillTheBeans более сложный, и я создаю базилион объектов, каждый из которых имеет двенадцать тысяч других методов?

Если так, что такое решение «части goot»? Мое предположение будет таким:

const spillStaticBeans = () => console.log("Tabs rule!")
const spillInstanceBeans = (beans) => console.log(beans)

function secretFactory() {
    const secret = "Favor composition over inheritance [...]!"

    return{
        spillStaticBeans,
        spillInstanceBeans: () => spillInstanceBeans(secret)
    }
}

const leaker1 = secretFactory()
const leaker2 = secretFactory()

leaker1.spillStaticBeans()
leaker2.spillInstanceBeans()

console.log(leaker1.spillStaticBeans === leaker2.spillStaticBeans) // true
console.log(leaker1.spillInstanceBeans === leaker2.spillInstanceBeans) // false

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

Но теперь я должен написать каждое имя метода два-три раза. Хуже того, я загромождаю пространство имен функциями publi c spillStaticBeans и spillInstanceBeans. Чтобы смягчить последнее, я мог бы написать модуль мета-фабрики:

const secretFactory = (function(){

    const spillStaticBeans = () => console.log("Tabs rule!")
    const spillInstanceBeans = (beans) => console.log(beans)

    return function() {
        const secret = "Favor composition over inheritance [...]!"

        return{
            spillStaticBeans,
            spillInstanceBeans: () => spillInstanceBeans(secret)
        }
    }

}())

Это можно использовать так же, как и раньше, но теперь методы скрыты в замыкании. Однако, это становится немного запутанным. Используя модули ES6, я также мог бы оставить их в области видимости модуля и просто не экспортировать их. Но так ли это go?

Или я вообще ошибаюсь, и внутреннее представление функции JavaScript позаботится обо всем этом, и на самом деле проблемы нет?

Ответы [ 2 ]

3 голосов
/ 06 апреля 2020

Мое предположение, которое лежит в основе всего этого поста, заключается в том, что getDataFromObject вызывается быстрее, чем getDataFromPrototype, потому что это сохраняет косвенное отношение к прототипу

Нет. Двигатели очень хороши в оптимизации прототипа косвенности. instance.getDataFromPrototype всегда разрешает один и тот же метод для экземпляров одного и того же класса, и движки могут воспользоваться этим. Подробности смотрите в этой статье .

Разве я не хочу избегать того, чтобы каждый объект получал собственный экземпляр каждого метода? Это может быть незначительным здесь

Да. В большинстве случаев это является незначительным. Поэтому пишите свои объекты методами, используя любой стиль, который вы предпочитаете. Только если вы фактически измерите узкое место в производительности, пересмотрите случаи, когда вы создаете много экземпляров.

Используя модули ES6, я мог бы также оставить их в области действия модуля и просто не экспортировать их. Но это путь к go?

Да, это разумное решение. Однако нет веской причины извлекать spillInstanceBeans в область действия stati c, просто оставьте ее там, где она была - вы все равно должны создать закрытие над secret.

1 голос
/ 07 апреля 2020

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

Это должно быть отметил, что вы просто реплицируете внутреннюю работу JavaScript VM: такая функция, как spillTheBeans, компилируется только один раз, когда она встречается в исходном коде, даже если у нее есть свободные переменные, такие как secret. Например, в SpiderMonkey результат называется «прото-функцией» (не путать с прототипом). Они являются внутренними по отношению к ВМ и недоступны из JavaScript.

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

Скажем так, это правда, что использование замыканий вместо методов-прототипов и this создает в целом больше функциональных объектов - надежность, полученная из истинных свойств конфиденциальности и свойств только для чтения, может сделать это стоящим. Предлагаемый стиль больше фокусируется на объектах, чем на классах, поэтому может появиться другой дизайн, который нельзя сравнить непосредственно с дизайном на основе классов.

Как говорит Берги , измерьте и пересмотрите, если производительность важнее в (некоторой части) вашего кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...