基本语法
变量和块作用域
变量有两种定义方式。使用定义变量会let引入一个块范围变量,其值可以更改。使用定义常量(即,其值无法更改的变量)const。在JavaScript中,变量的类型是自动推断的。
在下面,我们创建两个变量并将其输出到控制台日志。
let name = 'B. Eich';
let yearOfBirth = 1961;
name = 'Brendan Eich';
console.log(name + ' was born in ' + yearOfBirth + '.');
程序的输出如下。
$ deno run app.js
Brendan Eich was born in 1961.
如果我们将名称声明为常量,则无法更改名称。
const name = 'B. Eich';
let yearOfBirth = 1961;
name = 'Brendan Eich';
console.log(name + ' was born in ' + yearOfBirth + '.');
当我们运行上面的程序时,我们看到一个错误。该错误表明我们正在尝试为常数变量赋值(这是不可能的)。
$ deno run app.js
error: Uncaught TypeError: Assignment to constant variable.
name = 'Brendan Eich';
^
at file:///path-to-file/app.js:4:6
两者let并const用来定义块范围的变量。从广义上讲,块是指已使用大括号定义的区域,例如{此文本在块内}。块作用域定义意味着变量仅存在于已定义变量的块内(以及嵌套在该区域内的块内)。
在以下示例中,变量name在块内定义,在块内打印,然后在块外打印。
{
const name = 'B. Eich';
console.log(name);
}
console.log(name);
由于变量是块作用域的,因此记录变量在块内起作用,但不在块外起作用。当我们尝试记录不存在的变量时,我们看到一个错误variable is not defined。
$ deno run app.js
B. Eich
error: Uncaught ReferenceError: name is not defined
console.log(name);
^
at file:///path-to-file/app.js:6:13
另一方面,如前所述,可以在定义了变量的块内的任何位置(也可以在嵌套块内)访问变量。
{
const name = 'B. Eich';
console.log(name);
{
console.log(name);
}
}
$ deno run app.js
B. Eich
B. Eich
模板文字
编写使用变量作为输出一部分或者更广泛地使用字符串的代码的代码是通过模板文字完成的。模板文字是可以直接将变量值嵌入其中的字符串。模板文字以反引号 `` 开头和结尾。使用美元符号和大括号将变量嵌入到模板文字中-变量名称添加在大括号$ {brackets}中。
以下示例显示了如何使用模板文字。
const name = 'Brendan Eich';
const yearOfBirth = 1961;
console.log(`${name} was born in ${yearOfBirth}.`);
$ deno run app.js
Brendan Eich was born in 1961.
函数
使用箭头语法定义函数。创建函数时,我们定义一个常数变量,该常数存储函数代码,之后可以调用。以下示例定义了一个名为hello的函数,该函数在调用时记录message Hello world!。在示例中,函数在定义后被调用。
const hello = () => {
console.log('Hello world!');
}
hello();
$ deno run app.js
Hello world!
如果函数具有参数,则将参数值添加到括号中。在下面的示例中,我们定义一个函数greet,该函数以名称作为参数。然后Hello name!,该函数记录消息,该消息name已用参数的值替换。
const greet = (name) => {
console.log(`Hello ${name}!`);
}
greet('Brendan Eich');
运行该功能时,输出如下。
$ deno run app.js
Hello Brendan Eich!
如果未传递参数值,则该参数的值将不确定。
const greet = (name) => {
console.log(`Hello ${name}!`);
}
greet();
$ deno run app.js
Hello undefined!
多个参数使用逗号分隔。另外,可以在参数定义中设置默认参数值。下面的示例创建该greet函数的另一个版本,该版本具有两个带有默认值的参数。在不设置参数的情况下调用该函数将产生输出Hello John Doe!,而参数值也可以如下例所示进行设置。
const greet = (greeting = 'Hello', name = 'John Doe') => {
console.log(`${greeting} ${name}!`);
}
greet();
greet('Oh hi');
greet('Hello', 'Brendan Eich');
$ deno run app.js
Hello John Doe!
Oh hi John Doe!
Hello Brendan Eich!
从函数返回发生在函数末尾或显式使用该return语句。如果在return语句后添加了值或变量,则该值由函数返回。下面的示例显示一个返回问候语的函数。
const greeting = (who = 'John Doe') => {
return `Hello ${who}!`;
}
console.log(greeting('you'));
$ deno run app.js
Hello you!
如果某个函数将在另一个文件中使用,则可以从创建该函数的文件中将其导出。导出功能可以通过两种方式完成。可以将单个功能导出为默认导出,这可以使用完成export default functionName;。可以将多个功能导出为用大括号括起来的逗号分隔列表。以下示例显示了两种方式。
const greeting = (who = 'John Doe') => {
return `Hello ${who}!`;
}
export default greeting;
或者:
const hello = () => {
console.log('Hello world!');
}
const greeting = (who = 'John Doe') => {
return `Hello ${who}!`;
}
export {hello, greeting};
在另一个文件里面import:
import greeting from "./app.js";
console.log(greeting());
import {hello, greeting} from "./app.js";
hello();
console.log(greeting());
数组和对象
用于存储数据的最常用对象是数组和对象,可以将它们视为类似于列表和映射(尽管它们并不相同)。
数组
下面的示例定义一个具有两个值的数组,然后将其记录下来。用方括号和索引号完成索引数组(索引始终从零开始)。
const array = ['One', 'Two'];
console.log(array);
console.log(array[0]);
console.log(array[1]);
$ deno run app.js
["One", "Two"]
One
Two
添加/删除元素
使用push-方法可以将值添加到数组中,该方法会将作为参数给出的值添加到列表的末尾。使用shift删除第一个值和pop删除最后一个值。
const array = [];
array.push('One');
array.push('Two');
array.push('Three');
console.log(array);
array.shift();
console.log(array);
array.pop();
console.log(array);
$ deno run app.js
["One", "Two", "Three"]
["Two", "Three"]
["Two"]
可以使用多种方式遍历数组中的值。下面显示了我们将依赖的两种方法。
const array = ['One', 'Two', 'Three'];
for (const value of array) {
console.log(value);
}
console.log('**');
array.forEach((value) => {
console.log(value);
});
$ deno run app.js
One
Two
Three
**
One
Two
Three
对象 Object
可以将对象视为映射,其中每个键都引用一个值。以下示例定义了一个名为的对象person,该对象具有两个键name和yearOfBirth。name的值是Brendan Eich,yearOfBirth的值是1961。
const person = {
name: 'Brendan Eich',
yearOfBirth: 1961
};
如上所示,对象定义以大括号开头,以大括号结尾。使用中定义对象中的键值对name: value。每个键值对均以逗号分隔。
可以通过两种方式来访问变量中键的值,这两种方式都将在下面显示。
const person = {
name: 'Brendan Eich',
yearOfBirth: 1961
};
console.log(person.name);
console.log(person['name']);
$ deno run app.js
Brendan Eich
Brendan Eich
可以使用赋值运算符和访问器将值添加到已创建的对象中。在以下示例中,在创建person-object之后,我们inventorOf向person添加了一个字段。
const person = {
name: 'Brendan Eich',
yearOfBirth: 1961
};
person.inventorOf = 'Mocha';
console.log(person.name);
console.log(person.inventorOf);
$ deno run app.js
Brendan Eich
Mocha
可以按以下步骤遍历对象的所有键值对。
const person = {
name: 'Brendan Eich',
yearOfBirth: 1961
};
for (const [key, value] of Object.entries(person)) {
console.log(`${key}: ${value}`);
}
$ deno run app.js
name: Brendan Eich
yearOfBirth: 1961
条件语句
使用三个等号来比较JavaScript中的两个值。声明a === b比较的数值a和b。如果a和b相同,则比较结果为true,如果a和b不相同,则比较结果为false。
条件语句通常与比较结合使用,其中根据比较的输出执行一些代码。以下示例显示了一个简单的-if - else结构,其中打印的消息取决于比较。
let a = '5';
let b = '6';
if (a === b) {
console.log('The same');
} else {
console.log('Not the same');
}
a = '6';
if (a === b) {
console.log('The same');
} else {
console.log('Not the same');
}
$ deno run app.js
Not the same
The same
JavaScript具有许多其他编程语言中相同的比较运算符和逻辑运算符。
循环
我们之前已经简要地研究了循环。数组和对象的主要循环结构如下。
const array = ['One', 'Two', 'Three'];
for (const value of array) {
console.log(value);
}
console.log('**');
array.forEach((value) => {
console.log(value);
});
console.log('**');
const person = {
name: 'Brendan Eich',
yearOfBirth: 1961
};
for (const [key, value] of Object.entries(person)) {
console.log(`${key}: ${value}`);
}
$ deno run app.js
One
Two
Three
**
One
Two
Three
**
name: Brendan Eich
yearOfBirth: 1961
同步和异步函数
JavaScript具有同步和异步函数。同步函数和异步函数之间的区别在于,当从程序代码中调用同步函数时,从调用函数的位置开始执行代码,直到同步函数完成执行为止。对于异步函数,调用异步函数的代码执行不需要等待,而是可以继续执行。
异步函数是使用async参数列表之前的关键字定义的。以下是异步函数的示例,该函数等待指定的秒数,然后将消息记录到控制台。
const waitAndPrint = async(message, seconds) => {
await new Promise(resolve => setTimeout(resolve, seconds * 1000));
console.log(message);
}
waitAndPrint('First call!', 3);
waitAndPrint('Second call!', 2);
waitAndPrint('Third call!', 1);
现在,当我们运行程序时,我们看到以下输出。第一行显示大约需要一秒钟,第二行显示大约需要两秒钟,第三行显示大约需要三秒钟。
$ deno run app.js
Third call!
Second call!
First call!
现在,如果我们想在程序中等待异步函数完成执行,则await在调用函数时必须使用关键字。在下面的示例中,我们等待第一个waitAndPrint函数调用完成,然后再继续执行程序。
const waitAndPrint = async(message, seconds) => {
await new Promise(resolve => setTimeout(resolve, seconds * 1000));
console.log(message);
}
await waitAndPrint('First call!', 3);
waitAndPrint('Second call!', 2);
waitAndPrint('Third call!', 1);
现在,该程序的输出如下。
$ deno run app.js
First call!
Third call!
Second call!
同样,我们可以依次等待每个通话结束,然后再转到下一个通话。
const waitAndPrint = async(message, seconds) => {
await new Promise(resolve => setTimeout(resolve, seconds * 1000));
console.log(message);
}
await waitAndPrint('First call!', 3);
await waitAndPrint('Second call!', 2);
await waitAndPrint('Third call!', 1);
现在,该程序的输出如下。
$ deno run app.js
First call!
Second call!
Third call!
关于JavaScript的同步和异步(转载):
深入理解JavaScript的执行机制(同步和异步)
js中的同步和异步的个人理解