What is new function in javascript

Конструктор, оператор «new»

Обычный синтаксис <. >позволяет создать только один объект. Но зачастую нам нужно создать множество похожих, однотипных объектов, таких как пользователи, элементы меню и так далее.

Это можно сделать при помощи функции-конструктора и оператора «new» .

Функция-конструктор

Функции-конструкторы технически являются обычными функциями. Но есть два соглашения:

  1. Имя функции-конструктора должно начинаться с большой буквы.
  2. Функция-конструктор должна выполняться только с помощью оператора «new» .
function User(name) < this.name = name; this.isAdmin = false; >let user = new User("Jack"); alert(user.name); // Jack alert(user.isAdmin); // false

Когда функция вызывается как new User(. ) , происходит следующее:

  1. Создаётся новый пустой объект, и он присваивается this .
  2. Выполняется тело функции. Обычно оно модифицирует this , добавляя туда новые свойства.
  3. Возвращается значение this .

Другими словами, new User(. ) делает что-то вроде:

function User(name) < // this = <>; (неявно) // добавляет свойства к this this.name = name; this.isAdmin = false; // return this; (неявно) >

Таким образом, let user = new User(«Jack») возвращает тот же результат, что и:

Теперь, если нам будет необходимо создать других пользователей, мы можем просто вызвать new User(«Ann») , new User(«Alice») и так далее. Данная конструкция гораздо удобнее и читабельнее, чем многократное создание литерала объекта.

Это и является основной целью конструкторов – реализовать код для многократного создания однотипных объектов.

Давайте ещё раз отметим – технически любая функция (кроме стрелочных функций, поскольку у них нет this ) может использоваться в качестве конструктора. Его можно запустить с помощью new , и он выполнит выше указанный алгоритм. Подобные функции должны начинаться с заглавной буквы – это общепринятое соглашение, чтобы было ясно, что функция должна вызываться с помощью «new».

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

// создаём функцию и сразу же вызываем её с помощью new let user = new function() < this.name = "John"; this.isAdmin = false; // . другой код для создания пользователя // возможна любая сложная логика и инструкции // локальные переменные и так далее >;

Такой конструктор не может быть вызван снова, так как он нигде не сохраняется, просто создаётся и тут же вызывается. Таким образом, этот трюк направлен на инкапсуляцию кода, который создаёт отдельный объект, без возможности повторного использования в будущем.

Проверка на вызов в режиме конструктора: new.target

Синтаксис из этого раздела используется крайне редко. Вы можете пропустить его, если не хотите углубляться в детали языка.

Используя специальное свойство new.target внутри функции, мы можем проверить, вызвана ли функция при помощи оператора new или без него.

В случае обычного вызова функции new.target будет undefined . Если же она была вызвана при помощи new , new.target будет равен самой функции.

function User() < alert(new.target); >// без "new": User(); // undefined // с "new": new User(); // function User

Это можно использовать внутри функции, чтобы узнать, была ли она вызвана при помощи new , «в режиме конструктора», или без него, «в обычном режиме».

Также мы можем сделать, чтобы вызовы с new и без него делали одно и то же:

function User(name) < if (!new.target) < // в случае, если вы вызвали меня без оператора new return new User(name); // . я добавлю new за вас >this.name = name; > let john = User("John"); // переадресовывает вызов на new User alert(john.name); // John

Такой подход иногда используется в библиотеках, чтобы сделать синтаксис более гибким. Чтобы люди могли вызывать функцию с new и без него, и она все ещё могла работать.

Впрочем, вероятно, это не очень хорошая практика использовать этот трюк везде, так как отсутствие new может ввести разработчика в заблуждение. С new мы точно знаем, что создаётся новый объект.

Возврат значения из конструктора, return

Обычно конструкторы не имеют оператора return . Их задача – записать все необходимое в this , и это автоматически становится результатом.

Но если return всё же есть, то применяется простое правило:

  • При вызове return с объектом, вместо this вернётся объект.
  • При вызове return с примитивным значением, оно проигнорируется.

Другими словами, return с объектом возвращает этот объект, во всех остальных случаях возвращается this .

К примеру, здесь return замещает this , возвращая объект:

function BigUser() < this.name = "John"; return < name: "Godzilla" >; // alert( new BigUser().name ); // Godzilla, получили этот объект

А вот пример с пустым return (или мы могли бы поставить примитив после return , неважно):

function SmallUser() < this.name = "John"; return; // alert( new SmallUser().name ); // John

Обычно у конструкторов отсутствует return . Здесь мы упомянули особое поведение с возвращаемыми объектами в основном для полноты картины.

Кстати, мы можем не ставить круглые скобки после new :

Пропуск скобок считается плохой практикой, но просто чтобы вы знали, такой синтаксис разрешён спецификацией.

Создание методов в конструкторе

Использование конструкторов для создания объектов даёт большую гибкость. Функции-конструкторы могут иметь параметры, определяющие, как создавать объект и что в него записывать.

Конечно, мы можем добавить к this не только свойства, но и методы.

Например, new User(name) ниже создаёт объект с заданным name и методом sayHi :

function User(name) < this.name = name; this.sayHi = function() < alert( "Меня зовут: " + this.name ); >; > let john = new User("John"); john.sayHi(); // Меня зовут: John /* john = < name: "John", sayHi: function() < . >> */

Для создания сложных объектов есть и более продвинутый синтаксис – классы, который мы рассмотрим позже.

Итого

  • Функции-конструкторы или просто конструкторы, являются обычными функциями, но существует общепринятое соглашение именовать их с заглавной буквы.
  • Функции-конструкторы следует вызывать только с помощью new . Такой вызов подразумевает создание пустого this в начале и возврат заполненного в конце.

Мы можем использовать конструкторы для создания множества похожих объектов.

JavaScript предоставляет функции-конструкторы для множества встроенных объектов языка: таких как Date , Set , и других, которые нам ещё предстоит изучить.

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

Как только мы с ними разберёмся, мы вернёмся к объектам для более детального изучения в главах Прототипы, наследование и Классы.

Источник

Синтаксис "new Function"

Существует ещё один вариант объявления функции. Он используется крайне редко, но иногда другого решения не найти.

Синтаксис

Синтаксис для объявления функции:

let func = new Function([arg1, arg2, . argN], functionBody);

Функция создаётся с заданными аргументами arg1. argN и телом functionBody .

Это проще понять на конкретном примере. Здесь объявлена функция с двумя аргументами:

let sum = new Function('a', 'b', 'return a + b'); alert( sum(1, 2) ); // 3

А вот функция без аргументов, в этом случае достаточно указать только тело:

let sayHi = new Function('alert("Hello")'); sayHi(); // Hello

Главное отличие от других способов объявления функции, которые были рассмотрены ранее, заключается в том, что функция создаётся полностью «на лету» из строки, переданной во время выполнения.

Все предыдущие объявления требовали от нас, программистов, писать объявление функции в скрипте.

Но new Function позволяет превратить любую строку в функцию. Например, можно получить новую функцию с сервера и затем выполнить её:

let str = . код, полученный с сервера динамически . let func = new Function(str); func();

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

Замыкание

Обычно функция запоминает, где родилась, в специальном свойстве [[Environment]] . Это ссылка на лексическое окружение (Lexical Environment), в котором она создана (мы разбирали это в главе Замыкание).

Но когда функция создаётся с использованием new Function , в её [[Environment]] записывается ссылка не на внешнее лексическое окружение, в котором она была создана, а на глобальное. Поэтому такая функция имеет доступ только к глобальным переменным.

function getFunc() < let value = "test"; let func = new Function('alert(value)'); return func; >getFunc()(); // ошибка: value не определено

Сравним это с обычным объявлением:

function getFunc() < let value = "test"; let func = function() < alert(value); >; return func; > getFunc()(); // "test", из лексического окружения функции getFunc

Эта особенность new Function выглядит странно, но оказывается очень полезной на практике.

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

Наша новая функция должна взаимодействовать с основным скриптом.

Что если бы она имела доступ к внешним переменным?

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

Например, если в функции объявляется переменная let userName , то минификатор изменяет её на let a (или другую букву, если она не занята) и изменяет её везде. Обычно так делать безопасно, потому что переменная является локальной, и никто снаружи не имеет к ней доступ. И внутри функции минификатор заменяет каждое её упоминание. Минификаторы достаточно умные. Они не просто осуществляют «тупой» поиск-замену, они анализируют структуру кода, и поэтому ничего не ломается.

Так что если бы даже new Function и имела доступ к внешним переменным, она не смогла бы найти переименованную userName .

Если бы new Function имела доступ к внешним переменным, при этом были бы проблемы с минификаторами.

Кроме того, такой код был бы архитектурно хуже и более подвержен ошибкам.

Чтобы передать что-то в функцию, созданную как new Function , можно использовать её аргументы.

Итого

let func = new Function ([arg1, arg2, . argN], functionBody);

По историческим причинам аргументы также могут быть объявлены через запятую в одной строке.

Эти 3 объявления ниже эквивалентны:

new Function('a', 'b', 'return a + b'); // стандартный синтаксис new Function('a,b', 'return a + b'); // через запятую в одной строке new Function('a , b', 'return a + b'); // через запятую с пробелами в одной строке

Функции, объявленные через new Function , имеют [[Environment]] , ссылающийся на глобальное лексическое окружение, а не на родительское. Поэтому они не могут использовать внешние локальные переменные. Но это очень хорошо, потому что страхует нас от ошибок. Переданные явно параметры – гораздо лучшее архитектурное решение, которое не вызывает проблем у минификаторов.

Источник

Читайте также:  Java добавить элемент в коллекцию
Оцените статью