MDN速刷 JS JS指南篇——简介&语法和数据结构

开始之前

把博客本地文件夹放在了 OneDrive 里,炸了…

死活推不上去了,修复半天。

现在心情如图:

mmp

开始

简介

JavaScript 和 ECMAScript 规范

JavaScript 的标准化组织是 ECMA

ECMAScript 规范并没有描述文档对象模型(DOM),该模型由 万维网联盟(W3C) 制定

JavaScript 入门

Web 控制台(Web Console)

控制台写了个小函数调用了一下…

语法和数据类型

基础

JavaScript 取分大小写,使用 Unicode 字符

所以可以用 emoji 做变量名吗?

let 🥑 = "Avocado";
console.log(🥑);

试了试,不行…

试试中文

let 牛油果 = 'Avocado';
console.log(牛油果);

可以!!!

JS(JavaScript)中指令被称为语句。

Javascript 源码从左往右被扫描并转换成一系列由 token 、控制字符、行终止符、注释和空白字符组成的输入元素。空白字符指的是空格、制表符和换行符等。

声明

JavaScript 有三种声明方式。

  • var 声明一个变量,可选初始化一个值。
  • let 声明一个块作用域的局部变量,可选初始化一个值。
  • const 声明一个块作用域的只读常量。

变量

在应用程序中,使用变量来作为值的符号名。变量的名字又叫做标识符,其需要遵守一定的规则。

一个 JavaScript 标识符必须以字母、下划线(_)或者美元符号($)开头;后续的字符也可以是数字(0-9)。因为 JavaScript 语言是区分大小写的,所以字母可以是从“A”到“Z”的大写字母和从“a”到“z”的小写字母。

声明变量

你可以用以下三种方式声明变量:

  • 使用关键词 var 。例如 var x = 42。这个语法可以用来声明局部变量和全局变量。

  • 直接赋值。例如 x = 42。在函数外使用这种形式赋值,会产生一个全局变量。在严格模式下会产生错误。因此你不应该使用这种方式来声明变量。

  • 使用关键词 let 。例如 let y = 13。这个语法可以用来声明块作用域的局部变量。参考下方变量的作用域(Variable scope) 。

变量求值

用 var 或 let 语句声明的变量,如果没有赋初始值,则其值为 undefined 。

如果访问一个未声明的变量会导致抛出一个引用错误(ReferenceError)异常:

undefined 在布尔类型环境中,会被当做 false.

数值类型环境中,undefined 会被作为 NaN

null 在数值类型环境中会被当做 0 来对待,在布尔类型中会被作为 false

变量的作用域

在函数之外声明的变量,叫做全局变量,因为它可被当前文档中的任何其他代码所访问。在函数内部声明的变量,叫做局部变量,因为它只能在当前函数的内部访问。

ECMAScript 6 之前的 JavaScript 没有语句块作用域;相反,语句块中声明的变量将成为语句块所在函数(或全局作用域)的局部变量。

if (true) {
  var x = 5;
}
console.log(x); // 5

如果使用 ECMAScript 6 中的 let 声明,上述行为将发生变化。

if (true) {
  let y = 5;
}
console.log(y); // ReferenceError: y 没有被声明

变量提升

JavaScript 变量的另一个不同寻常的地方是,你可以先使用变量稍后再声明变量而不会引发异常。这一概念称为变量提升;JavaScript 变量感觉上是被“提升”或移到了函数或语句的最前面。但是,提升后的变量将返回 undefined 值。因此在使用或引用某个变量之后进行声明和初始化操作,这个被提升的变量仍将返回 undefined 值。

/**
 * 例子1
 */
console.log(x === undefined); // true
var x = 3;

/**
 * 例子2
 */
// will return a value of undefined
var myvar = 'my value';

(function () {
  console.log(myvar); // undefined
  var myvar = 'local value';
})();

等同于

/**
 * 例子1
 */
var x;
console.log(x === undefined); // true
x = 3;

/**
 * 例子2
 */
var myvar = 'my value';

(function () {
  var myvar;
  console.log(myvar); // undefined
  myvar = 'local value';
})();

由于存在变量提升,一个函数中所有的 var 语句应尽可能地放在接近函数顶部的地方。这个习惯将大大提升代码的清晰度。

在 ECMAScript 6 中,let(const)同样会被提升变量到代码块的顶部但是不会被赋予初始值。在变量声明之前引用这个变量,将抛出引用错误(ReferenceError)。这个变量将从代码块一开始的时候就处在一个“暂时性死区”,直到这个变量被声明为止。

函数提升

对于函数来说,只有函数声明会被提升到顶部,而函数表达式不会被提升。

/* 函数声明 */

foo(); // "bar"

function foo() {
  console.log('bar');
}

/* 函数表达式 */

baz(); // 类型错误:baz 不是一个函数

var baz = function () {
  console.log('bar2');
};

全局变量

实际上,全局变量是全局对象的属性。

在网页中,(译注:缺省的)全局对象是 window ,所以你可以用形如 window.variable 的语法来设置和访问全局变量。

因此,你可以通过指定 window 或 frame 的名字,在当前 window 或 frame 访问另一个 window 或 frame 中声明的变量。例如,在文档里声明一个叫 phoneNumber 的变量,那么你就可以在子框架里使用 parent.phoneNumber 的方式来引用它。

常量

你可以用关键字 const 创建一个只读的常量。常量标识符的命名规则和变量相同:必须以字母、下划线(_)或美元符号($)开头并可以包含有字母、数字或下划线。

常量不可以通过重新赋值改变其值,也不可以在代码运行时重新声明。它必须被初始化为某个值。

常量的作用域规则与 let 块级作用域变量相同。若省略 const 关键字,则该标识符将被视为变量。

在同一作用域中,不能使用与变量名或函数名相同的名字来命名常量。例如:

// 这会造成错误
function f() {}
const f = 5;

// 这也会造成错误
function f() {
  const g = 5;
  var g;

  //语句
}

然而,对象属性被赋值为常量是不受保护的,所以下面的语句执行时不会产生错误。

const MY_OBJECT = { key: 'value' };
MY_OBJECT.key = 'otherValue';

同样的,数组的被定义为常量也是不受保护的,所以下面的语句执行时也不会产生错误。

const MY_ARRAY = ['HTML', 'CSS'];
MY_ARRAY.push('JAVASCRIPT');
console.log(MY_ARRAY); //logs ['HTML','CSS','JAVASCRIPT'];

数据结构和类型

数据类型

最新的 ECMAScript 标准定义了 8 种数据类型:

七种基本类型

  • 布尔值
  • null
  • undefined
  • 数字 Number
  • 任意精度的整数 BigInt
  • 字符串 String
  • 代表 Symbol 一种实例是唯一且不可改变的数据类型。

以及对象 Object

对象(Objects)和函数(functions)是这门语言的另外两个基本元素。你可以把对象当作存放值的一个命名容器,然后将函数当作你的程序能够执行的步骤。

数据类型的转换

+号两边只要出现了字符串,就统一转化为字符串并拼接

字符串转换为数字

有一些方法可以将内存中表示一个数字的字符串转换为对应的数字。

parseInt()和 parseFloat()

parseInt 方法只能返回整数,所以使用它会丢失小数部分。另外,调用 parseInt 时最好总是带上进制(radix) 参数,这个参数用于指定使用哪一种进制。

将字符串转换为数字的另一种方法是使用一元加法运算符。

"1.1" + "1.1" = "1.11.1"
(+"1.1") + (+"1.1") = 2.2
// 注意:加入括号为清楚起见,不是必需的。

啥?

字面量

字面量是由语法表达式定义的常量;或,通过由一定字词组成的语词表达式定义的常量

在 JavaScript 中,你可以使用各种字面量。这些字面量是脚本中按字面意思给出的固定的值,而不是变量。(译注:字面量是常量,其值是固定的,而且在程序脚本运行中不可更改,比如 false,3.1415,thisIsStringOfHelloworld ,invokedFunction: myFunction(“myArgument”)。本节将介绍以下类型的字面量:

数组字面量

数组字面值是一个封闭在方括号对([])中的包含有零个或多个表达式的列表,其中每个表达式代表数组的一个元素。当你使用数组字面值创建一个数组时,该数组将会以指定的值作为其元素进行初始化,而其长度被设定为元素的个数。

下面的示例用 3 个元素生成数组 coffees,它的长度是 3。

var coffees = ['French Roast', 'Colombian', 'Kona'];

var a = [3];

console.log(a.length); // 1

console.log(a[0]); // 3

若在顶层(全局)脚本里用字面值创建数组,JavaScript 语言将会在每次对包含该数组字面值的表达式求值时解释该数组。另一方面,在函数中使用的数组,将在每次调用函数时都会被创建一次。

数组字面值同时也是数组对象。

数组字面值中的多余逗号

(译注:声明时)你不必列举数组字面值中的所有元素。若你在同一行中连写两个逗号(,),数组中就会产生一个没有被指定的元素,其初始值是 undefined。以下示例创建了一个名为 fish 的数组:

var fish = ['Lion', , 'Angel'];

在这个数组中,有两个已被赋值的元素,和一个空元素(fish[0]是“Lion”,fish[1]是 undefined,而 fish[2]是“Angel”;译注:此时数组的长度属性 fish.length 是 3)。

如果你在元素列表的尾部添加了一个逗号,它将会被忽略。在下面的例子中,数组的长度是 3,并不存在 myList[3]这个元素

再看一个例子。在这里,该数组的长度是 4,元素 myList[1]和 myList[3]被漏掉了。(但是)只有最后的那个逗号被忽略。

var myList = ['home', , 'school', ,];

布尔字面量 (Boolean literals)

布尔类型有两种字面量:true 和 false。

整数 (Integers)

整数可以用十进制(基数为 10)、十六进制(基数为 16)、八进制(基数为 8)以及二进制(基数为 2)表示。

  • 十进制整数字面量由一串数字序列组成,且没有前缀 0。
  • 八进制的整数以 0(或 0O、0o)开头,只能包括数字 0-7。
  • 十六进制整数以 0x(或 0X)开头,可以包含数字(0-9)和字母 af 或 AF。
  • 二进制整数以 0b(或 0B)开头,只能包含数字 0 和 1。

严格模式下,八进制整数字面量必须以 0o 或 0O 开头,而不能以 0 开头。

浮点数字面量 (Floating-point literals)

浮点数字面值可以有以下的组成部分:

一个十进制整数,可以带正负号(即前缀“+”或“ - ”),

小数点(“.”),

小数部分(由一串十进制数表示),

指数部分。

指数部分以“e”或“E”开头,后面跟着一个整数,可以有正负号(即前缀“+”或“-”)。浮点数字面量至少有一位数字,而且必须带小数点或者“e”(大写“E”也可)。

简言之,其语法是:

[(+|-)][digits][.digits][(E|e)[(+|-)]digits]
()内单选 []必填

例如:

3.14 -
  0.2345789 - // -0.23456789
  3.12e12; // -3.12*10^12
0.1e-23; // 0.1*10^(-23)=10^(-24)=1e-24

对象字面量 (Object literals)

对象字面值是封闭在花括号对({})中的一个对象的零个或多个“属性名-值”对的(元素)列表。你不能在一条语句的开头就使用对象字面值,这将导致错误或产生超出预料的行为, 因为此时左花括号({)会被认为是一个语句块的起始符号。(译者:这 里需要对语句 statement、块 block 等基本名词的解释)

以下是一个对象字面值的例子。对象 car 的第一个元素(译注:即一个属性/值对)定义了属性 myCar;第二个元素,属性 getCar,引用了一个函数(即 CarTypes(“Honda”));第三个元素,属性 special,使用了一个已有的变量(即 Sales)。

var Sales = 'Toyota';

function CarTypes(name) {
  return name === 'Honda' ? name : "Sorry, we don't sell " + name + '.';
}

var car = { myCar: 'Saturn', getCar: CarTypes('Honda'), special: Sales };

console.log(car.myCar); // Saturn
console.log(car.getCar); // Honda
console.log(car.special); // Toyota

更进一步的,你可以使用数字或字符串字面值作为属性的名字,或者在另一个字面值内嵌套上一个字面值。如下的示例中使用了这些可选项。

var car = { manyCars: { a: 'Saab', b: 'Jeep' }, 7: 'Mazda' };

console.log(car.manyCars.b); // Jeep
console.log(car[7]); // Mazda

对象属性名字可以是任意字符串,包括空串。如果对象属性名字不是合法的 javascript 标识符,它必须用”“包裹。属性的名字不合法,那么便不能用.访问属性值,而是通过类数组标记(”[]“)访问和赋值。

var unusualPropertyNames = {
  "": "An empty string",
  "!": "Bang!"
}
console.log(unusualPropertyNames."");   // 语法错误: Unexpected string
console.log(unusualPropertyNames[""]);  // An empty string
console.log(unusualPropertyNames.!);    // 语法错误: Unexpected token !
console.log(unusualPropertyNames["!"]); // Bang!

增强的对象字面量

在 ES2015,对象字面值扩展支持在创建时设置原型,简写了 foo: foo 形式的属性赋值,方法定义,支持父方法调用,以及使用表达式动态计算属性名。总之,这些也使对象字面值和类声明更加紧密地联系起来,让基于对象的设计从这些便利中更加受益。

var obj = {
  // __proto__
  __proto__: theProtoObj,
  // Shorthand for ‘handler: handler’
  handler,
  // Methods
  toString() {
    // Super calls
    return 'd ' + super.toString();
  },
  // Computed (dynamic) property names
  ['prop_' + (() => 42)()]: 42,
};

需要注意:

var foo = { a: 'alpha', 2: 'two' };
console.log(foo.a); // alpha
console.log(foo[2]); // two
//console.log(foo.2);  // SyntaxError: missing ) after argument list
//console.log(foo[a]); // ReferenceError: a is not defined
console.log(foo['a']); // alpha
console.log(foo['2']); // two

RegExp 字面值

一个正则表达式是字符被斜线(译注:正斜杠“/”)围成的表达式。下面是一个正则表达式文字的一个例子。

var re = /ab+c/;

字符串字面量 (String literals)

字符串字面量是由双引号(“)对或单引号(’)括起来的零个或多个字符。字符串被限定在同种引号之间;也即,必须是成对单引号或成对双引号。下面的例子都是字符串字面值:

'foo';
'bar';
'1234';
'one line \n another line';
"John's cat";

你可以在字符串字面值上使用字符串对象的所有方法——JavaScript 会自动将字符串字面值转换为一个临时字符串对象,调用该方法,然后废弃掉那个临时的字符串对象。你也能用对字符串字面值使用类似 String.length 的属性:

console.log("John's cat".length);
// 将打印字符串中的字符个数(包括空格)
// 结果为:10

在 ES2015 中,还提供了一种模板字面量(template literals),模板字符串提供了一些语法糖来帮你构造字符串。这与 Perl、Python 还有其他语言中的字符串插值(string interpolation)的特性非常相似。除此之外,你可以在通过模板字符串前添加一个 tag 来自定义模板字符串的解析过程,这可以用来防止注入攻击,或者用来建立基于字符串的高级数据抽象。

// Basic literal string creation
`In JavaScript '\n' is a line-feed.`// Multiline strings
`In JavaScript this is
 not legal.`;

// String interpolation
var name = 'Bob',
  time = 'today';
`Hello ${name}, how are you ${time}?`;

// Construct an HTTP request prefix is used to interpret the replacements and construction
POST`http://foo.org/bar?a=${a}&b=${b}
     Content-Type: application/json
     X-Credentials: ${credentials}
     { "foo": ${foo},
       "bar": ${bar}}`(myOnReadyStateChangeHandler);

除非有特别需要使用字符串对象,否则,你应当始终使用字符串字面值。

在字符串中使用的特殊字符

以下表格列举了你能在 JavaScript 的字符串中使用的特殊字符。

字符 意思
\0 Null 字节
\b 退格符
\f 换页符
\n 换行符
\r 回车符
\t Tab (制表符)
\v 垂直制表符
' 单引号
" 双引号
\\ 反斜杠字符(\)
\XXX 由从 0 到 377 最多三位八进制数 XXX 表示的 Latin-1 字符。例如,\251 是版权符号的八进制序列。
\xXX 由从 00 和 FF 的两位十六进制数字 XX 表示的 Latin-1 字符。例如,\ xA9 是版权符号的十六进制序列。
\uXXXX 由四位十六进制数字 XXXX 表示的 Unicode 字符。例如,\ u00A9 是版权符号的 Unicode 序列。见 Unicode escape sequences (Unicode 转义字符).
\u{XXXXX} Unicode 代码点 (code point) 转义字符。例如,\u{2F804} 相当于 Unicode 转义字符 \uD87E\uDC04 的简写。

转义字符

对于那些未出现在表 2.1 中的字符,其所带的前导反斜线’'将被忽略。但是,这一用法已被废弃,应当避免使用。

通过在引号前加上反斜线’',可以在字符串中插入引号,这就是引号转义。例如:

var quote = 'He read "The Cremation of Sam McGee" by R.W. Service.';
console.log(quote);

代码的运行结果为:

He read “The Cremation of Sam McGee” by R.W. Service.

要在字符串中插入’'字面值,必须转义反斜线。例如,要把文件路径 c:\temp 赋值给一个字符串,可以采用如下方式:

var home = 'c:\\temp';

也可以在换行之前加上反斜线以转义换行(译注:实际上就是一条语句拆成多行书写),这样反斜线和换行都不会出现在字符串的值中。

var str =
  'this string \
is broken \
across multiple\
lines.';
console.log(str); // this string is broken across multiplelines.

Javascript 没有“heredoc”语法,但可以用行末的换行符转义和转义的换行来近似实现。

var poem =
  'Roses are red,\n\
Violets are blue.\n\
Sugar is sweet,\n\
and so is foo.';

ECMAScript 2015 增加了一种新的字面量,叫做模板字面量 template literals。它包含一些新特征,包括了多行字符串!

var poem = `Roses are red,
Violets are blue.
Sugar is sweet,
and so is foo.`;

暂停小结

真是基础的不能再基础了。

ES6 的一些新特性,模板字符串、块级作用域、变量提升、函数提升、对象属性和方法的简写…

评论0

  • 还没有评论,来说点什么吧。