美文网首页前端译趣我爱编程
来自1000个项目的十大JS错误及规避方法

来自1000个项目的十大JS错误及规避方法

作者: linc2046 | 来源:发表于2018-06-05 15:11 被阅读39次
    来自1000个项目的十大JS错误及规避方法

    引言

    为了回馈社区开发者,我们查找数据库中数以千计的项目,找到JS中出现十大错误。

    本文将会深入展示错误原因和解决措施。同时如果你编码中能避免这些错误,相信你会成为一名更好的开发者。

    1.无法捕获的类型错误: 属性无法读取

    这是一条开发者常见的错误,当你在Chrome中读取或调用不存在对象的属性或方法,就会报这个错。

    错误起因有很多种,但最常见的是组件渲染过程中,状态没有正常初始化。

    我们可以看下现实世界的例子,这里以React为例,同时适用于Angular、Vue或其他框架。

    class Quiz extends Component {
      componentWillMount() {
        axios.get('/thedata').then(res => {
          this.setState({items: res.data});
        });
      }
      render() {
        return (
          <ul>
            {this.state.items.map(item =>
              <li key={item.id}>{item.name}</li>
            )}
          </ul>
        );
      }
    }
    

    这里有两点需要注意:

    • 1.组件状态默认是未定义;

    • 2.当组件异步获取数据时,无论数据获取时机发生在构造器、将要挂载或完成挂载期间,组件在数据取得之前至少会渲染一次。

    当Quiz组件第一次渲染时,this.state.items是未定义的,所以items的循环获取item也是未定义,此时会抛这个错误。

    最简单的修复方式是使用默认值进行构造函数状态初始化。

    class Quiz extends Component {
      // Added this:
      constructor(props) {
        super(props);
        // Assign state itself, and a default value for items
        this.state = {
          items: []
        };
      }
      componentWillMount() {
        axios.get('/thedata').then(res => {
          this.setState({items: res.data});
        });
      }
      render() {
        return (
          <ul>
            {this.state.items.map(item =>
              <li key={item.id}>{item.name}</li>
            )}
          </ul>
        );
      }
    }
    

    应用中真正的代码也许是不一样的,但我们希望可以给你提供足够的线索去修复或避免此类问题。

    2.类型错误: 对象未定义

    当你在Safari浏览器中读取或调用未定义对象的属性或方法,这里错误和第一条类似,只不过Safari使用不同的提示。

    3.类型错误: 对象为空

    此类错误发生在Safari中,读取或调用空对象属性或方法。

    有趣的是,js中的null和undefined并不一致,这也是我们看到两种不同提示的原因。

    未定义是变量还未赋值,null意味着值为空。可以使用严格等号运算符比较是否相等。

    现实编码中常见的例子是元素加载前就在JS中使用DOM元素,调用DOM API就会返回空或空对象引用。

    任何执行或处理DOM元素的JS代码都应该在DOM元素创建之后执行。

    JS代码根据html中的顺序,自上而下进行解释执行。当DOM元素中存在脚本标签时,当浏览器解析html页面时,标签的脚本也会执行。

    如果加载脚本前,DOM元素还没有创建,就会报这个错误。

    这里可以添加页面准备就绪的事件监听器来处理问题,一旦addEventListener被触发,init()方法可以使用整个DOM元素。

    <script>
      function init() {
        var myButton = document.getElementById("myButton");
        var myTextfield = document.getElementById("myTextfield");
        myButton.onclick = function() {
          var userName = myTextfield.value;
        }
      }
      document.addEventListener('readystatechange', function() {
        if (document.readyState === "complete") {
          init();
        }
      });
    </script>
    <form>
      <input type="text" id="myTextfield" placeholder="Type your name" />
      <input type="button" id="myButton" value="Go" />
    </form>
    

    4.未知脚本错误

    当JS错误跨月域限制,同时和跨域政策违背时,便会发送脚本错误。例如,將JS文件托管在CDN上,任何未知错误(错误会冒泡至window.onerror处理器,而不是

    由try-catch捕获)只会简单提示脚本错误,而不会提示有用信息。

    这是浏览器安全措施限制跨域数据传递,因此不允许错误信息的传递。

    可以通过下面错误还原此类错误提示:

    1.发送Access-Control-Allow-Origin头

    设置Access-Control-Allow-Origin*预示着资源可以从任何域进行访问。

    你可以將*替换成需要的域名,但是处理多个域名会变得麻烦,如果你用CDN导致缓存问题,其实也不值得处理。

    这里可以了解跨域设置问题

    下面是在不同开发环境中设置头的示例:

    • Apache

    在服务端JS文件夹中创建.htaccess文件,内容写入:

    Header add Access-Control-Allow-Origin "*"
    
    • Nginx

    直接在对应location块中添加add_header指令:

    location ~ ^/assets/ {
        add_header Access-Control-Allow-Origin *;
    }
    
    • HAProxy

    添加下面代码到后台资源服务中:

    rspadd Access-Control-Allow-Origin:\ *
    

    2.脚本标签设置 crossorigin=”anonymous”

    html源代码中在每个已经后台设置Access-Control-Allow-Origin的脚本标签上设置crossorigin="anonymous"

    在添加属性前确保服务端头设置已经完成。火狐中如果有crossorigin="anonymous",但服务端头没有设置,脚本将不会执行。

    5.类型错误: 对象不支持的属性

    IE浏览器中调用未定义的方法会发生此类错误。

    这和谷歌中的类型错误,函数未定义是一样的逻辑错误。

    这在使用js命名空间的IE浏览器的web应用中很常见。

    99.9%的问题都是IE不能正确把当前方法的命名空间绑定至this关键字导致的。

    例如,如果JS命名空间Rollbar存在方法isAwesome, 正常来说,在命名空间下你可以像这样调用方法:

    this.isAwesome();
    

    谷歌、火狐和Opera浏览器支持上面的语法,但IE不支持,使用JS命名空间最安全的方式是使用真正的命名空间名字。

    Rollbar.isAwesome();
    

    6.类型错误: 函数未定义

    谷歌浏览器下调用未定义函数会抛此类错误。

    这些年来随着JS代码技巧和设计模式日益复杂,回调和闭包中作用域进行自我引用的次数也频繁增加,这也是一般导致此类错误的根源。

    考虑下面的代码:

    function clearBoard(){
      alert("Cleared");
    }
    document.addEventListener("click", function(){
      this.clearBoard(); // what is “this” ?
    });
    

    如果你点击页面,控制台会报: 类型错误, this.clearBoard不是函数。

    错误原因是:匿名函数在document上下文中执行,然而clearBoard却在window中定义。

    兼容旧版本浏览器的一般解法是在函数外部將this引用指向另外的变量,同时在闭包中调用继承的变量:

    var self=this;  // save reference to 'this', while it's still this!
    document.addEventListener("click", function(){
      self.clearBoard();
    });
    

    高级浏览器中可以直接使用bind方法传递引用:

    document.addEventListener("click",this.clearBoard.bind(this));
    

    7.无法捕获的范围错误: 最大化调用栈溢出

    Chrome下有很多情况会发生此类错误。

    其中一种是调用递归函数而不进行终止。

    var a = [];
    function recurse(a){
        a[0] = [1];
        recurse(a[0]);
    }
    recurse(a);
    

    另外一种是原生函数接收范围溢出的参数。

    很多原生函数只接收特定范围的输入值。

    如: Number.toExponential(digits)Number.toFixed(digits) 只接收0到20位。

    Number.toPrecision(digits)接受1到21位。

    var a = new Array(4294967295);  //OK
    var b = new Array(-1); //range error
    var num = 2.555555;
    document.writeln(num.toExponential(4));  //OK
    document.writeln(num.toExponential(-2)); //range error!
    num = 2.9999;
    document.writeln(num.toFixed(2));   //OK
    document.writeln(num.toFixed(25));  //range error!
    num = 2.3456;
    document.writeln(num.toPrecision(1));   //OK
    document.writeln(num.toPrecision(22));  //range error!
    

    8.类型错误: 无法读取length属性

    Chrome下读取未定义变量的长度时会发生此类错误。

    一般数组才有length, 如果数组没有初始化或变量名称隐藏至其他上下文时,也会报错。

    var testArray= ["Test"];
    function testFunction(testArray) {
        for (var i = 0; i < testArray.length; i++) {
          console.log(testArray[i]);
        }
    }
    testFunction();
    

    可以通过两种方式解决上面的问题:

    • 1.删除形参, 直接访问外部作用域的变量
    var testArray = ["Test"];
    
    /* Precondition: defined testArray outside of a function */
    function testFunction(/* No params */) {
       for (var i = 0; i < testArray.length; i++) {
         console.log(testArray[i]);
       }
    }
    
    testFunction();
    
      1. 传递正确的实参
    var testArray = ["Test"];
    
    function testFunction(testArray) {
      for (var i = 0; i < testArray.length; i++) {
         console.log(testArray[i]);
       }
    }
    
    testFunction(testArray);
    

    9.无法捕获类型错误: 无法设置属性

    当访问未定义变量时,总是返回undefined, 我们不能获取或设置undefined的属性。

    var test = undefined;
    
    test.value = 0;
    

    10.引用错误: event未定义

    当试图访问未定义变量或超出当前作用域的变量时便会抛此类错误。

    function test(){
        var foo;
    }
    
    console.log(foo);
    

    使用事件处理器时抛此类错误,确保事件对象作为参数传递。

    旧式IE浏览器提供全局事件对象,谷歌会自动绑定事件变量到对应的处理函数中。火狐不会自动绑定。

    最好是事件处理函数中使用传递的事件对象。

    document.addEventListener("mousemove", function (event) {
      console.log(event);
    })
    

    总结

    从上面错误来看,很多都是空或未定义错误。

    如果你使用严格编译器,像静态类型检查工具,如: TypeScript可以帮助你避免这些问题。

    工具可以提示期望却尚未定义的类型。

    即使没有TypeScript, 也能帮助确保使用时对象已经定义。

    译者注

    • 原文链接

    • 原文有删减,因译者水平有限,如有错误,欢迎留言指正交流

    相关文章

      网友评论

        本文标题:来自1000个项目的十大JS错误及规避方法

        本文链接:https://www.haomeiwen.com/subject/woopsftx.html