이 글은 JavaScript에서 가장 자주 발생하는 10가지 오류를 모아 놓은 것입니다. 이러한 오류의 원인과 오류를 방지하는 방법을 알려드리겠습니다. 도움이 필요한 친구들이 참고할 수 있습니다
개발자 커뮤니티에 보답하기 위해 우리는 수천 개의 프로젝트 데이터베이스를 조사한 결과 JavaScript에서 가장 자주 발생하는 오류 10개를 발견했습니다. 이러한 오류의 원인과 이러한 오류가 발생하지 않도록 방지하는 방법을 알려드리겠습니다. 이러한 "함정"에 빠지지 않을 수 있다면 당신은 더 나은 개발자가 될 것입니다.
데이터가 왕입니다. 가장 자주 발생하는 JavaScript 오류 상위 10개를 수집하고 분석했습니다. Rollbar는 각 프로젝트의 모든 오류를 수집하고 각 오류가 발생한 횟수를 요약합니다. 이를 위해 "지문"(롤바에서 사용하는 알고리즘, https://rollbar.com/docs/grouping-algorithm/ 참조)을 기반으로 오류를 그룹화합니다. 기본적으로 두 번째 오류가 첫 번째 오류와 중복된 경우 두 오류를 모두 동일한 그룹으로 그룹화합니다. 이렇게 하면 로그 파일에 표시되는 엄청난 양의 덤프보다는 사용자에게 좋은 개요를 제공할 것입니다.
우리는 귀하와 귀하의 사용자에게 가장 큰 영향을 미칠 수 있는 버그에 중점을 둡니다. 이를 위해 다양한 회사의 프로젝트 세트를 연구하여 오류의 순위를 매겼습니다. 각 오류가 발생한 총 횟수만 살펴보면 대용량 프로젝트에서 생성된 오류가 다른 오류를 압도하여 대부분의 독자에게는 관련이 없는 오류 데이터 세트가 될 수 있습니다.
다음은 상위 10개 JavaScript 오류입니다.
쉽게 읽을 수 있도록 각 오류 설명을 단축했습니다. 다음으로, 각 오류를 자세히 살펴보고 오류의 원인과 오류 발생을 방지하는 방법을 알아보세요.
1. Uncaught TypeError: Cannot read property
JavaScript 개발자라면 이 오류를 감히 인정할 수 없을 만큼 많이 보았을 것입니다(웃음...). 이 오류는 정의되지 않은 개체의 속성을 읽거나 해당 메서드를 호출할 때 Chrome에서 발생합니다. Chrome 개발자 콘솔에서 쉽게 테스트(시도해보기)할 수 있습니다.
이런 일이 발생하는 데에는 여러 가지 이유가 있지만 일반적인 이유는 UI 구성 요소를 렌더링할 때 상태 초기화가 부적절하기 때문입니다.
실제 애플리케이션에서 어떤 일이 일어나는지 예를 살펴보겠습니다. 우리는 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> ); } }
여기서 달성해야 할 두 가지 중요한 사항이 있습니다.
구성 요소의 상태(예: this.state)는 정의되지 않음에서 시작됩니다.
데이터를 비동기식으로 가져오는 경우 생성자 componentWillMount 또는 componentDidMount에서 가져오든 구성 요소는 데이터가 로드되기 전에 적어도 한 번 렌더링되며 퀴즈가 처음 렌더링되면 this.state.items가 정의되지 않습니다. 이는 결국 ItemList가 항목을 정의되지 않은 것으로 정의하고 콘솔에 오류가 표시됨을 의미합니다. "Uncaught TypeError: Cannot read property 'map' of ununde".
이 문제는 쉽게 해결할 수 있습니다. 가장 간단한 방법은 합리적인 기본값을 사용하여 생성자의 상태를 초기화하는 것입니다.
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 개발자 콘솔에서 이를 쉽게 테스트할 수 있습니다. 이는 본질적으로 1에서 언급한 Chrome 오류와 동일하지만 Safari는 다른 오류 메시지를 사용합니다.
3. TypeError: null은 개체가 아닙니다
Safari에서 null 개체의 속성을 읽거나 메서드를 호출할 때 발생하는 오류입니다. Safari 개발자 콘솔에서 이를 쉽게 테스트할 수 있습니다.
흥미롭게도 JavaScript에서는 null과 정의되지 않음이 동일하지 않기 때문에 두 가지 다른 오류 메시지가 표시됩니다. 정의되지 않음은 일반적으로 할당되지 않은 변수이고, null은 값이 비어 있음을 의미합니다. 동일하지 않은지 확인하려면 엄격한 항등 연산자 ===를 사용해 보세요.
실제 예에서 이 오류가 발생할 수 있는 한 가지 시나리오는 요소를 로드하기 전에 JavaScript에서 요소를 로드하려고 시도하는 경우입니다. 강요. DOM API가 빈 객체 참조에 대해 null을 반환하기 때문입니다.
任何执行和处理 DOM 元素的 JS 代码都应该在创建 DOM 元素之后执行。 JS 代码按照 HTML 中的规定从上到下进行解释。 所以,如果 DOM 元素之前有一个标签,脚本标签内的 JS 代码将在浏览器解析 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. (unknown): Script error
当未捕获的 JavaScript 错误(通过window.onerror处理程序引发的错误,而不是捕获在try-catch中)被浏览器的跨域策略限制时,会产生这类的脚本错误。 例如,如果您将您的 JavaScript 代码托管在 CDN 上,则任何未被捕获的错误将被报告为“脚本错误” 而不是包含有用的堆栈信息。这是一种浏览器安全措施,旨在防止跨域传递数据,否则将不允许进行通信。
要获得真正的错误消息,请执行以下操作:
1. 发送 ‘Access-Control-Allow-Origin' 头部
将 Access-Control-Allow-Origin 标头设置为 * 表示可以从任何域正确访问资源。 如有必要,您可以将域替换为您的域:例如,Access-Control-Allow-Origin:www.example.com。 但是,处理多个域会变得棘手,如果你使用 CDN,可能由此产生更多的缓存问题会让你感觉到这种努力并不值得。 在这里看到更多。
这里有一些关于如何在各种环境中设置这个头文件的例子:
Apache
在 JavaScript 文件所在的文件夹中,使用以下内容创建一个 .htaccess 文件:
Header add Access-Control-Allow-Origin "*"
Nginx
将 add_header 指令添加到提供 JavaScript 文件的位置块中:
location ~ ^/assets/ { add_header Access-Control-Allow-Origin *; }
HAProxy
将以下内容添加到您为 JavaScript 文件提供资源服务的后端:
rspadd Access-Control-Allow-Origin:\ *
2. 在 3f1c4e4b6b16bbbd69b2ee476dc4f83a 中设置 crossorigin="anonymous"
在您的 HTML 代码中,对于您设置了Access-Control-Allow-Origin header 的每个脚本,在 script 标签上设置crossorigin =“anonymous”。在脚本标记中添加 crossorigin 属性之前,请确保验证上述 header 正确发送。 在 Firefox 中,如果存在crossorigin属性,但Access-Control-Allow-Origin头不存在,则脚本将不会执行。
5. TypeError: Object doesn't support property
这是您在调用未定义的方法时发生在 IE 中的错误。 您可以在 IE 开发者控制台中进行测试。
这相当于 Chrome 中的 “TypeError:”undefined“ is not a function” 错误。 是的,对于相同的逻辑错误,不同的浏览器可能具有不同的错误消息。
对于使用 JavaScript 命名空间的 Web 应用程序,这是一个 IE l浏览器的常见的问题。 在这种情况下,99.9% 的原因是 IE 无法将当前名称空间内的方法绑定到 this 关键字。 例如:如果你 JS 中有一个命名空间 Rollbar 以及方法 isAwesome 。 通常,如果您在 Rollbar 命名空间内,则可以使用以下语法调用isAwesome方法:
this.isAwesome();
Chrome,Firefox 和 Opera 会欣然接受这个语法。 另一方面 IE,不会。 因此,使用 JS 命名空间时最安全的选择是始终以实际名称空间作为前缀。
Rollbar.isAwesome();
6. TypeError: ‘undefined' is not a function
当您调用未定义的函数时,这是 Chrome 中产生的错误。 您可以在 Chrome 开发人员控制台和 Mozilla Firefox 开发人员控制台中进行测试。
随着 JavaScript 编码技术和设计模式在过去几年中变得越来越复杂,回调和关闭中的自引用范围也相应增加,这是这种/那种混淆的相当常见的来源。
考虑这个代码片段:
function testFunction() { this.clearLocalStorage(); this.timer = setTimeout(function() { this.clearBoard(); // what is "this"? }, 0); };
执行上面的代码会导致以下错误:“Uncaught TypeError:undefined is not a function”。 你得到上述错误的原因是,当你调用setTimeout()
时,实际上是调用window.setTimeout()
。 因此,在窗口对象的上下文中定义了一个传递给setTimeout()
的匿名函数,该函数没有clearBoard()
方法。
一个传统的,旧浏览器兼容的解决方案是简单地将您的 this 保存在一个变量,然后可以由闭包继承。 例如:
function testFunction () { this.clearLocalStorage(); var self = this; // save reference to 'this', while it's still this! this.timer = setTimeout(function(){ self.clearBoard(); }, 0); };
或者,在较新的浏览器中,可以使用bind()方法传递适当的引用:
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 开发者控制台中进行测试。
此外,如果您将值传递给超出范围的函数,也可能会发生这种情况。 许多函数只接受其输入值的特定范围的数字。 例如: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. TypeError: Cannot read property ‘length'
这是 Chrome 中发生的错误,因为读取未定义变量的长度属性。 您可以在 Chrome 开发者控制台中进行测试。
您通常会在数组中找到定义的长度,但是如果数组未初始化或者变量名称在另一个上下文中隐藏,则可能会遇到此错误。让我们用下面的例子来理解这个错误。
var testArray = ["Test"]; function testFunction(testArray) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); } } testFunction();
当你用参数声明一个函数时,这些参数变成了函数作用域内的本地参数。这意味着即使你函数外有名为 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. 用声明的数组调用该函数:
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”。
例如,在 Chrome 浏览器中:
如果测试对象不存在,错误将会抛出 “Uncaught TypeErrorUncaught TypeError: Cannot set property”。
10. ReferenceError: event is not defined
当您尝试访问未定义的变量或超出当前范围的变量时,会引发此错误。 您可以在 Chrome 浏览器中轻松测试。
如果在使用事件处理系统时遇到此错误,请确保使用传入的事件对象作为参数。像 IE 这样的旧浏览器提供了一个全局变量事件,但并不是所有浏览器都支持。像 jQuery 这样的库试图规范化这种行为。尽管如此,最好使用传入事件处理函数的函数。
function myFunction(event) { event = event.which || event.keyCode; if(event.keyCode===13){ alert(event.keyCode); } }
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
위 내용은 일상적인 자바스크립트 실수의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!