Coder Social home page Coder Social logo

js-snippets's People

Contributors

mhahaha avatar

js-snippets's Issues

关于 Goo....oogle 的函数实现 @method go()()()('gle')

实现一个 go 函数: 执行go函数,遇到参数‘gle’则结束并返回字符串,反之则追加'O'

image

/**
 * @method go
 * @param {*} str 传入的形参
 * 
 * @returns {Function|String}
 */
function go (str) {

    // 初始字符串
    let BASE_STRING = 'go';

    // 结束后缀
    let SUFFIX = 'gle';

    // 连接字符‘O’
    let STRING_O = 'o';

    function updateString (str) {
        let isEnd = str === SUFFIX;
        BASE_STRING += (isEnd ? SUFFIX : STRING_O);

        return isEnd ? BASE_STRING : updateString;
    }

    return updateString;
}

console.log(go()('gle'))
console.log(go()()('gle'))
console.log(go()()()('gle'))
console.log(go()()()()('gle'))

image

也许你可以更少更好地使用if-else分支条件

在前端JS编码过程,避免不了写 if-else 或 switch-case 分支语句,代码风格因人而异,但其实我们有很多种方式来减少一些晦涩的条件判断,减少嵌套。下面我们从前端JS层面来探讨下:怎样更少更好地写分支条件?

1. 单一条件用三目运算

if (isTrue) {
    return 1;
}
return 2;

三目运算改写:

return isTrue ? 1 : 2;

2. 善用逻辑运算符 &&(and) 和 ||(or)

if (isTrue) {
    setTrueValue();
}

if (!isFalse) {
    setTrueValue();
}

运用逻辑运算符改写:

isTrue && setTrueValue();

isFalse || setTrueValue();

注:尽量避免运用三目运算或逻辑运算符写较复杂逻辑,保证良好的可读性!

3. 多条件判断使用Array.includes()

function sayWords (role) {
    if (role === 'javascript' || role === 'go' || role === 'python' || role === 'java') {
        console.log('Hello World ~');
    }
}

这个写法逻辑没什么问题,相信大家也比较常见,但这种条件一多就特别冗长难看。

Array.includes() 改进写法:

function sayWords (role) {
    const ROLES_SAY_HELLO = ['javascript', 'go', 'python', 'java'];

    if (ROLES_SAY_HELLO.includes(role)) {
        console.log('Hello World ~');
    }
}

显然,我们将所有符合条件的角色提取到数组中进行维护,实现逻辑看起来更加简洁,后面即便增加条件,我们只需丰富数组就好了。

另外一种多条件情况可能存在多个维度,不仅仅像上面的单一值的判断,会涉及逻辑判断等,例如:

if (sex === 'man' && age < 30 && hasRequiredProperty(property) && acountNumber.indexOf('666666') !== -1) {
    doSomething();
}

类似上面这种情况,条件增加直接在if后追加条件会降低代码可读性,这时比较好的一个做法是将条件一一提取到变量里面:

let isMan = sex === 'man',
    isAgeAgree = age < 30,
    hasRequiredProperty = hasRequiredProperty(property),
    isAcountAgree = acountNumber.indexOf('666666') !== -1;

// 注意特殊条件补全注释
let isAvailable = isMan && isAgeAgree && hasRequiredProperty && isAcountAgree;

isAvailable && doSomething();

优点:代码可读性高

4. 尽早返回,减少嵌套

function sayWords (role) {
    if (typeof role === 'string') {
        const ROLES_SAY_HELLO = ['javascript', 'go', 'python', 'java'];

        if (ROLES_SAY_HELLO.includes(role)) {
            console.log('Hello World ~');
        }
    } else {
        throw new Error('role type error!');
    }
}

写法上很符合我们常规思维方式,但从编码角度来看这段代码,我们做如下改进:

function sayWords (role) {

    // 非法类型抛出异常
    if (typeof role !== 'string') {
        throw new Error('role type error!');
    }

    const ROLES_SAY_HELLO = ['javascript', 'go', 'python', 'java'];

    if (ROLES_SAY_HELLO.includes(role)) {
        console.log('Hello World ~');
    }
}

这种方式第一步就把异常类型的错误抛出,也相当于我们常规的 return ,那么我们就减少了主逻辑的一层嵌套了。特别是主逻辑比较长的时候,这种方式的优势就特别明显,只需始终遵循 无效条件提早返回 这个规则。

5. 善用JS函数的默认形参

function sayWords (role) {

    // 非法类型抛出异常
    if (typeof role !== 'string') {
        throw new Error('role type error!');
    }

    if (!role) {
        role = 'javascript';
    }

    // 再或者:role = role || 'javascript'

    const ROLES_SAY_HELLO = ['javascript', 'go', 'python', 'java'];

    if (ROLES_SAY_HELLO.includes(role)) {
        console.log('Hello World ~');
    }
}

我们也不少在函数里面添加类似上面role默认值的逻辑:

if (!role) {
    role = 'javascript';
}

role = role || 'javascript'

改用函数默认形参后,我们可以省去这块逻辑。

function sayWords (role = 'javascript') {

    // 非法类型抛出异常
    if (typeof role !== 'string') {
        throw new Error('role type error!');
    }

    // 再或者:role = role || 'javascript'

    const ROLES_SAY_HELLO = ['javascript', 'go', 'python', 'java'];

    if (ROLES_SAY_HELLO.includes(role)) {
        console.log('Hello World ~');
    }
}

默认形参的优点在于我们可以很直观地知道参数的类型和默认设置,不用担心undefined这类无效类型对逻辑的干扰。

6. 使用Object HashMap 代替 if-else / switch-case

function sayWords (role = 'javascript') {

    if (role === 'javascript') {
        return ['vue', 'react'];
    }

    if (role === 'python') {
        return ['Django'];
    }

    if (role === 'java') {
        return ['JavaWeb', 'Spring'];
    }

    return [];
}

function sayWords (role = 'javascript') {

    switch (role) {
        case 'javascript':
            return ['vue', 'react'];
        
        case 'python':
            return ['Django'];
        
        case 'java':
            return ['JavaWeb', 'Spring'];
        
        default:
            return [];
        
    }
}

类似上面这类写法,我们完全可以不用if-else进行判断,一个字面量对象完全可以搞定。

function sayWords (role = 'javascript') {

    const LANG_STACK = {
        javascript: ['vue', 'react', 'angular'],
        python: ['Django'],
        java: ['JavaWeb', 'Spring']
    }

    return LANG_STACK[role] || [];
}

利用Object维护所有分支信息后,我们省去了晦涩的if-else,逻辑也比较集中。当然我们还会遇到另外一种稍微复杂的情况,例如:

function sayWords (role = 'javascript', name = 'css') {

    if (role === 'javascript') {

        /**
         * 这里可能是多重处理逻辑
         */
        let availableNames = getNamesByRole(role);

        if (availableNames.includes(name)) {
            return ['vue', 'react'];
        }
        return [];
    }

    if (role === 'python') {

        /**
         * 这里可能是多重处理逻辑
         */
        let isAvailable = checkRoleName(role, name);

        if (isAvailable) {
            return ['Django'];
        } 
        return [name];
    }

    if (role === 'java') {

        /**
         * 这里可能是多重处理逻辑
         */
        let availableNames = getNamesByRole(role);

        if (availableNames.includes(name)) {
            return ['JavaWeb', 'Spring'];
        }
        return [role];
    }
}

例如上面写法,当逻辑分支增加,整个函数就显得很大很重,嵌套层级也越来越多,这个时候我们要想着怎么去优化这块的逻辑。
首先,场景合适的话我们依旧可以选择使用Object字面量对象进行逻辑的收敛,不同点在于我们将每一小块的逻辑提取出来作为一个函数单独维护。

改进示例:

function sayWords (role = 'javascript', name = 'css') {

    const LANG_STACK = {
        javascript: jsHandle(role, name),
        python: pythonHandle(role, name),
        java: javaHandle(role, name)
    }

    return LANG_STACK[role];
}

/**
 * javascript 语言处理器
 * @method jsHandle
 * @param {String} role
 * @param {String} name
 * 
 * @returns {Array}
 */
function jsHandle (role, name) {
    let availableNames = getNamesByRole(role);

    if (availableNames.includes(name)) {
        return ['vue', 'react'];
    }
    return [];
}

/**
 * python 语言处理器
 * @method pythonHandle
 * @param {String} role
 * @param {String} name
 * 
 * @returns {Array}
 */
function pythonHandle (role, name) {
    let isAvailable = checkRoleName(role, name);

    if (isAvailable) {
        return ['Django'];
    } 
    return [name];
}

/**
 * java 语言处理器
 * @method javaHandle
 * @param {String} role
 * @param {String} name
 * 
 * @returns {Array}
 */
function javaHandle (role, name) {
    let availableNames = getNamesByRole(role);

    if (availableNames.includes(name)) {
        return ['JavaWeb', 'Spring'];
    }
    return [role];
}

显然,主逻辑拆分之后,很简洁。我们运用策略模式对每一种情况做相应的差分封装处理,提取出最小单元,后面即便再增加更多情况,我们只需增加一个处理函数,然后在主逻辑配置对象信息就行,无需追加if-else。

总结

最近看了老项目的部分代码,看到很多不同的条件分支处理方法,个人对分支这块比较敏感,就临时梳理纪录下。当然,多人协作过程也难免,每个人不同的处理方法,某种意义上来讲没有对错之分,只是有些情况下可以更好。

方法不仅仅上面这些,这些只是我们平常开发编码过程可能比较常见的几种写法及改进方式,具体的还是需要我们在实际场景里面多思考,灵活应变,希望大家编码过程带几分“思考”,带几分“强迫症”,不断寻找最优写法,写出更nice的代码。

最后,不对之处欢迎指正!

手写 EventBus

class EventBus {
    constructor (initialEventMap = {}) {
        this.eventMap = {};

        // 初始化进行批量事件绑定, 同时做事件异常检测
        Object.entries(initialEventMap).forEach(([name, fn]) => {
            this.on(name, fn);
        });
        
    }
    
    /**
     * 事件绑定
     * @param { String } name 事件名称
     * @param { Function } fn 事件回调
     */
    on (name, fn) {
        if (this.eventMap[name]) {
            throw new Error(`已存在名为${name}的事件注册!`);
        }

        if (typeof fn !== 'function') {
            throw new Error(`${name} 不是一个有效Function!`);
        }

        this.eventMap[name] = fn;
    }

    /**
     * 事件触发
     * @param { String } name 事件名称
     * @param { Function } fn 事件参数
     */
    emit (name, ...rest) {
        if (!this.eventMap[name]) {
            throw new Error(`请先注册事件${name}!`);
        }

        this.eventMap[name](...rest);
    }

    /**
     * 事件解绑
     * @param { String } name 事件名称
     */
    off (name) {
        if (this.eventMap[name]) {
            delete this.eventMap[name];
        }
    }
}

手写一个tree过滤器 @method filterTree

现有如下结构的一个tree数组:

let tree = [
    {
        name: 'a',
        children: [
            {
                name: 'b'
            },
            {
                name: 'dd'
            }
        ]
    },
    {
        name: 'C',
        children: [
            {
                name: 'g',
                children: [
                    {
                        name: 'a'
                    },
                    {
                        name: 'c'
                    }
                ]
            }
        ]
    }
];

写一个函数,过滤出所有 name = 'a' 的分支。

/**
 * 过滤符合条件的树结构
 * 
 * @method filterTree
 * @param {Array} sourceTree 
 * 
 * @returns {Array}
 */
function filterTree (sourceTree) {

    if (!Array.isArray(sourceTree)) {
        return;
    }

    let filterRes = sourceTree.filter(item => {
        if (item.name === 'a') {
            return true;
        } else if (!item.children || !item.children.length) {
            return false;
        }

        item.children = filterTree(item.children);

        return item.children.length;
    });
    
    return filterRes;
}

let res = filterTree(tree);

console.log(res);

输出结果:

image

JS对象深拷贝 @method deepClone

/**
 * 对象深拷贝
 * 
 * @method deepClone
 * @param {Object|Array} obj 克隆对象
 * 
 * @returns {Object|Array} 返回拷贝生成的新对象
 */
function deepClone (obj) {
    
    if (typeof obj !== 'object') {
        return;
    }

    let isArray = Array.isArray(obj);
    let copyResult = isArray ? [] : {};

    for (let i in obj) {
        let item = obj[i];
        let isObject = typeof item === 'object';

        copyResult[i] = isObject ? this.deepClone(item) : item;
    }

    return copyResult;
}

说说 webpack 中 loader 和 plugin 的区别

loader是一个转换器,将A文件进行编译成B文件,比如:将A.less转换为A.css,单纯的文件转换过程。

plugin是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.