问题 在浏览器中编写javascript时限制副作用


使用javascript在浏览器中编程时限制副作用非常棘手。

我可以做一些事情,比如不访问成员变量,就像这个愚蠢的例子:

let number = 0;

const inc = (n) => {
  number = number + n;

  return number;
};

console.log(inc(1));  //=> 1
console.log(inc(1));  //=> 2

但是我可以做些什么来减少我的javascript中的副作用?


4053
2017-07-05 11:18


起源

什么是javascript中的“成员变量”? - Bergi
我不明白你的问题。您是否在问如何完全阻止自己编写使用副作用的代码,或者如何在不使用js副作用的情况下有效编程? - Bergi
我的意思是如何最大限度地减少副作用。消除副作用似乎不切实际,但我可以采取哪些措施来消除其中的大多数副作用 - dagda1
好吧,只是不要使用它们(除非你必须:DOM,网络,事件)? - Bergi
dos和donts的通用列表会很棒 - dagda1


答案:


当然,您可以通过仔细编程来避免副作用。我假设你的问题是关于如何做的 避免 他们。你这样做的能力受到语言本质的严重限制。以下是一些方法:

  1. 使用 网络工作者。看到 MDN。 Web worker在另一个与当前窗口不同的全局上下文中运行:

  2. 隔离某些逻辑内部 I帧。使用 跨窗口消息传递 与iframe沟通。

  3. 不变性库。看到 https://github.com/facebook/immutable-js。也 http://bahmutov.calepin.co/avoid-side-effects-with-immutable-data-structures.html

  4. 锁定你的物体 同 Object.freezeObject.seal, 要么 Object.preventExtensions。同样,使用创建对象的只读属性 Object.defineProperty 有吸气剂,但没有安装者,或与 writeable 属性设置为 false

  5. 使用 Object.observe 获取对象及其属性的各种类型更改的异步报告,您可以在其上抛出错误或采取其他操作。

  6. 如果可用,请使用 代理 用于完全控制对对象的访问。

有关阻止访问的注意事项 window,见 javascript沙箱一个模块,以防止引用Window。也 http://dean.edwards.name/weblog/2006/11/sandbox/。最后, 半沙盒Javascript评估

区分内向副作用和外向副作用是有用的。向内的副作用是一些其他代码侵入我的组件的状态。向外的副作用是我的代码侵入其他组件的状态。通过其他答案中提到的IIFE或ES6模块可以防止内向副作用。但更严重的问题是外部副作用,这需要采用上述方法之一。


9
2017-07-11 07:14





想到你的问题,我想到了什么:

  • 不要污染全局命名空间。使用'var'或'let',这些关键字将变量限制在本地范围内。

“通过将全球足迹减少到一个名称,您可以显着降低与其他应用程序,小部件或库进行错误交互的可能性。” - 道格拉斯·克罗克福德

  • 使用分号 
    评论部分 这篇文章 为一直使用分号提供了一些好的(现实生活)理由。

  • 不要声明String,Number或Boolean对象
    (万一你有诱惑)

    var m = new Number(2);
    var n = 2;
    m === n; // will be false because n is a Number and m is an object
    
  • “严格使用”; 是你的朋友。启用严格模式是一个好主意,但是请不要将它添加到现有代码中,因为它可能会破坏某些内容并且您无法仅在词法范围或单个脚本上声明严格 如此处所述

  • 首先声明变量。 一个常见的副作用是人们不了解JavaScript的吊装。提升会在块中搜索变量声明,并将它们一起移动到块的顶部。

    function(){
        var x = 3;
        // visible variables at runtime at this point: x,y,i !
    
        // some code....
    
        var y = 1; // y will be moved to top!
        for( var i = 0; i < 10; i++ ){ // i will be moved to top!
            // some code...
        }
    }
    

    这里 讨论了吊装实际意味着什么,以及它可能导致什么样的“意外行为”。

  • 使用'==='而不是'=='。这有很多很好的理由,这是JavaScript中最常见的“副作用”或“错误”之一。 有关详细信息,请参阅此 关于SO的好答案,但是让我给你一个快速演示:

    '' == '0'           // false
    0 == ''             // true
    
    // can be avoided by using '==='
    '' === '0'          // false
    0 === ''            // false
    
  • 利用IIFE。 IIFE(立即调用函数表达式)允许您声明将调用自身的匿名函数。这样您就可以创建一个新的词法范围,而不必担心全局命名空间。

  • 小心原型。请记住,同一类的JavaScript对象共享相同的原型。改变原型意味着改变原型的行为 所有 班级的实例。 (甚至那些被其他脚本/框架使用的)就像它发生的那样 这里

    Object.prototype.foo = function(){...} // careful!
    

这些是我脑海中浮现的“副作用”。当然还有更多的方法需要处理(有意义的变量名称,一致的代码风格等......)但我不认为这些东西是“副作用”,因为它们会使代码难以维护,但不会破坏它立刻。


4
2017-07-10 20:41



尽管这些都是有价值的编程规则,但我没有看到它们与OP关于副作用的问题有什么关系,这是他在正式的CS /函数编程意义上的意思。
正如他在评论中所说:“dos和donts的通用列表将是伟大的 - dagda1 7月6日6:54”我试图提供一些事情要做/避免,以便省略无意义的行为和冲突。我认为这是他想要的,但我可能是错的 - 问题有点模糊。感谢您的反馈意见 :) - Tim Hallyburton


我最喜欢的技巧是使用编译的语言  javascript,而不是使用JavaScript。

但是,你可以做两件重要的技巧:

  • 用...开始你的文件 "use strict";。这将打开您的代码验证并防止使用未声明的变量。是的,这是一个特殊的字符串,浏览器将知道如何处理。
  • 需要时使用功能。 Javascript不能做正常的范围,但对于函数它工作正常,所以得到 (function() { })(); 在你的肌肉记忆中。

正常的CS基础知识也适用:将代码分成逻辑部分,正确命名变量,在需要时始终显式初始化变量等。


2
2017-07-05 11:22



什么是“正常”范围? - a better oliver
@zeroflagL Imho正常范围,或者只是 理智的范围,是不跨越整个函数的范围,而只是声明它们的语句块。 - Tom van der Woerdt
所以你的意思是块范围? - a better oliver
你能提供一个编译成JavaScript的语言示例来避免副作用问题吗?
@torazaburo LiveScript是一个很好的例子,但是通过'emscripten'你甚至可以将C和朋友编译成JS。 - Tom van der Woerdt