如何写好 JavaScript | 青训营

98 阅读15分钟

各司其职

案例:夜间模式切换功能

  1. 版本一
<header>
    <button id="darkModeButton">🌞</button>
    <h1>夜间模式切换功能案例</h1>
</header>
<main>
    <p>这是网页上夜间模式的一个例子。</p>
</main>
const btn = document.getElementById('darkModeButton');
btn.addEventListener('click', (e) => {
    const body = document.body;
    if (e.target.innerHTML === '🌞') {
        body.style.backgroundColor = 'black';
        body.style.color = 'white';
        e.target.innerHTML = '🌜'
    } else {
        body.style.backgroundColor = 'white';
        body.style.color = 'black';
        e.target.innerHTML = '🌞'
    }
});

版本一是通过DOM操作获取ID为darkModeButton的按钮,然后使用addEventListener函数将一个点击事件监听器添加到按钮上。当用户点击按钮时,会在白天模式和夜间模式之间切换页面的背景色和文本颜色,按钮上的图标也会相应地变化,以指示当前的模式。

  1. 版本二

    版本二的 HTML 与版本一的差不多,css代码在版本一的基础上添加以下代码。

body.night {
    background-color: #333;
    color: #fff;
    transition: all 1s;
}

#darkModeButton::after {
    content: '🌞';
}

.night #darkModeButton::after {
     content: '🌜';
}
const btn = document.getElementById('darkModeButton');
btn.addEventListener('click', (e) => {
    const body = document.body;
    if (body.className !== 'night') {
        body.className = 'night';
    } else {
        body.className = '';
    }
});

版本二 CSS 样式部分中.night 样式定义了夜间模式下页面的背景颜色为深灰色(#333),文本颜色为白色。#darkModeButton::after 定义了按钮的伪元素内容为'🌞',表示白天模式。.night #darkModeButton::after 定义了在夜间模式下按钮的伪元素内容为'🌜',表示夜间模式。JavaScript部分:if (body.className !== 'night') 检查当前页面是否处于夜间模式。className属性用于获取或设置元素的class属性值。如果当前不是夜间模式,则将body元素的class设置为'night',从而应用夜间模式样式。如果当前已经是夜间模式,则将body元素的class设置为空字符串,从而切换回白天模式。

  1. 版本三
<input type="checkbox" id="modeCheckBox">
<div class="content">
    <header>
        <label id="darkModeButton" for="modeCheckBox"></label>
        <h1>夜间模式切换功能案例</h1>
    </header>
    <main>
         <p>这是网页上夜间模式的一个例子。</p>
    </main>
</div>
#modeCheckBox:checked+.content {
    background-color: #333;
    color: #fff;
    transition: all 1s;
}

#darkModeButton::after {
    content: '🌞';
}

#modeCheckBox:checked+.content #darkModeButton::after {
    content: '🌜';
}

版本三 HTML 样式中#modeCheckBox复选框的id属性与后面的for属性链接了<label>元素。这使得当复选框被选中时,<label>元素就会被激活,从而触发 CSS 样式的应用。CSS 样式中#modeCheckBox:checked + .content样式是一个复合选择器,当#modeCheckBox(复选框)被选中时,后续的.content元素将应用这个样式。在这里,它用于定义夜间模式下的背景颜色和文本颜色。#modeCheckBox:checked + .content #darkModeButton::after样式定义了在夜间模式下按钮的伪元素内容为'🌜',表示夜间模式。

总结

  1. HTML/CSS/JS各司其职
  2. 应当避免不必要的有JS直接操作样式
  3. 可以用 class 来表示状态
  4. 纯展示类交互寻求零 JS 方案

组件封装

案例:轮播图

  1. 基本方法
<body>
    <div id="my-slider" class="slider-list">
        <ul>
            <li class="slider-list__item--selected">
                <img src="../images/1.jpg" alt="">
            </li>
            <li class="slider-list__item">
                <img src="../images/2.jpg" alt="">
            </li>
            <li class="slider-list__item">
                <img src="../images/3.jpg" alt="">
            </li>
            <li class="slider-list__item">
                <img src="../images/4.jpg" alt="">
            </li>
            <li class="slider-list__item">
                <img src="../images/5.jpg" alt="">
            </li>
        </ul>
        <a class="slider-list__previous"></a>
        <a class="slider-list__next"></a>
        <div class="slider-list__control">
            <span class="slider-list__control-buttons--selected"></span>
            <span class="slider-list__control-buttons"></span>
            <span class="slider-list__control-buttons"></span>
            <span class="slider-list__control-buttons"></span>
            <span class="slider-list__control-buttons"></span>
        </div>
    </div>
</body>
class Slider {
    constructor(id, cycle = 3000) {
        // 获取轮播图的外部盒子元素和轮播项元素
        this.container = document.getElementById(id);
        this.items = this.container.querySelectorAll('.slider-list__item, .slider-list__item--selected');
        this.cycle = cycle; // 轮播时间间隔,默认为3000毫秒

        // 获取圆形控制点的父元素,并添加鼠标移入和移出事件监听
        const controller = this.container.querySelector('.slider-list__control');
        if (controller) {
            const buttons = controller.querySelectorAll(
                '.slider-list__control-buttons, .slider-list__control-buttons--selected');
            controller.addEventListener('mouseover', evt => {
                // 鼠标移入圆形控制点时,获取当前索引并切换至对应图片,并停止自动轮播
                const idx = Array.from(buttons).indexOf(evt.target);
                if (idx >= 0) {
                    this.slideTo(idx);
                    this.stop();
                }
            });

            controller.addEventListener('mouseout', evt => {
                // 鼠标移出圆形控制点时,恢复自动轮播
                this.start();
            });

            // 监听slide事件,当切换图片时更新圆形控制点的样式
            this.container.addEventListener('slide', evt => {
                const idx = evt.detail.index;
                const selected = controller.querySelector('.slider-list__control-buttons--selected');
                if (selected) selected.className = 'slider-list__control-buttons';
                buttons[idx].className = 'slider-list__control-buttons--selected';
            });
        }

        // 获取向前和向后控制按钮的元素,并添加点击事件监听
        const previous = this.container.querySelector('.slider-list__previous');
        if (previous) {
            previous.addEventListener('click', evt => {
                // 点击向前按钮时,停止自动轮播,切换至前一张图片,然后恢复自动轮播
                this.stop();
                this.slidePrevious();
                this.start();
                evt.preventDefault();
            });
        }

        const next = this.container.querySelector('.slider-list__next');
        if (next) {
            next.addEventListener('click', evt => {
                // 点击向后按钮时,停止自动轮播,切换至下一张图片,然后恢复自动轮播
                this.stop();
                this.slideNext();
                this.start();
                evt.preventDefault();
            });
        }
    }

    // 获取当前选中的图片项(.slider-list__item--selected)
    getSelectedItem() {
        let selected = this.container.querySelector('.slider-list__item--selected');
        return selected;
    }

    // 获取当前选中图片项的索引
    getSelectedItemIndex() {
        return Array.from(this.items).indexOf(this.getSelectedItem());
    }

    // 切换至指定索引的图片项,通过修改对应项的类名实现切换,并触发自定义的slide事件
    slideTo(idx) { // 接收idx参数,表示要滑动到的项的索引
        let selected = this.getSelectedItem();
        if (selected) {
            // 将当前选中的项的类名设为slider-list__item
            selected.className = 'slider-list__item';
        }
        let item = this.items[idx];
        if (item) {
            // 指定索引的项的类名设为slider-list__item--selected,以显示对应的图片
            item.className = 'slider-list__item--selected';
        }

        const detail = {
            index: idx
        };

        // 触发带有更新的索引信息的自定义事件slide,用于通知其他组件或插件轮播图的变化
        const event = new CustomEvent('slide', {
            bubbles: true,
            detail
        });
        this.container.dispatchEvent(event);
    }

    // 切换至下一张图片项,计算下一个索引,并调用slideTo方法实现切换
    slideNext() {
        let currentIdx = this.getSelectedItemIndex();
        let nextIdx = (currentIdx + 1) % this.items.length;
        this.slideTo(nextIdx);
    }

    // 切换至上一张图片项,计算上一个索引,并调用slideTo方法实现切换
    slidePrevious() {
        let currentIdx = this.getSelectedItemIndex();
        let previousIdx = (this.items.length + currentIdx - 1) % this.items.length;
        this.slideTo(previousIdx);
    }

    // 开始自动轮播,使用setInterval定时调用slideNext方法实现自动切换
    start() {
        this.stop();
        this._timer = setInterval(() => this.slideNext(), this.cycle);
    }

    // 停止自动轮播,清除之前设置的定时器,即clearInterval
    stop() {
        clearInterval(this._timer);
    }
}

// 创建Slider实例并开始自动轮播
const slider = new Slider('my-slider');
// 启动自动轮播功能
slider.start();

代码中实现了轮播图的功能,包括鼠标悬停暂停、点击控制按钮切换图片以及自动轮播功能。

  1. 重构:插件化

html代码与基本方法中的一致

// 定义 Slider类,用于管理图片轮播图的功能
class Slider {
    // 在构造函数中接受两个参数:id和cycle
    // id表示包含轮播图的容器元素的ID,而cycle表示自动轮播功能的时间间隔(以毫秒为单位)
    constructor(id, cycle = 3000) {
        // 通过提供的id选择了轮播图的容器元素以及轮播图项的列表
        this.container = document.getElementById(id);
        this.items = this.container.querySelectorAll('.slider-list__item, .slider-list__item--selected');
        this.cycle = cycle;
    }

    // registerPlugins方法允许注册不同的插件来扩展轮播图的功能,
    registerPlugins(...plugins) {
        plugins.forEach(plugin => plugin(this));
    }

    getSelectedItem() {
        const selected = this.container.querySelector('.slider-list__item--selected');
        return selected;
    }

    getSelectedItemIndex() {
        return Array.from(this.items).indexOf(this.getSelectedItem());
    }

    slideTo(idx) {
        const selected = this.getSelectedItem();
        if (selected) {
            selected.className = 'slider-list__item';
        }
        const item = this.items[idx];
        if (item) {
            item.className = 'slider-list__item--selected';
        }

        const detail = {
            index: idx
        };
        const event = new CustomEvent('slide', {
            bubbles: true,
            detail
        });
        this.container.dispatchEvent(event);
    }

    slideNext() {
        const currentIdx = this.getSelectedItemIndex();
        const nextIdx = (currentIdx + 1) % this.items.length;
        this.slideTo(nextIdx);
    }

    slidePrevious() {
        const currentIdx = this.getSelectedItemIndex();
        const previousIdx = (this.items.length + currentIdx - 1) % this.items.length;
        this.slideTo(previousIdx);
    }

    // 用于为轮播图的容器元素添加事件监听器
    addEventListener(type, handler) {
        this.container.addEventListener(type, handler);
    }

    start() {
        this.stop();
        this._timer = setInterval(() => this.slideNext(), this.cycle);
    }

    stop() {
        clearInterval(this._timer);
    }
}

// pluginController插件函数处理轮播图的控制按钮
function pluginController(slider) {
    const controller = slider.container.querySelector('.slider-list__control');
    if (controller) {
        const buttons = controller.querySelectorAll('.slider-list__control-buttons, .slider-list__control-buttons--selected');

        // 监听控制按钮的mouseover和mouseout事件来触发轮播切换和停止/开始自动轮播
        controller.addEventListener('mouseover', evt => {
            const idx = Array.from(buttons).indexOf(evt.target);
            if (idx >= 0) {
                slider.slideTo(idx);
                slider.stop();
            }
        });

        controller.addEventListener('mouseout', evt => {
            slider.start();
        });

        // 当发生轮播切换时,更新控制按钮的类名
        slider.addEventListener('slide', evt => {
            const idx = evt.detail.index;
            const selected = controller.querySelector('.slider-list__control-buttons--selected');
            if (selected) selected.className = 'slider-list__control-buttons';
            buttons[idx].className = 'slider-list__control-buttons--selected';
        });
    }
}

// pluginPrevious插件函数处理轮播图的"上一个"按钮
function pluginPrevious(slider) {
    const previous = slider.container.querySelector('.slider-list__previous');
    if (previous) {
        previous.addEventListener('click', evt => {
            slider.stop();
            slider.slidePrevious();
            slider.start();
            evt.preventDefault();
        });
    }
}

// pluginNext插件函数处理轮播图的"下一个"按钮
function pluginNext(slider) {
    const next = slider.container.querySelector('.slider-list__next');
    if (next) {
        next.addEventListener('click', evt => {
            slider.stop();
            slider.slideNext();
            slider.start();
            evt.preventDefault();
        });
    }
}

const slider = new Slider('my-slider');
// 注册插件函数
slider.registerPlugins(pluginController, pluginPrevious, pluginNext);
slider.start();

代码中定义了一个Slider类,用于管理图片轮播图的功能。它允许滑动到特定的图片、自动轮播功能以及注册插件来扩展轮播图的行为。提供的插件添加了控制按钮、上一个和下一个按钮,以及一个随机切换图片的按钮。

  1. 重构:模板化
<body>
    <div id="my-slider" class="slider-list"></div>
</body>
class Slider {
    constructor(id, opts = {
        // opts是一个包含图片数组images
        images: [],
        cycle: 3000
    }) {
        this.container = document.getElementById(id);
        this.options = opts;
        // 使用提供的images和render方法来创建轮播图的初始HTML结构,将生成的HTML字符串插入到轮播图容器中
        this.container.innerHTML = this.render();
        this.cycle = opts.cycle || 3000;
        // 选择轮播项的元素,并将选中的项初始化为第一张图片
        this.items = this.container.querySelectorAll('.slider-list__item, .slider-list__item--selected');
        this.slideTo(0);
    }
    
    // render方法用于将图片数组渲染成轮播图的HTML内容,并返回一个字符串表示图片列表
    render() {
        const images = this.options.images;
        const content = images.map(image => `
            <li class="slider-list__item">
                <img src="${image}"/>
            </li>
        `.trim());

        return `<ul>${content.join('')}</ul>`;
    }

    // 注册插件来扩展轮播图的功能
    registerPlugins(...plugins) {
        plugins.forEach(plugin => {
            // 为每个插件创建一个包含插件内容的div元素,并将其插入到轮播图容器中
            const pluginContainer = document.createElement('div');
            pluginContainer.className = 'slider-list__plugin';
            pluginContainer.innerHTML = plugin.render(this.options.images);
            this.container.appendChild(pluginContainer);

            // 调用action方法将所需的功能添加到轮播图
            plugin.action(this);
        });
    }

    getSelectedItem() {
        let selected = this.container.querySelector('.slider-list__item--selected');
        return selected;
    }

    getSelectedItemIndex() {
        return Array.from(this.items).indexOf(this.getSelectedItem());
    }

    slideTo(idx) {
        let selected = this.getSelectedItem();
        if (selected) {
            selected.className = 'slider-list__item';
        }
        let item = this.items[idx];
        if (item) {
            item.className = 'slider-list__item--selected';
        }

        const detail = {
            index: idx
        };
        const event = new CustomEvent('slide', {
            bubbles: true,
            detail
        });
        this.container.dispatchEvent(event);
    }

    slideNext() {
        const currentIdx = this.getSelectedItemIndex();
        const nextIdx = (currentIdx + 1) % this.items.length;
        this.slideTo(nextIdx);
    }

    slidePrevious() {
        const currentIdx = this.getSelectedItemIndex();
        const previousIdx = (this.items.length + currentIdx - 1) % this.items.length;
        this.slideTo(previousIdx);
    }

    addEventListener(type, handler) {
        this.container.addEventListener(type, handler);
    }

    start() {
        this.stop();
        this._timer = setInterval(() => this.slideNext(), this.cycle);
    }

    stop() {
        clearInterval(this._timer);
    }
}

// pluginController对象,其中包含了渲染轮播图控制按钮的render方法和处理控制按钮功能的action方法
const pluginController = {
    // render方法根据图片数组images,为每个图片创建一个控制按钮,并为第一张图片添加选中状态
    render(images) {
        return `
            <div class="slider-list__control">
                ${images.map((image, i) => `
                    <span class="slider-list__control-buttons${i===0?'--selected':''}"></span>
                `).join('')}
            </div>
        `.trim();
    },
    // action方法添加了控制按钮的交互功能,包括鼠标悬停时切换到对应图片和自动轮播的停止/开始
    action(slider) {
        const controller = slider.container.querySelector('.slider-list__control');
        
        if (controller) {
            const buttons = controller.querySelectorAll('.slider-list__control-buttons, .slider-list__control-buttons--selected');
            controller.addEventListener('mouseover', evt => {
                const idx = Array.from(buttons).indexOf(evt.target);
                if (idx >= 0) {
                    slider.slideTo(idx);
                    slider.stop();
                }
            });

            controller.addEventListener('mouseout', evt => {
                slider.start();
            });

            slider.addEventListener('slide', evt => {
                const idx = evt.detail.index;
                const selected = controller.querySelector('.slider-list__control-buttons--selected');
                if (selected) selected.className = 'slider-list__control-buttons';
                buttons[idx].className = 'slider-list__control-buttons--selected';
            });
        }
    }
};

// pluginPrevious对象,它包含了渲染"上一个"按钮的render方法和处理"上一个"按钮功能的action方法
const pluginPrevious = {
    render() {
        return `<a class="slider-list__previous"></a>`;
    },
    action(slider) {
        const previous = slider.container.querySelector('.slider-list__previous');
        if (previous) {
            previous.addEventListener('click', evt => {
                slider.stop();
                slider.slidePrevious();
                slider.start();
                evt.preventDefault();
            });
        }
    }
};

// pluginNext是一个对象,它包含了渲染"下一个"按钮的render方法和处理"下一个"按钮功能的action方法
const pluginNext = {
    render() {
        return `<a class="slider-list__next"></a>`;
    },
    action(slider) {
        const next = slider.container.querySelector('.slider-list__next');
        if (next) {
            next.addEventListener('click', evt => {
                slider.stop();
                slider.slideNext();
                slider.start();
                evt.preventDefault();
            });
        }
    }
};

const slider = new Slider('my-slider', {
    // 传递包含图片数组和自动轮播时间的对象
    images: ['../images/1.jpg', '../images/2.jpg', '../images/3.jpg', '../images/4.jpg', '../images/5.jpg'],
    cycle: 3000
});
slider.registerPlugins(pluginController, pluginPrevious, pluginNext);
slider.start();

代码中定义了一个Slider类,用于管理图片轮播图的功能。它可以滑动到指定的图片,还可以自动轮播,并通过注册插件来添加额外的功能,如控制按钮和前后切换按钮。插件对象提供了render方法用于渲染HTML内容和action方法用于添加功能。整体结构更加灵活和易于扩展。

  1. 重构:组件框架

html代码与模板化中的一致

// 定义Component类,它作为轮播图组件的基类
class Component {
    constructor(id, opts = {name, data: []}) {
        this.container = document.getElementById(id);
        this.options = opts;
        this.container.innerHTML = this.render(opts.data);
    }

    registerPlugins(...plugins) {
        plugins.forEach(plugin => {
            const pluginContainer = document.createElement('div');
            pluginContainer.className = `${name}__plugin`;
            pluginContainer.innerHTML = plugin.render(this.options.data);
            this.container.appendChild(pluginContainer);

            plugin.action(this);
        });
    }

    // 抽象方法
    render(data) {
         return '';
    }
}

// 定义Slider类,它继承自Component类,用于实现图片轮播功能
class Slider extends Component {
    constructor(id, opts = {name: 'slider-list', data: [], cycle: 3000}) {
        super(id, opts);
        this.items = this.container.querySelectorAll('.slider-list__item, .slider-list__item--selected')
        this.cycle = opts.cycle || 3000;
        this.slideTo(0);
    }

    // 重写了基类的render方法
    render(data) {
        // 根据传入的图片数据data,生成轮播图中的图片<li>元素
        const content = data.map(image => `
            <li class="slider-list__item">
                <img src="${image}"/>
            </li>
        `.trim());

        return `<ul>${content.join('')}</ul>`;
    }

    getSelectedItem() {
        let selected = this.container.querySelector('.slider-list__item--selected');
        return selected;
    }

    getSelectedItemIndex() {
        return Array.from(this.items).indexOf(this.getSelectedItem());
    }

    slideTo(idx) {
        const selected = this.getSelectedItem();
        if (selected) {
            selected.className = 'slider-list__item';
        }
        let item = this.items[idx];
        if (item) {
            item.className = 'slider-list__item--selected';
        }

        const detail = {
            index: idx
        };
        const event = new CustomEvent('slide', {bubbles: true, detail});
        this.container.dispatchEvent(event);
    }

    slideNext() {
        const currentIdx = this.getSelectedItemIndex();
        const nextIdx = (currentIdx + 1) % this.items.length;
        this.slideTo(nextIdx);
    }

    slidePrevious() {
        const currentIdx = this.getSelectedItemIndex();
        const previousIdx = (this.items.length + currentIdx - 1) % this.items.length;
        this.slideTo(previousIdx);
    }

     addEventListener(type, handler) {
        this.container.addEventListener(type, handler);
    }

    start() {
        this.stop();
        this._timer = setInterval(() => this.slideNext(), this.cycle);
    }

    stop() {
        clearInterval(this._timer);
    }
}

const pluginController = {
    render(images) {
        return `
            <div class="slider-list__control">
                ${images.map((image, i) => `
                    <span class="slider-list__control-buttons${i===0?'--selected':''}"></span>
                `).join('')}
            </div>
        `.trim();
    },
    action(slider) {
        let controller = slider.container.querySelector('.slider-list__control');

        if (controller) {
            let buttons = controller.querySelectorAll('.slider-list__control-buttons, .slider-list__control-buttons--selected');
            controller.addEventListener('mouseover', evt => {
                const idx = Array.from(buttons).indexOf(evt.target);
                if (idx >= 0) {
                    slider.slideTo(idx);
                    slider.stop();
                }
            });

            controller.addEventListener('mouseout', evt => {
                slider.start();
            });

            slider.addEventListener('slide', evt => {
                const idx = evt.detail.index;
                let selected = controller.querySelector('.slider-list__control-buttons--selected');
                if (selected) selected.className = 'slider-list__control-buttons';
                buttons[idx].className = 'slider-list__control-buttons--selected';
            });
        }
    }
};

const pluginPrevious = {
    render() {
        return `<a class="slider-list__previous"></a>`;
    },
    action(slider) {
        const previous = slider.container.querySelector('.slider-list__previous');
        if (previous) {
            previous.addEventListener('click', evt => {
                slider.stop();
                slider.slidePrevious();
                slider.start();
                evt.preventDefault();
            });
        }
    }
};

const pluginNext = {
    render() {
    return `<a class="slider-list__next"></a>`;
    },
    action(slider) {
        const next = slider.container.querySelector('.slider-list__next');
        if (next) {
            next.addEventListener('click', evt => {
                slider.stop();
                slider.slideNext();
                slider.start();
                evt.preventDefault();
            });
        }
    }
};

const slider = new Slider('my-slider', {
    // 传递包含图片数组、组件名称和自动轮播时间的对象
    name: 'slider-list',
    data: ['../images/1.jpg', '../images/2.jpg', '../images/3.jpg', '../images/4.jpg', '../images/5.jpg'],
    cycle: 3000
});
slider.registerPlugins(pluginController, pluginPrevious, pluginNext);
slider.start();

代码中定义了一个基于组件框架的图片轮播组件。它提供了扩展性和灵活性,通过继承和重写方法来实现不同功能。组件可以根据传入的数据渲染HTML内容,并可以通过注册插件来添加不同的功能,如控制按钮和前后切换按钮。最终创建一个轮播图实例,并注册相应插件来实现图片轮播功能。

总结:

  1. 组件设计原则:封装性、正确性、扩展性、复用性
  2. 实现组件的步骤:结构设计、展现效果、行为设计
  3. 三次重构:插件化、模板化、抽象化(组件框架)

过程抽象

1. 高阶函数

高阶函数是指能够接受其他函数作为参数或者返回一个函数的函数。这种特性让JavaScript可以更灵活和功能强大。高阶函数的应用可以使代码更简洁、可读性更高,并且有助于提高代码的复用性和扩展性。

常用高阶函数:

  • Once()

Once 函数用于创建一个只能执行一次的函数。多次调用这个函数,只会返回第一次执行时的结果,后续调用都将被忽略。这在某些情况下很有用,比如只需要执行某个操作一次,例如执行初始化代码或者只需要加载资源一次。

  • Throttle()-节流函数

Throttle 函数用于控制在一段时间内,只执行一次函数调用。它可以用于限制频繁触发的事件的执行次数,比如滚动事件、鼠标移动事件等。这样可以避免事件处理函数过于频繁地执行,提高性能和减少资源消耗。

  • Debounce()-防抖函数

Debounce 函数用于在一连续的事件中,只执行最后一次函数调用。当频繁触发事件时,它将等待一定的延迟时间,如果在这段延迟时间内再次触发了事件,之前的调用将被取消,只会执行最后一次调用。处理搜索输入框的输入事件时非常有用,可以避免频繁地进行搜索操作。

  • Consumer()

Consumer 函数接受两个函数作为参数,并返回一个新函数。这个新函数会将第一个函数的输出作为第二个函数的输入,并执行第二个函数。这个函数组合方式可以用于将两个功能独立的函数组合在一起,实现更复杂的逻辑。

  • Iterative()

Iterative 函数是一个迭代器函数,用于对数据集合进行迭代和处理。它可以帮助我们更方便地遍历数组、对象等数据结构,进行处理或者进行条件判断。在 JavaScript 中,可以使用 for...of 循环或者 forEach 方法进行迭代处理。

使用高阶函数的好处:

  • 代码复用和模块化:高阶函数可以帮助将一些通用的功能封装起来,使得这些功能可以在不同的地方重复使用,从而实现代码的复用和模块化。
  • 代码简洁和可读性:通过使用高阶函数,可以将复杂的逻辑简化成一个函数调用,使代码更加简洁和易于阅读,减少了冗余代码的存在。
  • 抽象和解耦:高阶函数可以将具体的实现细节抽象出来,使得函数的调用方不需要关心具体的实现细节,从而实现解耦。
  • 灵活性和可扩展性:高阶函数可以接受其他函数作为参数或返回一个函数,这样可以使代码更加灵活和可扩展。通过传递不同的函数作为参数,可以实现不同的功能。
  • 函数组合和管道:高阶函数可以用于将多个函数组合在一起,形成函数管道,从而实现复杂的功能。
  • 错误处理和容错机制:通过在高阶函数中处理错误和异常,可以增加代码的健壮性和容错能力。
  • 异步编程:高阶函数在处理异步编程中也非常有用,例如使用回调函数、Promise、async/await等。

2. 编程范式

编程范式是一种编程方式,它描述了程序员在编写代码时所采用的思维方式和方法。主要的编程范式包括命令式编程语言和声明式编程语言。

命令式编程语言:

  • 命令式编程语言是最常见和传统的编程范式。在命令式编程中,程序员编写一系列的指令,描述计算机执行的具体步骤和操作。程序员需要显式地指定计算机执行的每一个细节,包括控制流程、数据的处理和状态的变化等。
  • 这种编程方式更接近计算机的底层操作,对硬件进行直接操作。常见的命令式编程语言包括 C、C++、Java 和 Python(在一定程度上也支持声明式编程)等。

声明式编程语言:

  • 声明式编程语言更加注重描述问题的本质和所需的结果,而不是直接指定计算机执行的步骤。程序员将问题的逻辑和需求表达为一系列的声明,而不需要关注具体的执行细节。
  • 声明式编程语言更加抽象和高级,它们更关注问题的本质和解决方案,而不是计算机执行的步骤。这使得代码更加简洁、清晰和易于理解。
  • 常见的声明式编程语言包括 SQL(用于数据库查询)、HTML(用于网页布局)、CSS(用于样式定义)、JavaScript中的一些函数式编程特性(如map、filter、reduce等)等。

如何区分命令式的编程语言和声明式编程语言:

  • 控制流程:

    • 命令式编程语言需要显式地定义程序的控制流程,包括循环、条件语句等,以指定计算机执行的具体步骤。
    • 声明式编程语言则更关注问题的本质和所需的结果,不需要直接指定计算机的执行步骤,而是通过声明来表达需求。
  • 数据处理:

    • 在命令式编程语言中,程序员需要显式地处理数据和状态的变化。需要通过变量和数据结构来跟踪和修改数据。
    • 声明式编程语言更关注问题的描述,通常通过函数式编程或查询语言来进行数据处理和变换。
  • 代码抽象:

    • 在命令式编程中,代码通常是以指令和步骤的形式来表达,较为具体,更注重细节。
    • 声明式编程更注重问题的抽象和表达,通过高级的抽象方式来表达问题的本质,减少了对细节的关注。
  • 可读性:

    • 由于命令式编程需要显式地描述步骤和细节,代码可能会比较冗长,可读性稍差一些。
    • 声明式编程的代码通常更加简洁,表达更为清晰,易于阅读和理解。
  • 编程风格:

    • 命令式编程强调通过指令来控制计算机执行的步骤,类似于给计算机下达命令。
    • 声明式编程强调通过声明来描述问题的本质和所需的结果,更加抽象和高级。

本篇笔记为个人总结,学习交流使用,欢迎各位大佬评价指正,非常感谢!