Nice to meet you!
Если вы можете зайти сюда, значит, вы прочитали предыдущую статью оне только закрытиеСтатья окончена (можно не заходить, если не читали? Конечно... можно зайти), тогда без глупостей, давайте разберемся, что такое функции высшего порядка
Функции высшего порядка
- Функции можно передавать в качестве параметров
- Функции могут быть выведены как возвращаемые значения
функция передана как параметр
- Перезвоните
- В процессе асинхронного запроса ajax очень часто используется функция обратного вызова
- Если вы не уверены, когда запрос вернется, передайте функцию обратного вызова в качестве параметра
- Выполнить функцию обратного вызова после завершения запроса
Ниже приведен простойdemo:
Честно говоря, изначально она была просто простой, но чем больше я писала, тем больше волновалась, поэтому я превратила ее в небольшую демку, которую также можно скопировать и подлить масла (написано в разных версиях), давайте повеселимся, PS: Потому что код был перезаписан Займите больше статей, удалите стиль css, и вы можете использовать стиль, как вам нравится.
- HTML-структура
<body>
<div id="box" class="clearfix"></div>
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<script src="./index.js"></script>
</body>
js-часть
// index.js
// 回调函数
// 异步请求
let getInfo = function (keywords, callback) {
$.ajax({
url: 'http://musicapi.leanapp.cn/search', // 以网易云音乐为例
data: {
keywords
},
success: function (res) {
callback && callback(res.result.songs);
}
})
};
$('#btn').on('click', function() {
let keywords = $(this).prev().val();
$('#loading').show();
getInfo(keywords, getData);
});
// 加入回车
$("#search_inp").on('keyup', function(e){
if (e.keyCode === 13) {
$('#loading').show();
getInfo(this.value, getData);
}
});
function getData(data) {
if (data && data.length) {
let html = render(data);
// 初始化Dom结构
initDom(html, function(wrap) {
play(wrap);
});
}
}
// 格式化时间戳
function formatDuration(duration) {
duration = parseInt(duration / 1000); // 转换成秒
let hour = Math.floor(duration / 60 / 60),
min = Math.floor((duration % 3600) / 60),
sec = duration % 60,
result = '';
result += `${fillIn(min)}:${fillIn(sec)}`;
return result;
}
function fillIn(n) {
return n < 10 ? '0' + n : '' + n;
}
let initDom = function (tmp, callback) {
$('.item').remove();
$('#loading').hide();
$('#box').append(tmp);
// 这里因为不知道dom合适才会被完全插入到页面中
// 所以用callback当参数,等dom插入后再执行callback
callback && callback(box);
};
let render = function (data) {
let template = '';
let set = new Set(data);
data = [...set]; // 可以利用Set去做下简单的去重,可忽略这步
for (let i = 0; i < 8; i++) {
let item = data[i];
let name = item.name;
let singer = item.artists[0].name;
let pic = item.album.picUrl;
let time = formatDuration(item.duration);
template += `
<div class="item">
<div class="pic" data-time="${time}">
<span></span>
<img src="${pic}" />
</div>
<h4>${name}</h4>
<p>${singer}</p>
<audio src="http://music.163.com/song/media/outer/url?id=${item.id}.mp3"></audio>
</div>`;
}
return template;
};
let play = function(wrap) {
wrap = $(wrap);
wrap.on('click', '.item', function() {
let self = $(this),
$audio = self.find('audio'),
$allAudio = wrap.find('audio');
for (let i = 0; i < $allAudio.length; i++) {
$allAudio[i].pause();
}
$audio[0].play();
self.addClass('play').siblings('.item').removeClass('play');
});
};
В соответствии с приведенным выше кодом вы получите следующий эффект, давайте посмотрим
дружеское напоминание: По мере того, как NetEase Cloud Music не является своевременной и Джейской развлеченной компанией по возобновлению проблем с авторским правом, поэтому мы не можем услышать песни Джея, этот маленький сожалеет об этом!Но все же благодаря интерфейсу API, предоставляемому NetEase Cloud Music, давайте послушаем прекрасную музыку
- Ладно, вернемся к основной теме, предыдущих сцен было слишком много, и я по незнанию написал небольшое демо, что действительно слишком много.
- я собирался сказатьфункция передана как параметрПрименение
вывод функции как возвращаемое значение
Дорогие друзья, слишком много сценариев приложений, где функции выводятся как возвращаемые значения, что также отражает идею функционального программирования. На самом деле, на примере замыканий мы уже видели соответствующий контент о функциях высшего порядка, ха-ха
Помните, что когда мы судим о типе данных, мы всеObject.prototype.toStringвычислять. Только «[объект XXX]» отличается для каждого типа данных.
Итак, когда мы пишем суждения о типах, мы обычно передаем параметры в функции.Здесь я кратко напишу реализацию.Давайте сначала посмотрим.
function isType(type) {
return function(obj) {
return Object.prototype.toString.call(obj) === `[object ${type}]
}
}
const isArray = isType('Array');
const isString = isType('String');
console.log(isArray([1, 2, [3,4]]); // true
console.log(isString({}); // false
На самом деле реализованная выше функция isType также принадлежитчастичная функция, частичная функция фактически возвращаетПараметры предварительной обработкиновая функция, чтобы ее можно было вызвать позже
Так же есть предустановленная функция, принцип ее реализации тоже очень прост, при достижении условия выполняется callback функция
function after(time, cb) {
return function() {
if (--time === 0) {
cb();
}
}
}
// 举个栗子吧,吃饭的时候,我很能吃,吃了三碗才能吃饱
let eat = after(3, function() {
console.log('吃饱了');
});
eat();
eat();
eat();
Вышеупомянутая функция поедания выводит «полный» только тогда, когда она выполняется 3 раза, что довольно ярко.
Этот тип предустановленной функции также является реализацией оригинального шаблона декоратора в js. Шаблон декоратора также очень полезен в реальной разработке. Я хорошо изучу его и поделюсь с вами в ближайшие годы.
Ладно, не останавливайся, не останавливайся, посмотрим еще один каштан
// 这里我们创建了一个单例模式
let single = function (fn) {
let ret;
return function () {
console.log(ret); // render一次undefined,render二次true,render三次true
// 所以之后每次都执行ret,就不会再次绑定了
return ret || (ret = fn.apply(this, arguments));
}
};
let bindEvent = single(function () {
// 虽然下面的renders函数执行3次,bindEvent也执行了3次
// 但是根据单例模式的特点,函数在被第一次调用后,之后就不再调用了
document.getElementById('box').onclick = function () {
alert('click');
}
return true;
});
let renders = function () {
console.log('渲染');
bindEvent();
}
renders();
renders();
renders();
Можно сказать, что каштан этой функции более высокого порядка убивает двух зайцев: она не только передает функцию в качестве параметра, но и возвращает функцию в качестве возвращаемого значения.
Шаблон singleton также является очень практичным шаблоном проектирования, и мы проанализируем эти шаблоны проектирования в будущих статьях, так что следите за обновлениями, ха-ха, давайте посмотрим на другие варианты использования функций более высокого порядка.
Другие приложения
каррирование функций
Каррирование также известно как частичная оценка.Каррированная функция получит некоторые параметры, а затем не будет оцениваться немедленно, а продолжит возвращать новую функцию, сохранять входящие параметры в виде замыканий и ждать, пока они будут фактически оценены. ., оценивать все входящие параметры сразу
Можно ли это объяснить проще? Заполните несколько параметров в функции, затем верните новую функцию и, наконец, оцените ее, нет, не так ли просто сказать
Как бы это ни было просто, это не так понятно, как демонстрация нескольких строчек кода.
// 普通函数
function add(x,y){
return x + y;
}
add(3,4); // 7
// 实现了柯里化的函数
// 接收参数,返回新函数,把参数传给新函数使用,最后求值
let add = function(x){
return function(y){
return x + y;
}
};
add(3)(4); // 7
Приведенный выше код очень прост и служит только в качестве руководства. Теперь давайте напишем общую каррирующую функцию
function curry(fn) {
let slice = Array.prototype.slice, // 将slice缓存起来
args = slice.call(arguments, 1); // 这里将arguments转成数组并保存
return function() {
// 将新旧的参数拼接起来
let newArgs = args.concat(slice.call(arguments));
return fn.apply(null, newArgs); // 返回执行的fn并传递最新的参数
}
}
Реализована общая функция каррирования, которая потрясающая, все замечательные.
Но этого недостаточно, мы также можем использовать ES6, чтобы добиться этого снова, см. следующий код:
// ES6版的柯里化函数
function curry(fn) {
const g = (...allArgs) => allArgs.length >= fn.length ?
fn(...allArgs) :
(...args) => g(...allArgs, ...args)
return g;
}
// 测试用例
const foo = curry((a, b, c, d) => {
console.log(a, b, c, d);
});
foo(1)(2)(3)(4); // 1 2 3 4
const f = foo(1)(2)(3);
f(5); // 1 2 3 5
Две разные идеи реализации одинаковы, и вы можете попытаться проанализировать их позже.
Но обнаружили ли вы, что метод привязки, который мы использовали в ES5, на самом деле использовал идею каррирования, так что давайте посмотрим
let obj = {
songs: '以父之名'
};
function fn() {
console.log(this.songs);
}
let songs = fn.bind(obj);
songs(); // '以父之名'
Почему ты это сказал? Я не вижу здесь никаких подсказок, не волнуйтесь, давайте посмотрим на принцип реализации bind
Function.prototype.bind = function(context) {
let self = this,
slice = Array.prototype.slice,
args = slice.call(arguments);
return function() {
return self.apply(context, args.slice(1));
}
};
Это дежа вю, не так ли, из той же двери выбегает какая-то учительница
антикарринг
Какой? Анти-каррирование, люди, которых только что вылечили, а теперь еще одно анти-каррирование, тут нет ошибки! Так что же такое антикаррирование? Короче говоря, это заимствование функций, а функции (методы) в мире используются всеми.
Например, объект может не только использовать собственные методы, но и заимствовать методы, которые ему не принадлежат, добиться этого кажется очень просто, потому что вызов и применение могут решить эту задачу.
(function() {
// arguments就借用了数组的push方法
let result = Array.prototype.slice.call(arguments);
console.log(result); // [1, 2, 3, 'hi']
})(1, 2, 3, 'hi');
Math.max.apply(null, [1,5,10]); // 数组借用了Math.max方法
Из вышеприведенного кода видно, что все — это семья, которая любит друг друга. Используя вызов и применение для изменения указания this, this, используемый в методе, больше не ограничивается исходным указанным объектом и имеет более широкое применение после обобщения.
Тему антикарринга запостил наш дорогой отец js, давайте посмотрим, что он делает на практическом примере
let slice = Array.prototype.slice.uncurrying();
(function() {
let result = slice(arguments); // 这里只需要调用slice函数即可
console.log(result); // [1, 2, 3]
})(1,2,3);
Приведенный выше код превращает Array.prototype.slice в общую функцию среза за счет антикаррирования, благодаря чему она не ограничивается работой только с массивами, а вызов функции становится более лаконичным и понятным.
Наконец, давайте взглянем на его реализацию, посмотрим на код, более реалистичный.
Function.prototype.uncurrying = function() {
let self = this; // self 此时就是下面的Array.prototype.push方法
return function() {
let obj = Array.prototype.shift.call(arguments);
/*
obj其实是这种样子的
obj = {
'length': 1,
'0': 1
}
*/
return self.apply(obj, arguments); // 相当于Array.prototype.push(obj, 110)
}
};
let slice = Array.prototype.push.uncurrying();
let obj = {
'length': 1,
'0': 1
};
push(obj, 110);
console.log(obj); // { '0': 1, '1': 110, length: 2 }
На самом деле существует несколько способов добиться антикаррирования, я поделюсь с вами одним из них ниже и непосредственно рассмотрю код.
Function.prototype.uncurrying = function() {
let self = this;
return function() {
return Function.prototype.call.apply(self, arguments);
}
};
Методика выполнения примерно такая же, можно и писать и пробовать, шевелить руками, шевелить мышцами и костями.
регулирование функции
Давайте поговорим о регулировании функций.Все мы знаем, что в таких сценариях, как изменение размера, прокрутка, перемещение мыши и загрузка файлов, функции будут запускаться часто, что потребляет много производительности, и браузеры не могут этого выдержать.
Так что все начали изучать продвинутый метод, который заключается в контроле частоты срабатывания функции, то есть троттлинге функции. Кратко расскажу о принципе, при использовании setTimeout в определенный период времени функция срабатывает только один раз, что значительно снижает проблему частоты
Существуют также различные реализации дросселирования функций, здесь мы реализуем наиболее часто используемые.
function throttle (fn, wait) {
let _fn = fn, // 保存需要被延迟的函数引用
timer,
flags = true; // 是否首次调用
return function() {
let args = arguments,
self = this;
if (flags) { // 如果是第一次调用不用延迟,直接执行即可
_fn.apply(self, args);
flags = false;
return flags;
}
// 如果定时器还在,说明上一次还没执行完,不往下执行
if (timer) return false;
timer = setTimeout(function() { // 延迟执行
clearTimeout(timer); // 清空上次的定时器
timer = null; // 销毁变量
_fn.apply(self, args);
}, wait);
}
}
window.onscroll = throttle(function() {
console.log('滚动');
}, 500);
Установите высоту для тела на странице и попробуйте ее после появления полосы прокрутки. По сравнению с запуском каждой прокрутки, это значительно снижает потери производительности. Это функция дросселирования функции, которая позволяет получить вдвое больший результат с половиной усилия.Он также широко используется в разработке.
функция разделения времени
Мы знаем, что есть намек на то, что Рим не за один день строился, вообще говоря, жирную бумагу за день не съедают.
То же самое отражено в программе.Если мы получим много данных за один раз (типа данных 10 Вт), а затем он будет застрять при рендеринге фронтенда, и нежные виды браузера встанут и будут ругаться мама.
Таким образом, когда мы имеем дело с таким большим количеством данных, мы можем делать это партиями, не загружая так много за один раз, потому что рот такой большой.
Рассмотрим простую реализацию
function timeChunk(data, fn, count = 1, wait) {
let obj, timer;
function start() {
let len = Math.min(count, data.length);
for (let i = 0; i < len; i++) {
val = data.shift(); // 每次取出一个数据,传给fn当做值来用
fn(val);
}
}
return function() {
timer = setInterval(function() {
if (data.length === 0) { // 如果数据为空了,就清空定时器
return clearInterval(timer);
}
start();
}, wait); // 分批执行的时间间隔
}
}
// 测试用例
let arr = [];
for (let i = 0; i < 100000; i++) { // 这里跑了10万数据
arr.push(i);
}
let render = timeChunk(arr, function(n) { // n为data.shift()取到的数据
let div = document.createElement('div');
div.innerHTML = n;
document.body.appendChild(div);
}, 8, 20);
render();
ленивая загрузка
Метод добавления событий, совместимый с современными браузерами и браузерами IE, является хорошим каштаном.
// 常规的是这样写的
let addEvent = function(ele, type, fn) {
if (window.addEventListener) {
return ele.addEventListener(type, fn, false);
} else if (window.attachEvent) {
return ele.attachEvent('on' + type, function() {
fn.call(ele);
});
}
};
У этой реализации есть недостаток, то есть условие ветвления будет выполняться при вызове addEvent, на самом деле его нужно судить только один раз, и оно должно выполняться каждый раз.
Далее давайте оптимизируем addEvent, чтобы избежать вышеуказанных недостатков, а именно функцию ленивой загрузки, которую мы хотим реализовать.
let addEvent = function(ele, type, fn) {
if (window.addEventListener) {
addEvent = function(ele, type, fn) {
ele.addEventListener(type, fn, false);
}
} else if (window.attachEvent) {
addEvent = function(ele, type, fn) {
ele.attachEvent('on' + type, function() {
fn.call(ele)
});
}
}
addEvent(ele, type, fn);
};
Вышеупомянутая функция addEvent по-прежнему является обычной функцией, и по-прежнему существует ветвь. Однако, когда условие ветвления вводится впервые, функция addEvent внутренне переписывается.
В следующий раз, когда вы войдете в функцию addEvent, в функции не будет условного суждения.
конец
Еще не рано для шоу, время самое подходящее, пора снова прощаться, давайте заключительное слово
- Функции высшего порядка
- Функции могут быть переданы в качестве параметров и возвращены в качестве вывода.
- каррирование функций
- определение
- Получить параметры, вернуть новую функцию, передать параметры новой функции и, наконец, оценить
- эффект
- Повторное использование параметров (добавить функцию каштана)
- Вернуться раньше (ленивая загрузка)
- Ленивые вычисления (привязка)
- определение
- антикарринг
- Единый метод, чтобы не было метода, который нельзя было бы использовать в мире
- регулирование функции
- Установите часто вызываемые функции для выполнения в течение периода времени, чтобы предотвратить многократное срабатывание
- функция разделения времени
- Загрузка слишком большого количества данных за один раз слишком велика. Это может быть похоже на средний поток узла, не торопитесь, не волнуйтесь.
- ленивая загрузка
- Ветвление выполнения функции происходит только один раз
Я собираюсь идти, на самом деле перечислил так много вещей, всех очень трудно увидеть, рано ложиться спать и рано вставать, и хорошо отдохнуть!
Эти две статьи также для начала режима наблюдателя, и я продолжу писать статьи, чтобы поделиться с вами, всем спасибо, брат, за просмотр! Ха-ха