JavaScript is a scripting language used in webpages to add functionality and interactivity. For a beginner coming from a different programming language, JavaScript is quite easy to understand. With a few tutorials, you should be able to get started with it right away.
JavaScript是网页中用于添加功能和交互性的脚本语言 。 对于使用不同编程语言的初学者来说,JavaScript很容易理解。 通过一些教程,您应该可以立即开始使用它。
However, there are a few common mistakes that many beginner programmers make. In this article, we’ll address nine common mistakes (or bad practices) and their solutions to help you become a better JS developer.
但是,许多初学者会犯一些常见的错误。 在本文中,我们将解决9个常见错误(或不良做法)及其解决方案,以帮助您成为更好的JS开发人员。
混淆赋值(=)和相等(==,===)运算符 (Confusing the assignment (=) and equality (==, ===) operators)
Like its name implies, the assignment operator(=) is used to assign values to variables. Developers often confuse it with the equality operator.
顾名思义, 赋值运算符 (=)用于为变量赋值。 开发人员经常将其与相等运算符混淆。
Here's an example:
这是一个例子:
const name = "javascript";
if ((name = "nodejs")) {
console.log(name);
}
// output - nodejs
The name variable and ‘nodejs' string are not compared in this case. Instead, 'nodejs' is assigned to name and 'nodejs' is printed to the console.
在这种情况下,不比较名称变量和'nodejs'字符串。 相反,将“ nodejs”分配给名称,并将“ nodejs”打印到控制台。
In JavaScript, the double equal sign(==) and triple equal sign(===) are called comparison operators.
在JavaScript中,双等号(==)和三等号(===)称为比较运算符。
For the code above, this is the appropriate way of comparing values:
对于上面的代码,这是比较值的适当方法:
const name = "javascript";
if (name == "nodejs") {
console.log(name);
}
// no output
// OR
if (name === "nodejs") {
console.log(name);
}
// no output
The difference between these comparison operators is that the double equals performs a loose comparison while triple equals performs a strict comparison.
这些比较运算符之间的区别在于,double equals执行宽松的比较,而Triple equals执行严格的比较。
In a loose comparison, only the values are compared. But in a strict comparison, the values and datatype are compared.
在粗略的比较中,仅比较值。 但是在严格的比较中,将比较值和数据类型。
The following code explains it better:
以下代码对其进行了更好的解释:
const number = "1";
console.log(number == 1);
// true
console.log(number === 1);
// false
The variable number was assigned a string value of 1. When compared with 1 (of number type) using double equals, it returns true because both values are 1.
变量号的字符串值为1。与双精度等号的1(数字类型)比较时,它返回true,因为两个值均为1。
But when compared using triple equals, it returns false because each value has a different data type.
但是,当使用三元等于进行比较时,由于每个值具有不同的数据类型,因此它返回false。
期望回调是同步的 (Expecting callbacks to be synchronous)
Callbacks are one way that JavaScript handles asynchronous operations. Promises and async/await, however, are preferable methods for handling asynchronous operations because multiple callbacks lead to callback hell.
回调是JavaScript处理异步操作的一种方式。 但是,Promises和async / await是处理异步操作的首选方法,因为多个回调会导致回调hell 。
Callbacks are not synchronous. They are used as a function to be called after an operation when a delayed execution completes.
回调不是同步的 。 它们用作延迟执行完成后在操作后调用的函数。
An example is the global setTimeout
function which receives a callback function as its first argument and a duration (in ms) as a second argument like so:
一个例子是全球setTimeout
其接收回调函数作为像这样的第二参数的第一个参数和一个持续时间(毫秒)功能:
function callback() {
console.log("I am the first");
}
setTimeout(callback, 300);
console.log("I am the last");
// output
// I am the last
// I am the first
After 300 milliseconds, the callback function is called. But before it completes, the rest of the code runs. This is the reason why the last console.log was run first.
300毫秒后,将调用回调函数。 但在完成之前,其余代码将运行。 这就是为什么最后一个console.log首先运行的原因。
A common mistake developers make is to misinterpret callbacks as synchronous. For example, a callback which returns a value that would be used for other operations.
开发人员常犯的一个错误是将回调误解为同步。 例如,一个回调返回一个将用于其他操作的值。
Here's that mistake:
这是一个错误:
function addTwoNumbers() {
let firstNumber = 5;
let secondNumber;
setTimeout(function () {
secondNumber = 10;
}, 200);
console.log(firstNumber + secondNumber);
}
addTwoNumbers();
// NaN
NaN
is the output because secondNumber
is undefined. At the time of running firstNumber + secondNumber
, secondNumber
is still undefined because the setTimeout
function would execute the callback after 200ms
.
NaN
是输出,因为secondNumber
不确定。 在运行firstNumber + secondNumber
, secondNumber
仍未定义,因为setTimeout
函数将在200ms
后执行回调。
The best way to approach this is to execute the rest of the code in the callback function:
解决此问题的最佳方法是执行回调函数中的其余代码:
function addTwoNumbers() {
let firstNumber = 5;
let secondNumber;
setTimeout(function () {
secondNumber = 10;
console.log(firstNumber + secondNumber);
}, 200);
}
addTwoNumbers();
// 15
到错误引用this
(Wrong references to this
)
this
is a commonly misunderstood concept in JavaScript. To use this
in JavaScript, you really need to understand how it works because it operates a bit differently compared to other languages.
this
是一个常见的误解的概念在JavaScript中。 要在JavaScript中使用this
,您真的需要了解它的工作原理,因为它的运行方式与其他语言相比有所不同。
Here's an example of a common mistake when using this
:
下面是在使用一个常见的错误的例子this
:
const obj = {
name: "JavaScript",
printName: function () {
console.log(this.name);
},
printNameIn2Secs: function () {
setTimeout(function () {
console.log(this.name);
}, 2000);
},
};
obj.printName();
// JavaScript
obj.printNameIn2Secs();
// undefined
The first result is JavaScript
because this.name
correctly points to the object's name property. The second result is undefined
because this
has lost reference to the object's properties (including name).
第一个结果是JavaScript
因为this.name
正确指向对象的name属性。 第二个结果是undefined
,因为this
已经失去参考对象的属性(包括名称)。
This is because this
depends on the object calling the function which it lives in. There is a this
variable in every function but the object it points to is determined by the object calling it.
这是因为this
取决于调用它生活在该函数的对象。有一个this
变量在每一个功能,但它指向的是通过调用它的对象决定的对象。
The this
in obj.printName()
points directly to obj
. The this
in obj.printNameIn2Secs
points directly to obj
. But the this
in the callback function of setTimeout
does not point to any object because no object called it.
在this
在obj.printName()
直接指向obj
。 在this
在obj.printNameIn2Secs
直接指向obj
。 但是, this
在回调函数setTimeout
不指向任何对象,因为没有对象调用它。
For an object to have called setTimeout
, something like obj.setTimeout...
would be executed. Since there is no object calling that function, the default object (which is window
) is used.
对于一个对象,以呼吁setTimeout
,像obj.setTimeout...
会被处决。 由于没有对象调用该函数,因此使用默认对象(即window
)。
name
does not exist on window, resulting in undefined
.
name
上不存在窗口,导致undefined
。
The best ways to go about retaining the reference to this
in setTimeout
is to use bind
, call
, apply
or arrow functions (introduced in ES6). Unlike normal functions, arrow functions do not create their own this
.
到去保留提及的最佳途径this
在setTimeout
是使用bind
, call
, apply
或箭头功能(在ES6介绍)。 与普通函数不同,箭头函数不会创建自己的this
。
So, the following will retain its reference to this
:
因此,下面将保留其参考this
:
const obj = {
name: "JavaScript",
printName: function () {
console.log(this.name);
},
printNameIn2Secs: function () {
setTimeout(() => {
console.log(this.name);
}, 2000);
},
};
obj.printName();
// JavaScript
obj.printNameIn2Secs();
// JavaScript
忽略对象可变性 (Disregarding object mutability)
Unlike primitive data types like string, number and so on, in JavaScript objects are reference data types. For example, in key-value objects:
与字符串,数字等原始数据类型不同,JavaScript对象中的引用数据类型是。 例如,在键值对象中:
const obj1 = {
name: "JavaScript",
};
const obj2 = obj1;
obj2.name = "programming";
console.log(obj1.name);
// programming
obj1
and obj2
possess the same reference to the location in memory where the object is stored.
obj1
和obj2
具有在存储器中的相同的参考位置,其中所述对象被存储。
In arrays:
在数组中:
const arr1 = [2, 3, 4];
const arr2 = arr1;
arr2[0] = "javascript";
console.log(arr1);
// ['javascript', 3, 4]
A common mistake developers make is they disregard this nature of JavaScript and this results in unexpected errors. For instance, if 5 objects have the same reference to the same object, one of the object may interfere with the properties in a large-scale code base.
开发人员经常犯的一个错误是他们无视JavaScript的这种特性,这会导致意外错误。 例如,如果5个对象对同一对象的引用相同,则其中一个对象可能会干扰大规模代码库中的属性。
When this happens, any attempt to access the original properties would return undefined or possibly throw an error.
发生这种情况时,任何访问原始属性的尝试都将返回未定义或可能引发错误。
The best practice for this is to always create new references for new objects when you want to duplicate an object. To do this, the rest operator ( ...
introduced in ES6) is a perfect solution.
最佳做法是在要复制对象时始终为新对象创建新引用。 要做到这一点,其余的运营商( ...
在ES6介绍)是一个完美的解决方案。
For example, in key-value objects:
例如,在键值对象中:
const obj1 = {
name: "JavaScript",
};
const obj2 = { ...obj1 };
console.log(obj2);
// {name: 'JavaScript' }
obj2.name = "programming";
console.log(obj.name);
// 'JavaScript'
In arrays:
数组中:
const arr1 = [2, 3, 4];
const arr2 = [...arr1];
console.log(arr2);
// [2,3,4]
arr2[0] = "javascript";
console.log(arr1);
// [2, 3, 4]
将数组和对象保存到浏览器存储 (Saving arrays and objects to browser storage)
Sometimes, while working with JavaScript, developers may want to take advantage of the localStorage
for saving values. But a common mistake is trying to save arrays and objects as-is in the localStorage
. localStorage
only accepts strings.
有时,在使用JavaScript时,开发人员可能想利用localStorage
来保存值。 但是一个常见的错误是试图将数组和对象原样保存在localStorage
。 localStorage
仅接受字符串。
In an attempt to save objects, JavaScript converts the object to a string. The result is [Object Object]
for objects and a comma-separated string for array elements.
为了保存对象,JavaScript将对象转换为字符串。 结果是[Object Object]
和数组元素的逗号分隔字符串。
For example:
例如:
const obj = { name: "JavaScript" };
window.localStorage.setItem("test-object", obj);
console.log(window.localStorage.getItem("test-object"));
// [Object Object]
const arr = ["JavaScript", "programming", 45];
window.localStorage.setItem("test-array", arr);
console.log(window.localStorage.getItem("test-array"));
// JavaScript, programming, 45
When objects are saved like this, it becomes difficult to access them. For the object example, accessing the object like .name
would result in an error. This is because [Object Object]
is a string now, without a name
property.
这样保存对象时,将很难访问它们。 对于对象例如,访问对象等.name
将导致一个错误。 这是因为[Object Object]
现在是一个字符串,没有name
属性。
A better way to save objects and arrays in local storage is by using JSON.stringify
(for converting objects to strings) and JSON.parse
(for converting strings to objects). This way, accessing the objects becomes easy.
一种更好的方式来保存在本地存储对象和数组是通过使用JSON.stringify
(对于对象转换为字符串)和JSON.parse
(用于将字符串转换为对象)。 这样,访问对象变得容易。
The correct version of the code above would be:
上面的代码的正确版本是:
const obj = { name: "JavaScript" };
window.localStorage.setItem("test-object", JSON.stringify(obj));
const objInStorage = window.localStorage.getItem("test-object");
console.log(JSON.parse(objInStorage));
// {name: 'JavaScript'}
const arr = ["JavaScript", "programming", 45];
window.localStorage.setItem("test-array", JSON.stringify(arr));
const arrInStorage = window.localStorage.getItem("test-array");
console.log(JSON.parse(arrInStorage));
// JavaScript, programming, 45
不使用默认值 (Not using default values)
Setting default values in dynamic variables is a very good practice for preventing unexpected errors. Here's an example of a common mistake:
在动态变量中设置默认值是防止意外错误的一种很好的做法。 这是一个常见错误的例子:
function addTwoNumbers(a, b) {
console.log(a + b);
}
addTwoNumbers();
// NaN
The result is NaN
because a
is undefined
and b
is undefined
. By using default values, errors like this can be prevented. For example:
其结果是NaN
,因为a
是undefined
和b
是undefined
。 通过使用默认值,可以防止类似的错误。 例如:
function addTwoNumbers(a, b) {
if (!a) a = 0;
if (!b) b = 0;
console.log(a + b);
}
addTwoNumbers();
// 0
Alternatively, the default value feature introduced in ES6 can be used like so:
另外,可以像这样使用ES6中引入的默认值功能:
function addTwoNumbers(a = 0, b = 0) {
console.log(a + b);
}
addTwoNumbers();
// 0
This example, though minimal, emphasizes the importance of default values. Additionally, developers can provide errors or warning messages when expected values are not provided.
这个例子虽然很小,但强调了默认值的重要性。 此外,当未提供期望值时,开发人员可以提供错误或警告消息。
变量命名不正确 (Improper naming of variables)
Yes, developers still make this mistake. Naming is hard, but developers really have no choice. Comments are good practice in programming, and so is naming variables.
是的,开发人员仍然会犯此错误。 命名很困难,但是开发人员确实别无选择。 注释是编程的好习惯,命名变量也是如此。
For example:
例如:
function total(discount, p) {
return p * discount
}
The variable discount
is okay, but what about p
or total
? Total of what? A better practice for above would be:
变量discount
是好的,但对于p
或total
? 总共是什么? 更好的做法是:
function totalPrice(discount, price) {
return discount * price
}
Properly naming variables is important because a developer may never be the only developer on a codebase at a particular time or in the future.
正确命名变量很重要,因为在特定的时间或将来,开发人员可能永远不是代码库中唯一的开发人员。
Naming variables properly will allow contributors easily understand how a project works.
正确命名变量将使贡献者可以轻松地了解项目的工作方式。
检查布尔值 (Check-up for boolean values)
const isRaining = false
if(isRaining) {
console.log('It is raining')
} else {
console.log('It is not raining')
}
// It is not raining
It is common practice to check boolean values as seen in the above code. While this is okay, errors set in when testing some values.
如上面的代码所示,检查布尔值是一种常见的做法。 虽然可以,但是在测试某些值时会设置错误。
In JavaScript, a loose comparison of 0
and false
returns true
and 1
and true
returns true
. This means that if isRaining
was 1
, isRaining
would be true
.
在JavaScript中,一个松散的比较0
和false
返回true
和1
和true
返回true
。 这意味着如果isRaining
为1
,则isRaining
为true
。
This is also a mistake often made in objects. For example:
这也是对象经常犯的错误。 例如:
const obj = {
name: 'JavaScript',
number: 0
}
if(obj.number) {
console.log('number property exists')
} else {
console.log('number property does not exist')
}
// number property does not exist
Although the number
property exists, obj.number
returns 0
, which is a falsy
value, therefore the else
block is executed.
虽然number
属性存在, obj.number
返回0
,这是一个falsy
值,因此, else
块被执行。
So unless you're sure of the range of values that would be used, boolean values and properties in objects should be tested like this:
因此,除非您确定要使用的值的范围,否则应该像这样测试对象中的布尔值和属性:
if(a === false)...
if(object.hasOwnProperty(property))...
令人困惑的加法和串联 (Confusing Addition and Concatenation)
The plus sign (+)
has two functions in JavaScript: addition and concatenation. Addition is for numbers and Concatenation is for strings. Some developers often misuse this operator.
加号(+)
在JavaScript中具有两个功能:加法和串联。 加号用于数字,串联用于字符串。 一些开发人员经常滥用此运算符。
For example:
例如:
const num1 = 30;
const num2 = "20";
const num3 = 30;
const word1 = "Java"
const word2 = "Script"
console.log(num1 + num2);
// 3020
console.log(num1 + num3);
// 60
console.log(word1 + word2);
// JavaScript
When adding strings and numbers, JavaScript converts the numbers to strings, and concatenates all values. For addition of numbers, a mathematical operation is performed.
在添加字符串和数字时,JavaScript将数字转换为字符串,并连接所有值。 对于数字加法,将执行数学运算。
结论 (Conclusion)
There are, of course, more mistakes (some trivial, some serious) than the ones listed above. So just make sure you stay up to date with developments in the language.
当然,比上面列出的错误更多(一些琐碎的错误,一些严重的错误)。 因此,只要确保您了解最新的语言发展情况即可。
Studying and avoiding these mistakes will help you build better and more reliable web applications and tools.
研究并避免这些错误将帮助您构建更好,更可靠的Web应用程序和工具。
翻译自: https://www.freecodecamp.org/news/nine-most-common-mistakes-developers-make-in-javascript/