资讯专栏INFORMATION COLUMN

【译】为什么我更喜欢对象而不是switch语句

dack / 1620人阅读

摘要:在本文中我将重点介绍第三种方式我更为喜欢的方法,即使用对象进行快速地查找。现在让我们更实际一点,不是我们写的所有条件都会返回简单的字符串,其中很多会返回布尔值,执行函数等等。

原文自工程师Enmanuel Durán博客,传送门

最近(或者不是最近,这完全取决于您什么时候阅读这边文章),我正在跟我的团队伙伴讨论如何去处理这种需要根据不同的值去处理不同的情况的方法,通常对于这种情况下,人们喜欢使用switch语句或者使用很多if搭配else if条件。在本文中我将重点介绍第三种方式(我更为喜欢的方法),即使用对象进行快速地查找。

switch 语句

switch语句允许我们根据传递的表达式的值来执行表达式并执行某些特定的操作,通常当你学习编写代码和算法时,你会发现可以将它专门用于多种值的情况,你开始使用它,它看起来很好,你很快意识到它给了你很大的自由,耶!但是要小心,自由度越大责任感也就越大。

让我们快速了解一下典型的switch语句是怎么样的:

switch (expression) {
    case x: {
        /* Your code here */
        break;
    }
    case y: {
        /* Your code here */
        break;
    }
    default: {
        /* Your code here */
    }
}

很好,现在有一些你可能不知道需要注意的事情:

可选的关键字break

break关键字允许我们在满足条件时停止执行块。如果不将break关键字添加到switch语句,则不会抛出错误。如果我们不小心忘记break的话,可能意味着在执行代码的时候你甚至不知道代码已经正在执行中了,这还会在调试问题时增加实现结果的的不一致性、突变、内存泄漏和复杂度等问题。我们来看看这个问题的一种表示形式:

switch ("first") {
    case "first": {
        console.log("first case");
    }
    case "second": {
        console.log("second case");
    }
    case "third": {
        console.log("third case");
        break;
    }
    default: {
        console.log("infinite");
    }
}

如果你在控制台中执行这段代码,你会看到输出是

firt case
second case
third case

switch语句在第二种和第三种情况下也会执行,即使第一种情况已经是正确的,然后它在第三种情况块中找到关键字break并停止执行,控制台中没有警告或错误让你知道它,这会让你认为这是预期的行为。

每种情况下的大括号都不是强制的

在javascript中大括号代表着代码块,因为自ECMAscript 2015我们可以使用关键字声明块编译变量,如const或let(但对于switch来说并不是很好),因为大括号不是强制性的,重复声明会导致错误变量,让我们看看当我们执行下面的代码时会发生什么:

switch ("second") {
    case "first":
        let position = "first";
        console.log(position);
        break;
    case "second":
        let position = "second";
        console.log(position);
        break;
    default:
        console.log("infinite");
}

我们会得到:

Uncaught SyntaxError: Identifier "position" has already been declared

这里将会返回一个错误,因为变量position已经在第一种情况下声明过了,并且由于它没有大括号,所以在第二种情况下尝试声明它,它已经存在了。

现在想象使用带有不一致break关键字和大括号的switch语句时会发生什么事:

switch ("first") {
    case "first":
        let position = "first";
        console.log(position);
    case "second":
        console.log(`second has access to ${position}`);
        position = "second";
        console.log(position);
    default:
        console.log("infinite");
}

控制台将输出以下内容:

first
second has access to first
second
infinite

试想一下,由此而引起的错误和突变是如此之多,其可能性是无穷无尽的……不管怎样,switch语句已经讲够了,我们来这里是为了讨论一种不同的方法,我们来这里是为了讨论对象。

更安全查找的对象

对象查找速度很快,随着它们的大小增长它们也会更快,它们也允许我们将数据表示为对于条件执行非常有用的键值对。

使用字符串

让我们从简单的switch示例开始,让我们假设我们需要有条件地保存和返回一个字符串的情景,并使用我们的对象:

const getPosition = position => {
    const positions = {
        first: "first",
        second: "second",
        third: "third",
        default: "infinite"
    };

    return positions[position] || positions.default;
};

const position = getPosition("first"); // Returns "first"
const otherValue = getPosition("fourth"); // Returns "infinite"

这可以做同样类型的工作,如果你想进一步的压缩简化代码,我们可以利用箭头函数:

const getPosition = position =>
    ({
        first: "first",
        second: "second",
        third: "third"
    }[position] || "infinite");

const positionValue = getPosition("first"); // Returns "first"
const otherValue = getPosition("fourth"); // Returns "infinite"

这与前面的实现完全相同,我们在更少的代码行中实现了更紧凑的解决方案。

现在让我们更实际一点,不是我们写的所有条件都会返回简单的字符串,其中很多会返回布尔值,执行函数等等。

使用布尔值

我喜欢创建返回类型一致的值的函数,但是,由于javascript是动态类型语言,因此可能存在函数可能返回动态类型的情况,因此我将在此示例中考虑这一点,如果找不到键,我将创建一个返回布尔值,未定义或字符串的函数。

const isNotOpenSource = language =>
    ({
        vscode: false,
        sublimetext: true,
        neovim: false,
        fakeEditor: undefined
    }[language] || "unknown");

const sublimeState = isNotOpenSource("sublimetext"); // Returns true

看起来不错,对吧?别急,好像我们有一个问题......如果我们调用带有参数的函数,会发生什么"vscode"或fakeEditor不是?嗯,让我们来看看:

它会寻找对象中的键。

它会看到vscode键的值是false。

它会试图返回false,但因为false || "unknown"是unknown,我们最终会返回一个不正确的值。

对于key为fakeEditor也会有同样的问题

Oh no, 好吧,不要惊慌,让我们来解决这个问题:

const isNotOpenSource = editor => {
    const editors = {
        vscode: false,
        sublimetext: true,
        neovim: false,
        fakeEditor: undefined,
        default: "unknown"
    };

    return editor in editors ? editors[editor] : editors.default;
};

const codeState = isNotOpenSource("vscode"); // Returns false
const fakeEditorState = isNotOpenSource("fakeEditor"); // Returns undefined
const sublimeState = isNotOpenSource("sublimetext"); // Returns true
const webstormState = isNotOpenSource("webstorm"); // Returns "unknown"

这就解决了问题,但是......我希望你们问自己一件事:这真的是问题所在吗?我认为我们应该更关心为什么我们需要一个返回布尔值,未定义值或字符串的函数,这里存在严重的不一致性,无论如何,对于这样一个非常棘手的情况这也只是一个可能的解决方案。

使用函数

我们继续讲函数,通常我们会发现我们需要根据参数来执行一个函数,假设我们需要根据输入的类型来解析一些输入值,如果解析器没有注册,我们只返回值:

const getParsedInputValue = type => {
    const emailParser = email => `email,  ${email}`;
    const passwordParser = password => `password, ${password}`;
    const birthdateParser = date => `date , ${date}`;

    const parsers = {
        email: emailParser,
        password: passwordParser,
        birthdate: birthdateParser,
        default: value => value
    };

    return parsers[type] || parsers.default;
};

// We select the parser with the type and then passed the dynamic value to parse
const parsedEmail = getParsedInputValue("email")("myemail@gmail.com"); // Returns email, myemail@gmail.com
const parsedName = getParsedInputValue("name")("Enmanuel"); // Returns "Enmanuel"

如果我们有一个类似的函数返回另一个函数但这次没有参数,我们可以改进代码,以便在调用第一个函数时直接返回,如:

const getValue = type => {
    const email = () => "myemail@gmail.com";
    const password = () => "12345";

    const parsers = {
        email,
        password,
        default: () => "default"
    };

    return (parsers[type] || parsers.default)(); // we immediately invoke the function here
};

const emailValue = getValue("email"); // Returns myemail@gmail.com
const passwordValue = getValue("name"); // Returns default
通用代码块

Switch语句允许我们为多个条件定义公共代码块。

switch (editor) {
    case "atom":
    case "sublime":
    case "vscode":
        return "It is a code editor";
        break;
    case "webstorm":
    case "pycharm":
        return "It is an IDE";
        break;
    default:
        return "unknown";
}

我们如何使用对象来处理它?我们可以在下一个方面做到这一点:

const getEditorType = type => {
    const itsCodeEditor = () => "It is a code editor";
    const itsIDE = () => "It is an IDE";

    const editors = {
        atom: itsCodeEditor,
        sublime: itsCodeEditor,
        vscode: itsCodeEditor,
        webstorm: itsIDE,
        pycharm: itsIDE,
        default: () => "unknown"
    };

    return (editors[type] || editors.default)();
};

const vscodeType = getEditorType("vscode"); 

现在我们有一种方法:

更有条理

更易拓展

更容易维护

更容易测试

更安全并且副作用和风险更小

注意事项

正如预期的那样,所有的方法都有其缺点,这一个也不例外。

由于我们正在使用对象,所以我们将占用内存中的一些临时空间来存储它们,当定义对象的作用域不再可访问时,这个空间将被垃圾收集器释放。

当没有太多情况需要处理时,对象方法可能比switch语句的速度要慢,这可能是因为我们正在创建一个数据结构,然后接收一个键,然而在switch中,我们只是检查值并返回值。

结论

本文不打算改变你的编码风格或让你停止使用switch语句,它只是试图提高你对switch语句的认识,以便它可以正确使用,并开放你的思想探索新的替代方案,在这种情况下,我已经分享了我喜欢使用的方法,但还有更多,例如,你可能想看一个称为模式匹配的ES6提案,如果你不喜欢它,你可以继续探索。

好的开发未来,就是这样,我希望你喜欢这篇文章,如果你这样做,你可能会喜欢这篇关于工厂模式的文章。此外,不要忘记分享和点赞,你可以在twitter上找到我或通过我的电子邮件duranenmanuel@gmail.com联系我,下一个见。

阅读EnmaScript.com上发布的原始文章

译者总结

本文介绍了一种使用对象去代替我们之前用switch和繁琐的if else语句的方法。其实,很多情况下我们可以利用对象与其他组合搭配写出更为高效或可维护的代码。当然,如何去灵活地使用对象去处理一些对应的情况,还是靠我们自己。好的,这篇就总结到这了,不知道对你们有什么启发。相信会给到一些帮助给读者,我们可不是一个只会if else的工程师,哈哈~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102166.html

相关文章

  • 喜欢的5个编程技巧

    摘要:更多信息嵌套三元运算符之前改造后我承认,一开始,使用嵌套三元运算符的想法的确令人倒胃口。当然使用三元运算符具有两面性,但就我个人而言,嵌套三元运算符真的越来越吸引我了。 在这篇文章中,我介绍了一些编程时尝试使用的模式。这些模式是多年来我自己在工作中实践的结果,也有是从同事那里偷偷学到的。 这些模式没有特定的顺序,只是一个简单的集合。 提前退出(early exits) function...

    Aceyclee 评论0 收藏0
  • 」编写更好的 JavaScript 条件式和匹配条件的技巧

    摘要:通常情况下,面向对象编程让我们得以避免条件式,并代之以继承和多态。同时,使用条件式简写来表示值。因此,对于以这种方式编写的代码,你需要使用进行编译。 原文地址:Tips and Tricks for Better JavaScript Conditionals and Match Criteria 原文作者:Milos Protic 介绍 如果你像我一样乐于见到整洁的代码,那么你...

    honmaple 评论0 收藏0
  • [] JavaScript 性能优化杀手

    摘要:原文引言这篇文档包含了如何避免使代码性能远低于预期的建议尤其是一些会导致牵涉到等无法优化相关函数的问题一些背景在中并没有解释器但却有两个不同的编译器通用编译器和优化编译器这意味着你的代码总是会被编译为机器码后直接运行这样一定很快咯并不是 原文:http://dev.zm1v1.com/2015/08/19/javascript-optimization-killers/引言 这篇文档包...

    MockingBird 评论0 收藏0
  • [] Angular 属性绑定更新机制

    摘要:本文主要介绍输入输出绑定方式,特别是当父组件输入绑定值变化时,如何更新子组件输入值。更新指令的属性上文中已经描述了函数是用来更新元素的属性,而是用来更新子组件的输入绑定属性,并且变更检测期间传入的参数就是函数。 原文链接:The mechanics of property bindings update in Angular showImg(https://segmentfault....

    tianhang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<