详解1000+项目数据分析出来的10大JavaScript毛病

译者按: null/undefined激发的毛病在10大毛病中比例很高。而它们极能够致使严重题目,所以要注重起来。

为了保证可读性,本文采纳意译而非直译。别的,本文版权归原作者一切,翻译仅用于进修。

为了回馈拥戴我们的开发者,我们将一切项目数据剖析了一下,总结出10大JavaScript毛病。我们会细致诠释毛病的缘由以及怎样防备再次发作。假如你学会了避开这些坑,那末你将会是一个越发精彩的开发者。

现在数据为王,我们聚合了大批BUG数据,并对它们举行剖析,列出了排名前十的JavaScript毛病。Rollbar网络每一个项目一切的毛病,并统计它们发作的次数。我们将雷同的毛病聚合起来。假如同一个毛病涌现很屡次的话,如许就可以防备像日记一样非常多,让人无从下手。

我们将统计同一个毛病在多少个项目中涌现,并以此来排序。以下所示:

《详解1000+项目数据分析出来的10大JavaScript毛病》

为了轻易浏览,每一条毛病我们将背面的内容做了恰当省略。接下来我们细致引见每一个毛病。

1. Uncaught TypeError: Cannot read property

假如你是一个JavaScript开发者,这类毛病或许你已见怪不怪了。在Chrome下,当你从一个不存在的对象(undefined)猎取属性或则举行函数挪用,就会报如许的错。你可以在Chrome浏览器掌握台测试:

《详解1000+项目数据分析出来的10大JavaScript毛病》

有许多种缘由可以致使这类状况的涌现,一个罕见的状况是在衬着UI部件的时刻,没有准确地初始化状况(state)。我们来看一个实在的例子。在这里我选用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>
    );
  }
}

这里有两个症结点:

  • 组件的状况(state)(this.state)没有初始化,值为undefined
  • 假如运用异步的体式格局猎取数据,那末在数据加载前,该组件已最少衬着一次。这和componentWillMount或则componentDidMount是不是猎取数据无关。也就是说,当Quiz第一次衬着的时刻,this.state.items是未定义的。因而,会报错:"Uncaught TypeError: Cannot read property ‘map’ of undefined"

这个bug很轻易修复。最简朴的要领:在组织函数中初始化state。

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. TypeError: ‘undefined’ is not an object (evaluating

在Safari下,假如在一个未定义(undefined)的对象上读取属性或则挪用函数,就会触发如许的毛病。你可以在Safari掌握台测试。这个毛病基础上来说和第一个在Chrome下的毛病是一样的,只是毛病的音讯差别。

《详解1000+项目数据分析出来的10大JavaScript毛病》

备注:Fundebug早已机灵地将这两种状况聚合为一个毛病了,越发轻易剖析,迎接列位老铁试用!

3. TypeError: null is not an object (evaluating

在Safari下,假如你尝试从null读取属性或则挪用要领,就会报错。以下:

《详解1000+项目数据分析出来的10大JavaScript毛病》

风趣的是,在JavaScript中,null和undefined是差别的,所以我们看到两个差别的毛病音讯。Undefined指的是一个变量没有被赋值,而null指的是值为空。我们可以用===来推断:

《详解1000+项目数据分析出来的10大JavaScript毛病》

一种实际中能够的状况就是:假如你尝试在一个DOM元素加载之前运用它。那末DOM API就会返回null。任何处置惩罚DOM元素的JS代码都应当在DOM加载终了以后挪用。JS代码是根据代码的递次从上往下顺次诠释实行。假如在DOM元素前有剧本,那末在浏览器剖析HTML页面的时刻,JS代码也在实行了。假如JS代码实行的时刻,DOM还没有建立好,那末你会遇到这个毛病。

最经常使用的解法是运用事宜监听,当DOM加载终了以后,再触发JS代码的实行。

<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>

来自网友的备注

  • 上面说的这个题目,是由于在html中一切资本的加载都是从上而下同步加载的,所以之前的代码范例都邑有一句:”在html里css标签放上面,js标签放下面“;包括比方jQuery里的ready要领,这些做法都是为了保证js代码实行的时刻,页面上的dom元素都是建立好了的。
  • 这里我再引见一下defer和async,在外链引入js文件的状况,可以在script标签上加上defer或async修饰符,使该js可以异步加载,从而处理上面遇到的题目。async示意后续的剖析使命和当前js标签的加载使命并行实行,defer示意该js标签的代码会在一切页面元素剖析完成以后,DOMContentLoaded 事宜触发之前实行。二者详细区分参考:https://segmentfault.com/q/1010000000640869

4. (unknown): Script error

当未捕捉的 JavaScript 毛病(经由过程window.onerror处置惩罚顺序激发的毛病,而不是捕捉在try-catch中)被浏览器的跨域战略限定时,会发生这类的剧本毛病。 比方,假如您将您的 JavaScript 代码托管在 CDN 上,则任何未被捕捉的毛病将被报告为“剧本毛病” 而不是包括有效的客栈信息。这是一种浏览器平安措施,旨在防备跨域通报数据,不然将不许可举行通讯。

想要猎取到实在细致的毛病信息,你可以像如许做:

  1. 在header里增添 Access-Control-Allow-Origin 字段
    在header(这应该是服务器返回的response header)字段里,把Access-Control-Allow-Origin设为,如许就示意来自恣意的域名要求都可以准确地接见到服务器的资本。必要的话也可以指定详细的域名来替代星号,比方:Access-Control-Allow-Origin: www.example.com。然则设置的域名太多的话,处置惩罚起来会有点辣手,而且假如你在运用CDN的话还会涌现缓存的题目,如许就有点费力不讨好了。更多参考这里

    下面举一些在种种环境下设置这个header的示例:

    • Apache
      在JavaScript代码地点的文件夹目次下,新建一个.htaccess文件,内容以下:

      Header add Access-Control-Allow-Origin "*"
    • Nginx
      在JavaScript代码地点文件夹目次下面,增添add_header敕令:

      location ~ ^/assets/ {
        add_header Access-Control-Allow-Origin *;
      }```
    • HAProxy
      在后端的JavaScript地点文件到场以下内容:

      rspadd Access-Control-Allow-Origin:\ *
  2. 在JavaScript标签上设置crossorigin=”anonymous”
    在html代码里,每一个设置好了Access-Control-Allow-Origin的js资本,都可以在其JavaScript标签上增添crossorigin=”anonymous”。在设置crossorigin=”anonymous”之前,确定好header字段都是准确发送了的。在Firefox里,假如js标签上涌现了crossorigin属性,然则header里没有Access-Control-Allow-Origin,那末该js将不会被实行。(crossorigin是html5新增的功用,不只是JavaScript标签独占的,比方video、image也可以设置)

5. TypeError: Object doesn’t support property

在IE中,假如挪用未定义的要领就会发作这类毛病。您可以在IE开发者掌握台中举行测试。

《详解1000+项目数据分析出来的10大JavaScript毛病》

相当于 Chrome 中的 “TypeError:”undefined“ is not a function” 毛病。 关于雷同的毛病,差别的浏览器具有差别的毛病音讯。

在IE里运用JavaScript的定名空间时,就很轻易遇到这个毛病。发作这个毛病十有八九是由于IE没法将当前定名空间里的要领绑定到this症结字上。比方,假设有个定名空间Rollbar,它有一个要领叫isAwesome()。在Rollbar定名空间中,可以直接运用this症结字来挪用这个要领:

this.isAwesome();

在Chrome、Firefox和Opera中如许做都是没有题目的,但在IE中就不可。所以,最平安的做法是指定全定名空间:

Rollbar.isAwesome();

6. TypeError: ‘undefined’ is not a function

在Chrome下,挪用一个未定义的函数时就会发作这个毛病,可以在Chrome/Mozilla开发者掌握台测试:

《详解1000+项目数据分析出来的10大JavaScript毛病》

跟着js代码的编码技能和设想形式愈来愈庞杂,在回调函数、闭包等种种作用域中this的指向的层级也随之增添,这就是js代码中this/that指向轻易殽杂的缘由。

比以下面这段代码:

function testFunction() {
 this.clearLocalStorage();
 this.timer = setTimeout(function() {
  this.clearBoard();  // 这里的”this"是指什么?
 }, 0);
};

实行上面的代码会报错:“Uncaught TypeError: undefined is not a function”。由于在挪用setTimeout()要领时,实际上是在挪用window.setTimeout()。传给setTimeout()的匿名函数的this实际上是window,而window并不包括clearBoard()要领。

一个最简朴的、能兼容旧版本浏览器的要领,就是先把this指向赋值给一个变量self,然后在闭包里直接援用这个self变量。比方:

function testFunction () {
 this.clearLocalStorage();
 var self = this;  // 将this赋值给self
 this.timer = setTimeout(function(){
  self.clearBoard();  
 }, 0);
};

也可以运用bind要领来通报this:

function testFunction () {
  this.clearLocalStorage();
  this.timer = setTimeout(this.reset.bind(this), 0);  // bind to 'this'
};

function testFunction(){
    this.clearBoard();    //back in the context of the right 'this'!
};

7. Uncaught RangeError: Maximum call stack

在Chrome里,有几种状况会发作这个毛病,个中一个就是函数的递归挪用,而且不能停止。这个毛病可以在Chrome开发者掌握台重现。

《详解1000+项目数据分析出来的10大JavaScript毛病》

另有,假如传给函数的值超越可吸收的局限时,也会涌现这个毛病。许多函数只吸收指定局限的数值,比方,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!

来自网友的备注

  • 我在chorme测试时,发明上述的第二种参数超越局限的状况,毛病信息并非”Maximum call stack“,而且Number.toExponential(digits) 和 Number.toFixed(digits)要领,吸收的局限应该是0到100
    《详解1000+项目数据分析出来的10大JavaScript毛病》
  • 别的,假如递归层数太多,会致使内存溢出。那末怎样防备呢?可以尾挪用优化,函数末端改成尾递归,详细内容参考这里,文中提到的一个看法就是运用尾递返来防备栈溢出,遗憾的是现在js照样没法支撑”尾挪用优化”。

8. TypeError: Cannot read property ‘length’

在Chrome中,假如读取未定义变量的长度属性,会报错。

《详解1000+项目数据分析出来的10大JavaScript毛病》

假如数组未初始化,或许由于作用域的题目而没有准确地猎取到,则能够会遇到此毛病。让我们用下面的例子来明白这个毛病。

var testArray= ["Test"];

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

testFunction();

函数的参数名会掩盖全局的变量名。也就是说,全局的testArray被函数的参数名掩盖了,所以在函数体里接见到的是当地的testArray,但当地并没有定义testArray,所以涌现了这个毛病。

有两种要领可用于处理这个题目:

  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();
  2. 把外部的变量传给函数testFunction函数

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

9. Uncaught TypeError: Cannot set property

假如对undefined变量举行赋值或读取操纵,会抛出“Uncaught TypeError: cannot set property of undefined”非常。

《详解1000+项目数据分析出来的10大JavaScript毛病》

由于test对象不存在,就会抛出“Uncaught TypeError: cannot set property of undefined”非常。

10. ReferenceError: event is not defined

当接见一个未定义的对象或超越当前作用域的对象,就会发作这个毛病。

《详解1000+项目数据分析出来的10大JavaScript毛病》

假如在运用事宜处置惩罚体系时遇到此毛病,请确保运用传入的事宜对象作为参数。旧浏览器(IE)供应了全局的event变量,但并非一切的浏览器都支撑。像jQuery如许的库试图范例化这类行动。尽管如此,最好运用传入事宜处置惩罚函数的函数。

function myFunction(event) {
    event = event.which || event.keyCode;
    if(event.keyCode===13){
       alert(event.keyCode);
    }
}

结论

看到这里,你会发明这十大毛病险些都是null/undefined毛病。假如有一个好的静态范例搜检体系,比方运用TypeScript可以协助你在编译的时刻就发明题目。假如没有运用TypeScript,那末请多多运用前提语句做推断,防备这类状况涌现。

在临盆环境中会涌现种种不可预期的毛病。症结是要及时发明那些影响用户体验的毛病,并运用恰当的东西疾速发明和处理这些题目。Fundebug供应网站bug监控,助你及时发明bug。

《详解1000+项目数据分析出来的10大JavaScript毛病》

版权声明:
转载时请说明作者Fundebug以及本文地点:
https://blog.fundebug.com/2018/03/12/top-10-javascript-errors-from-1000-projects/

    原文作者:Fundebug
    原文地址: https://segmentfault.com/a/1190000013950385
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞