Core points
if
or for
blocks are promoted to the top of the function or global scope. with
statement (dynamic scope variable) in JavaScript has been deprecated because it can cause runtime errors and is confusing in nature. table.sp_table { width: 100%; border-collapse: collapse; border-spacing: 0; } table.sp_table td, table.sp_table th { border: solid 1px #ccc; padding: 10px; line-height: 1.5; text-align: center; width: 20%; } table.sp_table tr td:first-child { font-weight: bold; }
JavaScript can be a deceptive language, it can be painful because it is not 100% consistent. It is known that it does have some bad parts of i.e. confusing or redundant functions that should be avoided: the infamous statements, implicit global variables, and comparison behavior exceptions are probably most famous of. JavaScript is one of the most successful flame generators in history! Apart from the flaws it has (partially solved in the new ECMAScript specification), most programmers hate JavaScript for two reasons: - DOM, they mistakenly think it is equivalent to the JavaScript language, which has a very bad API . - They moved from languages like C and Java to JavaScript. The syntax of JavaScript fooled them into believing that it worked the same way as those imperative languages. This misunderstanding can lead to confusion, frustration, and error.
with
This is why, usually, JavaScript has a worse reputation than it deserves. In my career, I have noticed some patterns: Most developers with Java or C/C backgrounds think the same language features in JavaScript, and they are completely different. This article collects the most troublesome language features, comparing Java approaches to JavaScript approaches to show differences and highlighting best practices in JavaScript.
Most developers start using JavaScript because they are forced to do so, and almost all developers start writing code before they spend time learning the language. Each such developer has been fooled by JavaScript scope at least once. Because JavaScript's syntax is very similar to that of C-series (intentionally), separating the body of functions, if and for with braces, people would reasonably expect the lexical block-level scope. Unfortunately, this is not the case. First, in JavaScript, variable scope is determined by functions, not by brackets. In other words, if and for bodies do not create new scopes, and variables declared in their bodies are actually promoted, i.e. created at the beginning of the innermost function that declares it, otherwise global scope. Second, the existence of Another complexity is caused by the implicit global scope of variable assignments for (implicitly) not declared in the Elevation is a simplified method for explaining actual behavior of statements. The promoted variable is declared at the beginning of the function it contains and is initialized to What is the value you expect to print to the console? Would you be surprised by the following output? In the Although you may expect differently, the function expressions (they are actually variables) and function declarations will also be promoted. This topic needs to be handled more carefully than I did here, but in short, function declarations behave roughly the same as function expressions, except that their declarations are moved to the beginning of their scope. Consider the following example showing the behavior of function declaration:
has a field named statement. Consider the following code: inside the body of and Function Another very misunderstood feature of JavaScript is that functions, especially in imperative programming languages such as Java, there is no concept of functions. In fact, JavaScript is a functional programming language. Well, it's not a pure functional programming language like Haskell -- it still has an imperative style after all, and encourages mutability rather than just allowing, just like Scala. However, JavaScript can be used as a purely functional programming language, with function calls without any side effects. Functions in JavaScript can be treated like any other type, such as String and Number: they can be stored in variables, passed as parameters to functions, returned by functions, and stored in an array. Functions can also have properties and can be changed dynamically, because... A very surprising fact for most JavaScript newbies is that functions are actually objects. In JavaScript, each function is actually a Function object. Function constructor creates a new Function object: This is almost equivalent to: I say they are almost equivalent because using the Function constructor is less efficient, produces anonymous functions and does not create closures for their creation context. Function objects are always created in the global scope. Function (the type of function) is built on Object. This can be easily seen by checking any function you declare: This means that the function can and does have properties. Some of these are assigned to functions at creation time, such as names or lengths. These properties return the name and number of parameters in the function definition, respectively. Consider the following example: But you can even set new properties for any function yourself: The following table describes functions in Java, Python, and JavaScript: Closing If I were asked to choose my favorite JavaScript feature, I would choose the closure without hesitation. JavaScript is the first mainstream programming language to introduce closures. As you know, Java and Python have long had weakened versions of closures from which you can only read (some) values of enclosed scopes. For example, in Java, anonymous inner classes provide closure-like functionality with certain limitations. For example, the final local variables can only be used in their scope—more precisely, their values can be read. JavaScript allows full access to external scoped variables and functions. They can be read, written, and even hidden by local definitions if needed: you can see examples of all these cases in the Scope section. What's more interesting is that the function created in the closure remembers the environment in which it was created. By combining closures and function nesting, you can have external functions return internal functions without executing them. Additionally, you can make local variables of external functions last long in the closure of their internal functions, even if the execution of the function that declares them has ended. This is a very powerful feature, but it also has its drawbacks, as it is a common cause of memory leaks in JavaScript applications. Some examples will illustrate these concepts: The above To make it more interesting, we can update the External function parameters are also saved in the closure, so we don't need to declare local variables this time. Each call Using this pattern, using closures, we can create a wrapper for the property name and use our own setters and getters. ES5 makes this much easier because you can create objects for their properties using getters and setters and control access to the properties themselves at the finest grain. The following table describes closures in Java, Python, and JavaScript: Conclusion In this article, I introduce three features of JavaScript that are often misunderstood by developers from different languages, especially Java and C. In particular, we discuss concepts such as scope, promotion, functions and closures. If you want to dig into these topics, here is a list of some articles you can read: - Scope in JavaScript - Function Declarations and Function Expressions - In JavaScript, "==" and "===" are comparison operators, but they work differently. The “==” operator is called the loose equality operator. It compares whether the two values are equal after performing any necessary type conversion. This means that if you compare a number to a string with a numeric literal, it will return true. For example, "5" == 5 will return true. On the other hand, "===" is a strict equality operator. It does not perform type conversion, so if the two value types are different, it returns false. For example, "5" === 5 will return false because one is a string and the other is a number. In JavaScript, null and undefined are special values that do not exist. However, they are used slightly differently. Undefined means that the variable has been declared but has not been assigned yet. null, on the other hand, is an assignment value, indicating no value or no object. It implies that the value of the variable does not exist, while undefined means that the variable itself does not exist. Elevation is a mechanism in JavaScript that moves variables and function declarations to the top of its included scope during the compilation phase. This means you can use variables and functions before declaring them. However, it should be noted that only declarations will be promoted, and initialization will not be promoted. If the variable is declared and initialized after using it, the value will be undefined. In JavaScript, variables can be global or local variables. Global variables are variables declared outside any function, or variables declared with the "var" keyword at all. It can be accessed from any function in the script. Local variables, on the other hand, are variables declared within a function using the "var" keyword. It can only be accessed within the functions it declares. The "this" keyword in JavaScript is a special keyword, which refers to the context of calling a function. Its value depends on how the function is called. In a method, "this" refers to the owner object. Individually, "this" refers to a global object. In a function, "this" refers to a global object. In an event, "this" refers to the element that receives the event. A closure in JavaScript is a function that can access its own scope, the scope and global scope of external functions, as well as access function parameters and variables. This allows the function to access variables in the returned external function, keeping the variables in memory, and allows data privacy and function factories. In JavaScript, functions can be defined in many ways, two of which are function declarations and function expressions. A function declaration defines a named function, and the declaration is promoted, allowing the function to be used before defining it. A function expression defines a function in an expression and is not promoted, meaning it cannot be used before its definition. "let", "var" and "const" are all used to declare variables in JavaScript, but they have different scope rules. "var" is function scoped, meaning it can only be used within the functions it declares. "let" and "const" are block-scoped, meaning they can only be used within the blocks they declare. The difference between "let" and "const" is that "let" allows you to reassign variables, while "const" does not. In JavaScript, objects and arrays are used to store data, but they are stored in different ways. An object is a collection of properties, where each property is a key-value pair. A key is a string and a value can be any data type. An array is a special type of object that represents a list of items. Keys are numeric indexes, and values can be of any data type. In JavaScript, a function is a block of code designed to perform a specific task, and it is a separate entity that can be used as needed. On the other hand, a method is a function associated with an object, or in other words, a method is a property of an object as a function. Methods are defined in the same way as ordinary functions, except that they must be assigned values as attributes of objects. The above is the detailed content of Three JavaScript Quirks That Java/C Developers Should Know. For more information, please follow other related articles on the PHP Chinese website!with
statements forces JavaScript scope to become dynamic and cannot be determined until runtime. You may not be surprised to hear that the use of with
statements has been deprecated: JavaScript without with
will actually be a lexical scoped language, i.e., the scope can be completely determined by looking at the code. Formal, in JavaScript, there are four ways to get into scope: - Language Definition: By default, all scopes contain names this
and arguments
. - Formal Parameters: The scope of any (formal) parameter declared for a function belongs to the body of the function. - Function declaration. - Variable declaration. var
keyword. This madness is combined with implicit assignment of global scope to this
references when calling a function without explicit binding (more on the next section). Before digging into the details, let's clarify what good patterns can be used to avoid confusion: use strict pattern ('use strict';
), and move all variables and function declarations to the top of each function; Avoid declaring variables within for and if blocks, and declaration of functions within those blocks (this is beyond the scope of this article for different reasons). Elevation
undefined
. The assignment then takes place in the actual line of the original declaration. Please see the following example: table.sp_table {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
}
table.sp_table td, table.sp_table th {
border: solid 1px #ccc;
padding: 10px;
line-height: 1.5;
text-align: center;
width: 20%;
}
table.sp_table tr td:first-child {
font-weight: bold;
}
table.sp_table {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
}
table.sp_table td, table.sp_table th {
border: solid 1px #ccc;
padding: 10px;
line-height: 1.5;
text-align: center;
width: 20%;
}
table.sp_table tr td:first-child {
font-weight: bold;
}
if
block, the var
statement does not declare a local copy of the variable i
, but rather overwrites the previously declared variable. Note that the first console.log
statement prints the actual value of the variable i
, which is initialized to undefined
. You can test it by using the "use strict";
directive as the first line in the function. In strict mode, variables must be declared before using them, but you can check that the JavaScript engine does not report errors because of the declaration. By the way, please note that you won't report an error because of redeclaring var
: If you want to catch such errors, you'd better use a linter like JSHint or JSLint to handle your code. Now let's look at another example to highlight another error-prone usage of variable declarations: function myFunction() {
console.log(i);
var i = 0;
console.log(i);
if (true) {
var i = 5;
console.log(i);
}
console.log(i);
}
if
body is executed because a local copy of the variable named test()
is declared inside the notNull
function, and it is promoted by . Type mandatory works here too.
Elevation is not only applicable to variables, but <code>undefined
0
5
5</code>
var notNull = 1;
function test() {
if (!notNull) {
console.log("Null-ish, so far", notNull);
for(var notNull = 10; notNull < 20; notNull++) {
//..
}
console.log("Now it's not null", notNull);
}
console.log(notNull);
}
The following example shows a situation where the scope can only be determined at runtime: function foo() {
// 函数声明
function bar() {
return 3;
}
return bar();
// 此函数声明将被提升并覆盖之前的声明
function bar() {
return 8;
}
}
y
, the function x
will return foo()
, otherwise 123 will be returned. This coding practice may be the source of runtime errors, so it is highly recommended that you avoid the y.x
statement. with
The
ECMAScript 6 specification will add a fifth method to add block-level scope: the let
function foo() {
// 函数表达式
var bar = function() {
return 3;
};
return bar();
// 变量 bar 已经存在,并且永远不会到达此代码
var bar = function() {
return 8;
};
}
if
declares let
to create a new variable local to the i
block. As a non-standard alternative, the if
block can be declared as follows: let
function foo(y) {
var x = 123;
with(y) {
return x;
}
}
i
only exist within the block. At the time of writing, support for j
is limited, even for Chrome. let
The following table summarizes the scope in different languages:
特性
Java
Python
JavaScript
警告
作用域
词法(块)
词法(函数、类或模块)
是
它与 Java 或 C 的工作方式大相径庭
块作用域
是
否
let
关键字(ES6)再一次警告:这不是 Java!
提升
不可能!
否
是
仅提升变量和函数表达式的声明。对于函数声明,也会提升定义
函数
作为内置类型
是
是
回调/命令模式对象(或 Java 8 的 lambda)
动态创建
否
否
eval
– Function
对象eval
存在安全问题,Function
对象可能无法按预期工作
属性
否
否
可以具有属性
无法限制对函数属性的访问
闭包
弱化,只读,在匿名内部类中
弱化,只读,在嵌套的 def 中
是
内存泄漏
First Class Citizen
Object
table.sp_table {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
}
table.sp_table td, table.sp_table th {
border: solid 1px #ccc;
padding: 10px;
line-height: 1.5;
text-align: center;
width: 20%;
}
table.sp_table tr td:first-child {
font-weight: bold;
}
function myFunction() {
console.log(i);
var i = 0;
console.log(i);
if (true) {
var i = 5;
console.log(i);
}
console.log(i);
}
<code>undefined
0
5
5</code>
var notNull = 1;
function test() {
if (!notNull) {
console.log("Null-ish, so far", notNull);
for(var notNull = 10; notNull < 20; notNull++) {
//..
}
console.log("Now it's not null", notNull);
}
console.log(notNull);
}
function foo() {
// 函数声明
function bar() {
return 3;
}
return bar();
// 此函数声明将被提升并覆盖之前的声明
function bar() {
return 8;
}
}
Function summary
特性
Java
Python
JavaScript
警告
函数作为内置类型
lambda,Java 8
是
是
回调/命令模式对象(或 Java 8 的 lambda)
动态创建
否
否
eval
– Function
对象eval
存在安全问题,Function
对象可能无法按预期工作
属性
否
否
可以具有属性
无法限制对函数属性的访问
table.sp_table {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
}
table.sp_table td, table.sp_table th {
border: solid 1px #ccc;
padding: 10px;
line-height: 1.5;
text-align: center;
width: 20%;
}
table.sp_table tr td:first-child {
font-weight: bold;
}
makeCounter()
function creates and returns another function that tracks the environment it created. Although the execution of counter
is finished when the variable makeCounter()
is allocated, the local variable i
remains in the closure of displayCounter
and can therefore be accessed inside its body. If we want to run makeCounter
again, it will create a new closure where the entry for i
is different: function myFunction() {
console.log(i);
var i = 0;
console.log(i);
if (true) {
var i = 5;
console.log(i);
}
console.log(i);
}
makeCounter()
function to accept a parameter: <code>undefined
0
5
5</code>
makeCounter()
will remember the initial value we set and continue counting. Closures are essential for many basic JavaScript patterns: namespaces, modules, private variables, memory are just the most famous. For example, let's see how to simulate private variables for an object: var notNull = 1;
function test() {
if (!notNull) {
console.log("Null-ish, so far", notNull);
for(var notNull = 10; notNull < 20; notNull++) {
//..
}
console.log("Now it's not null", notNull);
}
console.log(notNull);
}
Closing summary
特性
Java
Python
JavaScript
警告
闭包
弱化,只读,在匿名内部类中
弱化,只读,在嵌套的 def 中
是
内存泄漏
记忆化模式
必须使用共享对象
可以使用列表或字典
是
最好使用惰性求值
命名空间/模块模式
不需要
不需要
是
私有属性模式
不需要
不可能
是
可能令人困惑
let
Statements and let
Blocks FAQs about JavaScript Features (FAQ)
What is the difference between "==" and "===" in JavaScript?
Why does JavaScript have both null and undefined?
What is the improvement in JavaScript?
What is the difference between global variables and local variables in JavaScript?
What is the "this" keyword in JavaScript?
What are closures in JavaScript?
What is the difference between function declarations and function expressions in JavaScript?
What is the difference between "let", "var" and "const" in JavaScript?
What is the difference between objects and arrays in JavaScript?
What is the difference between methods and functions in JavaScript?