В языке JavaScript есть примитивы, объекты и функции и все они рассматриваются в качестве объектов, даже примитивы.
Примитивы
Число, булевый (логический) тип данных, строка, undefined и null — все это и есть примитивы.
Числа
В JavaScript существует только один числовой тип данных с плавающей точкой. При выполнении арифметических действий, их значения округляются, что провоцирует некоторые неточности.
Как вы уже догадались, выполнив0.1 + 0.2
вы не получите 0.3
, так как если у вас получилась дробь меньше 0.5, то вам необходимо округлить ответ до ближайшего меньшего числа. Однако в случае с целыми числами все точно и просто, поэтому 1+2 === 3
.
Числовым объектам методы присваивают объектом Number.prototype
:
(123).toString(); //"123" (1.23).toFixed(1); //"1.2"
Существуют глобальные функции для преобразования в числа: parseInt ()
, parseFloat ()
и Number ()
:
parseInt("1") //1 parseInt("text") //NaN parseFloat("1.234") //1.234 Number("1") //1 Number("1.234") //1.234
Недопустимые арифметические операции или недопустимые числовые преобразования не будут вызывать исключение, но это приведет к значению NaN
«не ялвяется числом». isNaN()
поможет обнаружить NaN
.
Оператор +
может добавлять или объединять.
1 + 1 //2 "1" + "1" //"11" 1 + "1" //"11"
Строка
В JavaScript любые текстовые данные являются строками. Текст может быть внутри двойных кавычек “” или одинарных кавычек ‘’. Внутренним форматом строк, вне зависимости от кодировки страницы, является Unicode (Юникод).
Строки имеют такие методы, как: substring (), indexOf () и concat ().
"text".substring(1,3) //ex "text".indexOf('x') //2 "text".concat(" end") //text end
Строки, как и все примитивы, неизменяемы. Например, concat () не изменяет существующую строку, а создает новую.
Булевый (логический) тип данных
Логический тип данных может принимать два значения, в зависимости от ситуации: true и false. false, null, undefined, ‘’ (пустая строка), 0 и NaN являются ложными. Все остальные значения, включая все объекты, являются правдивыми.
Правдивые значения оценивается как true при их выполнении в логическом типе данных, также как и ложные значения оцениваются как false. Взгляните на следующий пример с ложным значением:
let text = ''; if(text) { console.log("This is true"); } else { console.log("This is false"); }
Объекты
Объект представляет собой динамический набор пар ключ-значение. Ключ — это строка. Значение может быть примитивом, объектом или функцией.
Самый простой способ создать объект — использовать литерал объекта:
let obj = { message : "A message", doSomething : function() {} }
Мы можем читать, добавлять, редактировать и удалять свойства объекта в любое время.
- get:
object.name
,object[expression]
- set:
object.name = value,
object[expression] = value
- delete:
delete object.name
,delete object[expression]
let obj = {}; //create empty object obj.message = "A message"; //add property obj.message = "A new message"; //edit property delete object.message; //delete property
Объекты реализуются как хэш-карты. Простую хэш-карту можно создать с помощью Object.create (null):
let french = Object.create(null); french["yes"] = "oui"; french["no"] = "non"; french["yes"];//"oui"
Если вы хотите сделать объект неизменным, используйте Object.freeze () .
Object.keys () может использоваться для перебора всех свойств.
function logProperty(name){ console.log(name); //property name console.log(obj[name]); //property value } Object.keys(obj).forEach(logProperty);
Примитивы против объектов
Примитивы рассматриваются как объекты, в том смысле, что у них есть методы, но они не являются объектами.
Примитивы неизменяемы, а объекты — изменяемы.
Переменные
Переменные могут быть определены с помощью var, let и const.
var создает и инициализирует переменную. Значение переменной, которая не инициализируется, не определена. Переменные, объявленные с помощью var, имеют определенную область действия.
Переменная, созданная через let, будет иметь область действия в пределах данного блока.
Объявление const
задаёт константу, то есть переменную, которую нельзя менять. Однако, она все еще может быть изменчивой. const «замораживает» переменную, а Object.freeze () «замораживает» объект.
Массивы
Массив — разновидность объекта, которая предназначена для хранения пронумерованных значений и предлагает дополнительные методы для удобного манипулирования такой коллекцией.
Обычно они используются для хранения упорядоченных коллекций данных, например — списка товаров на странице, студентов в группе и т.п. Доступ к элементам массива осуществляется с использованием их индексов. Индексы преобразуются в строки и используются в качестве имен для получения значений. В следующем примере arr [1] дает то же значение, что и arr [‘1’]: arr [1] === arr [‘1’].
Простой массив, например let arr = [‘A’, ‘B’, ‘C’], можно легко эмулировать:
{ '0': 'A', '1': 'B', '2': 'C' }
Удаление значений из массива с помощью delete оставит в нем дыры. Для того чтобы избежать этой проблемы, можно использовать splice (), но это может занять много времени:
let arr = ['A', 'B', 'C']; delete arr[1]; console.log(arr); // ['A', empty, 'C'] console.log(arr.length); // 3
Stack и queue могут быть легко реализованы с использованием методов массива:
let stack = []; stack.push(1); // [1] stack.push(2); // [1, 2] let last = stack.pop(); // [1] console.log(last); // 2 let queue = []; queue.push(1); // [1] queue.push(2); // [1, 2] let first = queue.shift();//[2] console.log(first); // 1
Функции
В JS функции являются полноценными объектами встроенного класса Function. Следовательно — их можно присваивать переменным, передавать и, конечно, у них есть свойства:
Существует 3 способа объявления функции:
- Function Declaration — функция, объявленная в основном потоке кода.
- Function Expression –функция, объявленная в контексте какого-либо выражения, например, присваивания.
- Arrow Function (Стрелочные функции)
The Function Declaration
- Function — первое ключевое слово в строке.
- Необходимо дать имя (name) функции.
- Функцию можно использовать до определения. Объявление функции перемещается в верхнюю часть кода.
function doSomething(){}
The Function Expression
- Function — не первое ключевое слово в строке.
- Необязательно давать имя (name) функции.
- Функцию нельзя использовать до определения.
- Данная функция является IIFE (Немедленно вызываемая функция), то есть ее можно вызвать сразу же в месте ее определения.
let doSomething = function() {}
Arrow Function
Стрелочная функция — это такой “синтаксический сахар”, который не несет в себе никакой новой функциональности. Данная функция всегда анонимная.
let doSomething = () = > {};
Стрелочные функции лексически не привязаны к собственным this и arguments.
Вызов функции
Функция, определенная с помощью ключевого слова function, может быть вызвана различными способами:
- Function form
doSomething(arguments)
- Method form
theObject.doSomething(arguments) theObject["doSomething"](arguments)
- Constructor form
new doSomething(arguments)
- Apply form
doSomething.apply(theObject, [arguments]) doSomething.call(theObject, arguments)
- Bind form
let doSomethingWithObject = doSomething.bind(theObject); doSomethingWithObject();
Вообще, функции можно вызвать с большим или наоборот, меньшим количеством аргументов, чем заявлено в определении. Лишние аргументы будут проигнорированы, а отсутствующие будут определены как undefined.
У функций есть два псевдо-объекта: this и arguments.
this
В отличие от PHP, Java, C++ и т.п, значение this в JS не привязывается статически ни к какому объекту, а зависит от контекста вызова:
function doSomething(){ console.log(this) } +------------+------------------+ | Form | this | +------------+------------------- | Function | window/undefined | | Method | theObject | | Constructor| the new object | | apply | theObject | | bind | theObject | +------------+------------------+
arguments
Объект arguments не является массивом данных. Он похож на массив, но не обладает ни одним из его свойств, кроме length.
function reduceToSum(total, value){ return total + value; } function sum(){ let args = Array.prototype.slice.call(arguments); return args.reduce(reduceToSum, 0); } sum(1,2,3);
Однако он спокойно может быть преобразован в обычный массив:
function sum(...args){ return args.reduce(reduceToSum, 0); }
return
При вызове оператора return в функции, выполнение этой функции прерывается. Указанное значение возвращается вызывателю функции. Если оно не указано, вместо него возвращается undefined. Обратите внимание на автоматическую расстановку точки с запятой при использовании return.
function getObject(){ return { } } getObject()
Чтобы избежать разного рода проблем, используйте { в той же строке, что и return:
function getObject(){ return { } }
Динамическая типизация
У языка JavaScript — динамическая типизация. Это значит, что все типы выясняются уже во время выполнения программы.
function log(value){ console.log(value); } log(1); log("text"); log({message : "text"});
Оператор typeof() поможет определить тип переменной.
let n = 1; typeof(n); //number let s = "text"; typeof(s); //string let fn = function() {}; typeof(fn); //function
Однопоточный JS
JS не способен в одно и то же время выполнять несколько функций одновременно, то есть он однопоточный язык. У него есть функция, которая называется «Очередь событий», в которой хранятся задачи для обработки. Никто ни за кем не гонится, нет пробок и заторов в работе. Однако код в «Очереди событий» должен обрабатываться очень быстро, иначе может произойти сбой в браузере, который уничтожит задачу в пух и прах.
Исключения
У JS есть один очень любопытный и крайне полезный механизм, называется он — обработка исключительных ситуаций. Из названия становится понятно, как он работает, нужно только обернуть код в оператор try/catch. Блок catchответственен за перехват и обработку исключений.
Кстати, JS не будет перехватывать незначительные ошибки. Следующий код не выдаст исключение при попытке изменить замороженный объект:
let obj = Object.freeze({}); obj.message = "text";
Еще один момент: используйте директиву “use strict”, чтобы перевести код в режим полного соответствия современному стандарту.
Паттерн Prototype
Object.create(), метод Constructor, и class создают объекты на основе системы прототипа.
Рассмотрим следующий пример:
let service = { doSomething : function() {} } let specializedService = Object.create(service); console.log(specializedService.__proto__ === service); //true
Я использовал Object.create () для создания нового объекта specializedService, прототипом которого является служебный объект. Это означает, что doSomething () доступен на specializedService объекте. Это также означает, что свойство __proto__ объекта specializedService указывает на служебный объект.
Теперь построим аналогичный объект с помощью класса:
class Service { doSomething(){} } class SpecializedService extends Service { } let specializedService = new SpecializedService(); console.log(specializedService.__proto__ === SpecializedService.prototype);
Все методы, определенные в классе Service будут добавлены к объекту Service.prototype. Все экземпляры класса Service будут иметь один и тот же прототип (Service.prototype) объекта. Все экземпляры класса делегируют вызов методов объекту Service.prototype. Методы всего лишь один раз определяются на Service.prototype, а затем наследуются всеми экземплярами класса.
Цепочка прототипов
Каждый объект имеет внутреннюю ссылку на другой объект, называемый его прототипом. У объекта-прототипа также есть свой собственный прототип и так далее до тех пор, пока цепочка не завершится объектом, у которого свойство prototype равно null.
Когда вы запрашиваете свойство, которое объект не содержит, JavaScript будет постепенно двигаться вниз по цепочке прототипов, пока не найдет запрашиваемое свойство или не достигнет конца цепочки.
Паттерн Functional
В JavaScript есть функции первого класса и замыкание. Они открывают путь к функциональному программированию в JS. В результате, можно получить функции высшего порядка.
Все функции в JavaScript это замыкания, когда задается функция — задается замыкание. Так что замыкание создается при определении функции.
Функция высшего порядка — это функция, которая может принимать другую функцию в качестве аргумента или возвращать другую функцию в качестве результата.
Чтобы получить больше сведений про функциональное программирование на JS, пройдите по этим ссылкам:
Узнайте всю мощь функций первого класса
Как point-free композиция сделает из вас профессионала в функциональном программировании
Несколько простеньких функций, которые вы сможете написать с нуля
Почему вы должны дать второй шанс функции замыкания?
Заключение
JS хорош тем, что он прост. Это его сильная сторона.
Перевод статьи Cristi Salcescu: Learn these JavaScript fundamentals and become a better developer