React: Где Расширить Прототип Объекта

Я создал чистое приложение React с помощью create-react-app. Я хотел бы расширить String class и использовать его в одном или нескольких компонентах. Например:

String.prototype.someFunction = function () {
    //some code
}

(возможно, вы захотите взглянуть на этот вопрос чтобы узнать больше о расширении объекта прототипы.)

Да, я могу определить его рядом с компонентом и использовать его внутри. Но каков наилучший и самый чистый путь?

Я должен написать это как class method или внутри componentDidMount или что-то еще?

EDIT:

это даже "ОК", чтобы расширить прототип объекта в React (или в JavaScript) вообще?

1 ответов


TLDR ответ: нигде!

-- Тонкий Ответ...

то, что я пытаюсь сделать, это расширить чистые классы JavaScript, такие как String class, что является очень распространенной задачей в javascript

это даже "ОК", чтобы расширить прототип объекта в React (или в JavaScript) вообще?

расширение / изменение собственных прототипов в JavaScript-это спорная тема, и, вопреки тому, что вы сказали, не то, что большинство профессиональных разработчиков не очень часто. The общий консенсус это расширение собственных прототипов JS-это программный анти-шаблон, которого следует избегать, потому что это нарушает принцип инкапсуляции и изменяет глобальное состояние. Однако, как и во многих правилах, могут быть редкие исключения. Например: вы работаете над игрушечным проектом, который не должен быть качеством производства, вы единственный разработчик, который когда-либо прикоснется к этой базе кода, или код никогда не будет зависимостью для кого-либо еще.

если у вас есть действительно хорошая причина, и действительно знаете, что делаете и полностью осознаете возможные последствия изменений в исходные данные/типы поведения, для выполнения условий и зависимостей, тогда, возможно, вы найдете допустимого использования для этой практики. Но, скорее всего, нет, или, по крайней мере, не очень часто. Почти никогда.

если вы сразу после удобства / синтаксического сахара, вам лучше использовать служебные функции (например, lodash, underscore или ramda) и научиться практиковать функциональную композицию. Но если вы действительно привержены объектно-ориентированной парадигме со всем ее состоянием и подразумевается аргументов (т. е. x.doIt() получает this(x) и все его члены как неявный аргумент), то вы, вероятно, должны просто "подклассифицировать" собственные типы данных, а не изменять их.

так а не мутирует прототип класса такой:

String.prototype.somethingStupid = function () {
  return [].map.call(this, function(letter) {
    if ((Math.random() * 1) > .5) return letter.toUpperCase()
    else return letter.toLowerCase()
  }).join('')
}

console.log('This is a silly string'.somethingStupid())

вы создадите подкласс (работает только с синтаксисом класса ES6), например:

class MyString extends String {
  constructor(x = '') {
    super(x)
    this.otherInstanceProp = ':)'
  }
  
  somethingStupid() {
    return [].map.call(this, function(letter) {
      if ((Math.random() * 1) > .5) return letter.toUpperCase()
      else return letter.toLowerCase()
    }).join('')
  }
}

const myStr = new MyString('This is a silly string')
console.log(myStr)
console.log(myStr.valueOf())
console.log(myStr.somethingStupid() + ', don\'t you think?')

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

я создал чистое приложение React с помощью create-react-app. Я бы как расширить класс String и использовать его в одном или нескольких компонентах ... Да, я могу определить его рядом с компонентом и использовать его внутри. Но каков наилучший и самый чистый путь? ... Должен ли я писать его как метод класса или внутри componentDidMount или что-то еще?

потому что изменение встроенных прототипов (путем мутации таких вещей, как String.prototype) изменяет глобальное состояние приложения, это то, что вы хотите выполнить только один раз и почти наверняка перед любым другим код выполняется (поскольку вы устанавливаете глобальное состояние поведения строк для всего кода, который выполняется после). Поэтому изменение встроенных прототипов из метода экземпляра компонента React не имеет большого смысла.

если вы собираетесь сделать грязное дело, я бы рекомендовал создать отдельный модуль для каждого собственного типа, который вы хотите изменить, и сохранить эти модули где-то вроде src/lib/extend-built-ins/ или что-то, а потом import их как самое первое в src/index.js. Вам не нужно экспортируйте что угодно. Делать import src/lib/extend-built-ins/String.js выполнит код, который изменит ваше глобальное состояние. Это обеспечит по крайней мере достойную организацию и гарантирует, что ваша среда приложения полностью изменена до запуска остальной части кода вашего приложения. Таким образом, вы можете просто использовать расширенные типы во всем приложении, не думая об импорте их откуда-либо.

если вы собираетесь пройти маршрут подклассов (class MyThing extends NativeThing), то я бы порекомендовал вам точно определить свой пользовательские классы в отдельных модулях где-то вроде src/lib/native-subclasses/. Но в этом случае, вам придется import ваши конструкторы классов в любой / каждый модуль, где вы хотите их использовать.

однако, если вы хотите развивать чистые, тестирования, код refactorable, что будет легко для других, и ваше будущее я понял, вы не должны делать такого рода вещи. Вместо этого подумайте о принятии принципов функционального программирования React и его экосистемы. Любой может быстро прочитать и поймите чистую функцию, поэтому используйте их для выполнения преобразований данных и состояний, а не полагайтесь на труднодоступные хаки, такие как изменение глобальных объектов. Это может быть мило и тривиально, чтобы понять этот один маленький хак, но делать это даже один раз в проекте способствует и поощряет себя и других использовать дополнительные ярлыки и анти-шаблоны.