8 хороших способов написания функций на JavaScript
На JS удобно не только обрабатывать массивы, но и писать приложения на любую тему, как небольшие, так и довольно сложные.
Цель этой заметки скромна - обратить внимание на полезные возможности написания на этом языке обычных функций, из которых, как правило, и состоит наш код.
Все мы занятые люди, поэтому только главные приёмы и, по возможности, компактно.
Коды можно выполнить в браузере, просто скопировав их в файлы .html в кодировке Юникода UTF-8.
1. Просто функция, имеет значение аргумента по умолчанию (arg2), проверку типа аргумента (arg1)
<script> function foo(arg1,arg2='') { //Если первый аргумент - число, дописывает его ко второму и возвращает результат let n = parseInt(arg1); if (isNaN(n) || !isFinite(n)) return ''; return arg2+n; } let s = foo(1,'str1 '); s += foo(2,'str2 '); s += foo('3','str3 '); //str1 1str2 2str3 3 s += foo(1e9999,'str4 '); //нет, так как переполнение s += foo('e99','str5 '); //нет, так как не число alert(s); </script>
Или можно было писать функцию в виде:
var foo = function(arg1,arg2='') { //... }
и потом динамически менять имя функции в коде:
<script> var foo = function(arg1,arg2='') { let n = parseInt(arg1); if (isNaN(n) || !isFinite(n)) return ''; return arg2+n; } var foo2 = function(arg1,arg2='') { //Просто сцеплят аргументы как строки, в обратном порядке, чем foo return String(arg1)+String(arg2); } let f = foo; let s = ''; s = f(1,'s'); f = foo2; s += f(2,'d'); alert (s); //s12d </script>
2. Массив аргументов для функции, как делать перебор аргументов в цикле
<script> function sum (...vals) { //Ищет сумму своих аргументов if (vals==undefined) return 0; let s = 0; for (let i in vals) { s += (isNaN(parseFloat(vals[i])) ? 0 : parseFloat(vals[i])); } return s; } let s = []; s[0] = sum(); //0 s[1] = sum([]); //0 s[2] = sum([1,2,3]); //1, т.к. значения передаются по одному! s[3] = sum(1,2,3); //6 s[4] = sum(4); //4 alert (s.join(',')); </script>
3. Доступ к аргументам функции через свойство arguments
<script> function cnt () { //Считает количество допустимых чисел в списке своих аргументов let n = 0; for (let i in arguments) n += isNaN(arguments[i]) ? 0 : 1; return n; } let s = []; s[0] = cnt(); //0 s[1] = cnt('1'); //1 s[2] = cnt(1,'aaa',2); //2 s[3] = cnt(1e3,'aaa',-2.055,9999); //3 alert (s.join(',')); </script>
4. Функция-замыкание - рекомендуется всегда решать любую составную (состоящую из подзадач) задачу замыканием
<div id="result"></div> <script> function calc (id,n) { //Функция-оболочка, может вызывать вложенные функции function nf(n) { //Вложенная функция, вычисляет факториал от n let v = parseInt(n); if (isNaN(n) || n<0) return 0; let p = 1; for (let i=1; i<=n; i++) p *= i; return p; } document.getElementById(id).innerHTML = nf(n); //120 } //Весь код инкапсулирован в calc, ничего не мешает "снаружи" и не грозит конфликтом имён calc('result',5); </script>
5. Вызов функции по загрузке страницы и отложенный вызов через таймаут
<div id="result"></div> <script> function calc (id,i,n,s) { let elem = document.getElementById (id); if (elem == null) { alert ('Нет элемента '+id); return; } s += i; elem.innerHTML = s; if (i<n) setTimeout(calc,300,id,i+1,n,s);//функция, время мс, аргументы функции } window.addEventListener ('load', function (e) { calc('result',0,10,0); }); </script>
6. Стрелочные функции
Позволяют сократить запись функций из вида
function (arg1, arg2) { /* ... */ return val; }
до
(arg1, arg2) => { /* ... */ return val; }
Особенно это удобно, если тело функции состоит из одного оператора, тогда и операторные скобки тела функции плюс слово return
писать не нужно. Сравните вот это вычисление характеристик массива с помощью метода reduce
<div id="res"></div> <script> //Функция reduce - расчёт любой характеристики массива function sum(arr) { //Сумма элементов массива return arr.reduce(function(prev, curr){ return prev+curr; },0); } function avg(arr) { //Арифм.среднее элементов массива return arr.length ? sum(arr)/arr.length : undefined; } function prod(arr) { //Произведение элементов массива return arr.reduce(function(prev, curr){ return prev*curr; },1); } function cntMoreThan(arr,val) { //Количество элементов > val return arr.reduce(function(prev, curr){ return curr>val ? prev+1 : prev; },0); } let elem = document.getElementById('res'); let arr = []; elem.innerHTML = "Массив arr="+arr.join(',')+"<br>\n"; elem.innerHTML += "Среднее="+avg(arr) + "<br>\n"; //undefined let arr2 = [1,5,3,2,4]; elem.innerHTML += "Массив arr2="+arr2.join(',')+"<br>\n"; elem.innerHTML += "Среднее="+avg(arr2) + "<br>\n"; //3 elem.innerHTML += "Произведение="+prod(arr2) + "<br>\n"; //120 elem.innerHTML += "Элементов>3="+cntMoreThan(arr2,3) + "<br>\n"; //2 </script>
вот с такой короткой записью (показаны только тела изменённых функций):
<script> //Стрелочные функции позволяют записывать код callback-функций //короче, без ключевых слов "function" и "return" function sum(arr) { //Сумма элементов массива return arr.reduce( (prev, curr) => prev+curr, 0); } function prod(arr) { //Произведение элементов массива return arr.reduce( (prev, curr) => prev*curr, 1); } function cntMoreThan(arr,val) { //Количество элементов > val return arr.reduce((prev, curr) => (curr>val ? prev+1 : prev),0); } </script>
Нужно учесть, что стрелочные функции не имеют своего указателя this
и своего прототипа, то есть, не могут быть конструкторами.
7. Реализация задачи на моделирование объекта в виде класса (приведём полный листинг валидного файла .html)
Обратите внимание, что не нужно инициализировать в конструкторе все свойства, их можно добавлять просто динамически.
Также не нужно писать "function
" перед определением методов класса.
Как и в других языках, механизм классов позволяет определять произвольное количество экземпляров класса, а не просто конструировать уникальные объекты (правда, их потом можно клонировать через Object.assign).
Проверено в MS Edge и современных версиях других распространённых браузеров. В IE11 или других браузерах, выпущенных до 2015 года, работать не будет.
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="utf-8"> <title>Задача (класс с описанием флешки)</title> </head> <body> <script> class Flash { constructor(volume, name='') { //Конструктор, ставим только нужные свойства this.volume = volume; //Объём в Гб this.free = volume; //Свободно this.name = name; //Название this.files = []; //Список файлов } toString() { //Переопределили встроенный прототип метода для преобразования объекта класса в строку return (this.name.length ? this.name : 'noName') + ', ' + this.volume + ' Gb' + ' ('+this.free.toFixed(3) +' free, '+this.files.length+' file(s))'; } error (n) { //Метод для вывода сообщений об ошибках, простейший let msg = [ '', 'Не передан массив файлов', 'Не передано имя файла', 'Недопустимый размер файла', 'Недопустимая величина измерения размера файла', 'Нет места на носителе', 'Файл не найден в списке' ]; this.errors++; console.log('Error '+n+': '+msg[n]); } write(...files) { //Записать файл(ы), каждый файл - массив [имя,размер,единица_измерения_размера] this.errors = 0; for (let i in files) { if (Array.isArray(files[i])==false) { this.error(1); continue; } let name = files[i][0].trim(); if (name.length==0) { this.error(2); continue; } let size = parseFloat(files[i][1]); if (isNaN(size) || size<0) { this.error(3); continue; } let measures = ['b','kb','mb','gb']; let measure = files[i][2]; if (measures.includes(measure)==false) { this.error(4); continue; } let n = measures.indexOf(measure); for (let j=n; j<measures.length-1; j++) size /= 1024; if (this.free - size < 0) { this.error(5); return this.errors; } this.free -= size; this.files.push([name,size]); } return this.errors; } remove(...files) { //Удалить файл(ы), имена которых переданы this.errors = 0; for (let i in files) { let name = files[i].trim(); if (name.length==0) { this.error(2); continue; } let n = this.files.findIndex(function(item) { return item[0] == name; }); if (n == -1) { this.error(6); continue; } this.free += this.files[n][1]; if (this.free > this.volume) this.free = this.volume; this.files.splice(n,1); } return this.errors; } list() { return this.files.join('; '); } } //class Flash let f1 = new Flash (16,'Silicon Power'); console.log ('f1= '+f1); let errors = f1.write ( ['file1.txt',1,'kb'], [' file2.dat',2,'gb'], ['file3.cpp',1024,'kb'], ['file4.dat',500,'gb'] ); console.log ('Write errors='+errors); console.log ('f1= '+f1); console.log ('list= '+f1.list()); errors = f1.remove ( 'file2.dat' ); console.log ('Remove errors='+errors); console.log ('f1= '+f1); let f2 = new Flash (4); //Теперь можно создавать любое количество объектов класса console.log ('f2= '+f2); </script> <noscript> <p>Включите JavaScript в браузере для работы приложения!</p> </noscript> <p>Вывод приложения - в консоли...</p> </body></html>
8. Функция, получающая аргументом другую функцию
Делается просто, и метод eval не нужен.
<div id="table"></div> <script> function calcStr (f,x) { return f(x).toFixed(3).toString(); } let f1 = function (x) { return Math.sin(x); } let f2 = function (x) { return Math.cos(x); } let div = document.getElementById('table'); let func = f1; div.innerHTML = calcStr(func,Math.PI/2); //sin(pi/2) func = f2; div.innerHTML += '<br>'+calcStr(func,Math.PI/2); //cos(pi/2) </script>
09.03.2020, 17:56 [1685 просмотров]