Рубрики
JavaScript

Копировать массив или объект JS по правилам

Как только вы решили копировать массив или объект в JavaScript следует вспомнить о том, что массивы и объекты являются изменяемыми(mutable), в отличие от примитивных переменных и хранятся как ссылки.

Изменяемые, это те, состояние которых может быть изменено после их создания.

Изменяемые

(Mutable):

  • object
  • array
  • function

Неизменяемые

(Immutable):

  • string
  • number
  • boolean
  • null
  • undefined
  • symbol

На практике это будет означать следующее:

const sheeps = ['🐑', '🐑']
const sheeps2 = sheeps
sheeps2.push('🐺')

console.log(sheeps2)
// [ '🐑', '🐑', '🐺' ]

// Аааа 😱, оригинальный массив sheeps изменился?!
console.log(sheeps)
// [ '🐑', '🐑', '🐺' ]

Поэтому необходимо создавать копию данных, а не делать ссылку на существующие.

Копировать массив или объект в JS не так просто как кажется

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

Использовать функцию slice() для копирования массива

Эта функция используется для копирования части массива. Если не указывать параметры, то массив будет копироваться целиком.

const sheeps = ['🐑', '🐑', '🐑']
const cloneSheeps = sheeps.slice()

Другой способ с использованием Array.from:

const cloneSheeps = Array.from(sheeps)

Если бы sheeps был бы объектом, то копировать следовало бы так:

const cloneSheeps = {}
cloneSheeps.prototype = sheeps.prototype

Или так:

let cloneSheeps = Object.assign({}, sheeps)

Использовать Spread syntax чтобы копировать массив или объект

Spread syntax, появившийся в ES6, позволяет “вытаскивать” перебираемые элементы из своего контейнера.

const sheeps = ['🐑', '🐑', '🐑'];

const fakeSheeps = sheeps;

// три точки - это и есть spread operator
const cloneSheeps = [...sheeps];

console.log(sheeps === fakeSheeps);
// true --> это указывает на тот же кусок в памяти

console.log(sheeps === cloneSheeps);
// false --> это указывает на новый кусок в памяти

Неглубокое копирование

Обратите внимание на то, что происходит клонирование только одного уровня – поверхностное копирование!

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

Например так:

let sheeps      = [['🐑', '🐑'], ['🐑', '🐑'], ['🐑', '🐑']]
let cloneSheeps = []

for (let i = 0; i < sheeps.length; i++)
	cloneSheeps.push(sheeps[i].slice())

cloneSheeps[1].push('🐺')

console.log(sheeps)
console.log(cloneSheeps)

Или с помощью функции .map():

let sheeps      = [['🐑', '🐑'], ['🐑', '🐑'], ['🐑', '🐑']]
let cloneSheeps = sheeps.map(function (item) {
	return [...item]
})

cloneSheeps[1].push('🐺')

console.log(sheeps)
console.log(cloneSheeps)

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

Пример:

  function deepCopy (obj) {

    if ('object' === typeof obj) {
      if (obj instanceof Array) {
        let length = obj.length
        let newObj = new Array(length)
        for (let i = 0; i < length; i++) {
          newObj[i] = (deepCopy(obj[i]))
        }
        return newObj
      } else {
        let newObj = {}
        if (obj.prototype) {
          newObj.prototype = obj.prototype
        }
        for (let key in obj) {
          newObj[key] = deepCopy(obj[key])
        }
        return newObj
      }
    }
    return obj
  }

Лучший способ клонировать многомерный массив или объект

На данный момент наиболее простым способом клонирования массивов и объектов является преобразование данных в строку, а за тем обратное преобразование в объект с помощью JSON:

let cloneSheeps = JSON.parse(JSON.stringify(sheeps))

Подводя итог

  1. Помните – объекты Javascript изменчивы и хранятся в памяти по ссылке;
  2. Все классические методы клонирования или Spread syntax осуществляют поверхностное копирование(на одном уровне);
  3. JSON.stringify и JSON.parse можно использовать для глубокого копирования;
  4. Можно создать свою собственную функцию для глубокого копирования или использовать сторонние библиотеки, такие как Lodash, Underscore и тп.

Статья подготовлена по материалам следующих источников:

Рубрики
FrontEnd

JavaScript get параметры

В действительности речь пойдет не по теме “JavaScript get параметры”, а о получении значения параметра из строки адреса, эквиваленте глобальной переменной $_GET на PHP.

Представим, что на наш сайт обратились по средствам такого вида ссылки: http://mysite.ru/?text=hello

Наша задача состоит в том, чтобы получить значение параметра text и использовать его в коде JavaScript. Так как в JavaScript нет глобальной переменной типа $_GET необходимо взять всю ссылку из строки адреса и вытащить из нее нужную информацию.

JavaScript get параметры, получение значения:

// получение значения параметра из строки адреса
function get(name){
   if(name=(new RegExp('[?&]'+encodeURIComponent(name)+'=([^&]*)')).exec(location.search))
      return decodeURIComponent(name[1]);
}

Теперь для того, чтобы получать значение какого-либо параметра из строки адреса мы можем использовать функцию, написанную выше, выглядеть это будет так:

var text = get('text');
console.log( text ); // вывод значения переменной в консоль

 

Рубрики
FrontEnd

Урок: блоки одинаковой высоты на jQuery

Задача состоит в том, чтобы все блоки одного типа имели равную высоту.

Есть два правильных способа сделать блоки одинаковой высоты:

Рубрики
Backend

Использование PHP для сайта-визитки, разделение на модули

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

Зачем вообще использовать PHP в создании сайтов? Даже если вы не являетесь программистом и делаете сайт исключительно для себя, то со временем сайт будет расти и шириться. Так бывает всегда и у всех. Все и всегда сталкиваются с одной и той же проблемой – при добавлении нового раздела сайта, а следовательно и нового пункта меню, нужно, что бы измененное меню появилось на всех страницах сайта. Все прекрасно, когда вы используете CMS, измененное меню “само собой” появится на всех страницах сайта, но как быть, если вы, по какой-то причине, использовали статические страницы, сохраненные с расширением *.html? Такое бывает довольно часто, даже в наше время, когда, казалось бы, все используют различные CMS.

Отличным решением проблемы будет разделение страницы сайта на модули.

В этом случае есть два, наиболее простых, варианта решений:

  • использовать SSI
  • использовать JavaScript
  • использовать PHP

Первый способ, с использованием SSI можно обнаружить по следующему адресу:

SSI в примерах

Второй способ будет описан на данном сайте позднее.

Третий способ, с использованием PHP рассмотрим далее.

Использование PHP при разделении страницы сайта на модули:

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

<?php /* ВАШ КОД */ ?>

С помощью PHP можно подключить файл как обычную функцию, при этом содержимое файла будет обработано как если бы он был автономным PHP сценарием. При этом все функции, определенные в подключаемом файле можно использовать как внутри файла в котором происходит подключение, так и в любых других подключенных файлах. И называется эта функция include.
При разработке простого сайта-визитки удобно разделить уже сверстанный макет сайта на логические части/модули. Например можно разделить макет на три части: header, footer и content. В первых двух модулях будут содержаться куски HTML кода, которые на всех страницах сайта изменяться не должны, что логично, ведь дизайн должен быть неизменным во всех разделах сайта. Изменяться будет только та часть, которая содержит контент.
Во-первых данный подход существенно снижает риск допустить ошибку верстки, так как готовые файлы header и footer уже не будут подвергаться изменениям, а во-вторых, при внесении изменений в дизайн достаточно будет изменить только два файла, не затрагивая файлы содержащие контент, это особенно актуально, когда ваш сайт содержит хотя бы более десяти страниц.

В своем примере я разделил сайт на четыре модуля. В одном файле я буду хранить функции, которые стану использовать на сайте, в других двух файлах будет часть до и после контента и третья часть – контент.
При этом я буду загружать нужный контент динамически, точнее за меня это сделает PHP.
Мой основной файл, index.php, будет содержать следующий код:

<?
include 'functions.php';
include 'header.php';
include page_name().'.php';
include 'footer.php';
?>

Сперва я подключил файл содержащий функции, затем все остальное. Поскольку PHP язык линейный, то я подключаю файлы, содержащие части верстки в той последовательности, в которой они были изначально, до разделения.
Вы заметили, что я использую функция page_name(). В данной функции я определяю – какой файл запросил пользователь и подключаю его.
Код данной функции выглядит следующим образом:

function page_name() // функция определяет какой файл подключить в части контента
{
$page = $_GET['p']; // получаем значение переменной из строки адреса
if($page==''){$page = 'main';} // если значение не указано, значение переменной page устанавливается равным main
return $page; // возвращаем значение переменной page
}

 

Сперва функция получает из строки адреса имя файла, который следует подключить в части контента. Эти данные попадают в адресную строку при клике по ссылке, в меню, так как я указываю такую конструкцию:

<a href="/">Главная</a>
<a href="/?p=about">Обо мне</a>
<a href="/?p=contacts">Контакты</a>

 

То есть в строке адреса содержится информация о том, что некая переменная p имеет значение равное тому, что идет после знака “равно”. Соответственно при клике на ссылку “Обо мне” пользователь перейдет в корень сайта(на главную страницу) и в строке адреса будет передан параметр p со значением about, поэтому вместо текста, главной страницы пользователь увидит текст страницы “Обо мне“, а при клике на “Контакты“, значение будет равно contacts и текст страницы будет соответствующим. В первой строке функции как раз говорится, что:  нужно получить значение переменной p из адресной строки и поместить это значение в переменную $page.

Как мы видим, ссылка на главную не содержит никаких параметров, но тем не менее необходимость подгрузить какой-то контент остается и для этой цели применяется условный оператор, который помещает в переменную $page значение равное слову “main“. Затем функция возвращает результат своей работы, а именно возвращает значение переменной $page, которое подставляется в то место, где была вызвана функция, а именно в 4-ю строку файла index.php, в которой загружается соответствующий файл. Для главной, при отсутствии параметров, main.php, для “Обо мне” – about.php , для “Контакты” – contacts.php.

Выделение активного раздела

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

function current_page($link='') // функция опреляет страницу, которая отображается в данный момент
{
$page = $_GET['p']; // получаем значение переменной из строки адреса
if($page==$link) // если значение переменной page равно значению переменной link...
{
return ' class="current"'; // возвращаем указанную строку
}
}

Данная функция точно так же получает из адресной строки значение переменной p, а за тем сравнивает это значение с тем, что было передано в функцию в качестве ее параметров и если эти значения совпадают, то функция возвращает строку, содержащую конструкцию определяющую атрибут класса в html.

<a href="/">Главная</a>
<a href="/?p=about">Обо мне</a>
<a href="/?p=contacts">Контакты</a>


Далее, значение, возвращаемое функцией, выводится в том месте, где эта функция была вызвана.

Предположим, что пользователь просматривает раздел “Обо мне“, значит в адресной строке находится нечто следующее: http://mysite.ru/?p=about и следовательно программа обработчик идя по коду и встречая на своем пути PHP код делает проверку. Когда обработчик встречает функцию без параметра – current_page() он сверяет то, что в адресной строке и то что передано в функцию в качестве параметра. Поскольку в адресной строке значение p чему-то равно, то в данном случае функция ничего не возвращает. Затем следует та же функция, но уже с параметром, равным “about” и сравнивая это значение со значение в адресной строке оказывается, что значения равны и следовательно функция возвращает строку  class="current", таким образом получается, что при просмотре HTML кода страницы мы увидим, что для ссылки на раздел “Обо мне” определен класс – <a href="/?p=about" class="current">Обо мне</a>. Ну и в последнем случае, когда встречается функция current_page('contacts'), она ничего не возвращает, так как значение параметра функции не соответствует значению переменной из адресной строки.

Это простейший вариант решения задачи. Так как в примере используется всего три пункта меню, решение было “в лоб”, но если пунктов меню больше, то удобней использовать цикл, для вывода меню.

Пример такого цикла приведен ниже:

function menu()
{
// формируем массив, состоящий из имени файла и соответствующего названия раздела
$menu = array(
'main' => 'Главная',
'about' => 'Обо мне',
'contacts' => 'Контакты',
);
$out = ''; // обнуляем переменную, которая будет содержать сформированный код меню
foreach($menu as $url=>$name) // начинаем перебор массива
{
if($url==$_GET['p']||($_GET['p']==''&&$url=='main')) // сравниваем значение текущего элемента массива с параметром из адресной строки
{
// значение текущего элемента массива соответствует значению переменной из адресной строки
$current = ' class="current"'; // значение переменной равно строке определяющей класс
}else
{
$current = ''; // значение переменной пусто
}
if($url<>'main') // проверяем, является ли значение текущего элемента массива соответствующим строке 'main'
{
$url = '?p='.$url; // если нет, то ссылка будет иметь указанный вид
}else
{
$url = '?'; // если да, то нужно ссылаться на корень сайта
}
$out .= '<a href="'.$url.'"'.$current.'>'.$name.'</a>'; // формируем ссылку соответствующую текущему элементу массива и дописываем ее к уже сформированной строке
}
return $out; // возвращаем сформированную строку
}
print menu(); // выводим результат работы функции


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