JavaScript高级程序设计(第4版) -- -- 观后笔记(篇章一)

一、什么是 JavaScript 

1、JavaScript 实现

完整的 JavaScript 实现包含以下几个部分:

-- --

核心(ECMAScript)文档对象模型(DOM)浏览器对象模型(BOM)

2、DOM 

文档对象模型(DOM,Document Object Model)是一个应用编程接口(API),用于在 HTML 中使 用扩展的 XML。DOM 将整个页面抽象为一组分层节点。HTMLXML 页面的每个组成部分都是一种节点,包含不同的数据。

-- --

如下 HTML:

<html> 
 <head> 
 <title>Sample Page</title> 
 </head> 
 <body> 
 <p> Hello World!</p> 
 </body> 
</html> 

这些代码通过 DOM 可以表示为一组分层节点

DOM 通过创建表示文档的树,让开发者可以随心所欲地控制网页的内容和结构使用 DOM API, 可以轻松地删除、添加、替换、修改节点

3、BOM 

浏览器对象模型(BOM) API,用于支持访问和操作浏览器的窗口。使用BOM,开发者可以操控浏览器显示页面之外的部分

-- --

总体来说,BOM主要针对浏览器窗口子窗口(frame),不过人们通常会把任何特定于浏览器的扩展都归在BOM的范畴内。比如,下面就是这样一些扩展:

  •  弹出新浏览器窗口的能力;
  •  移动、缩放和关闭浏览器窗口的能力;
  •  navigator对象,提供关于浏览器的详尽信息;
  •  location对象,提供浏览器加载页面的详尽信息;
  •  screen对象,提供关于用户屏幕分辨率的详尽信息;
  •  performance对象,提供浏览器内存占用、导航行为和时间统计的详尽信息;
  •  对cookie的支持;
  •  其他自定义对象,如XMLHttpRequest和IE的ActiveXObject。

4、总结

JavaScript是一门用来与网页交互的脚本语言,包含以下三个组成部分。

  •  ECMAScript:由ECMA-262定义并提供核心功能
  •  文档对象模型(DOM):提供与网页内容交互的方法和接口。
  •  浏览器对象模型(BOM):提供与浏览器交互的方法和接口。

二、HTML中的 JavaScript

1、<script> 元素

将JavaScript插入HTML的主要方法是使用<script>元素。有下列属性:

  • async:可选。表示应该立即开始下载脚本,但不能阻止其他页面动作,比如下载资源或等待其他脚本加载。只对外部脚本文件有效
  • defer:可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本文件有效
  • src:可选。表示包含要执行的代码的外部文件
  • type:可选。代替language,表示代码块中脚本语言的内容类型(也称MIME类型)。
  • crossorigin:可选。配置相关请求的CORS(跨源资源共享)设置。默认不使用CORS。crossorigin= "anonymous"配置文件请求不必设置凭据标志。crossorigin="use-credentials"设置凭据标志,意味着出站请求会包含凭据。

  • integrity:可选。允许比对接收到的资源和指定的加密签名以验证子资源完整性(SRI,

    Subresource Integrity)。如果接收到的资源的签名与这个属性指定的签名不匹配,则页面会报错脚本不会执行。这个属性可以用于确保内容分发网络(CDN,Content Delivery Network)不会提供恶意内容

  • charset:可选。使用src属性指定的代码字符集。这个属性很少使用,因为大多数浏览器不在乎它的值。

-- --

注意:

  1. 在使用行内JavaScript代码时,要注意代码中不能出现字符串</script>浏览器解析行内脚本的方式决定了它在看到字符串时,会将其当成结束的标签。想避免这个问题,只需要转义字符“\”即可。
  2. 使用了src属性的<script>元素不应该在<script>和</script>标签中再包含其他JavaScript代码。如果两者都提供的话,则浏览器只会下载并执行脚本文件,从而忽略行内代码

2、行内代码与外部文件

虽然可以直接在 HTML 文件中嵌入 JavaScript 代码,但通常认为最佳实践是尽可能将 JavaScript 代码放在外部文件中。不过这个最佳实践并不是明确的强制性规则。推荐使用外部文件的理由如下。 

-- --

  • 可维护性。JavaScript 代码如果分散到很多 HTML 页面,会导致维护困难。而用一个目录保存 所有 JavaScript 文件,则更容易维护,这样开发者就可以独立于使用它们的 HTML 页面来编辑 代码。
  • 缓存。浏览器会根据特定的设置缓存所有外部链接的 JavaScript 文件,这意味着如果两个页面都用到同一个文件,则该文件只需下载一次。这最终意味着页面加载更快。
  • 适应未来。通过把 JavaScript 放到外部文件中,就不必考虑用 XHTML 或前面提到的注释黑科技。 包含外部 JavaScript 文件的语法在 HTML 和 XHTML 中是一样的。

3、<noscript> 元素

用于给不支持 JavaScript 的浏览器提供替代内容。

-- --

在下列两种 情况下,浏览器将显示包含在 <noscript> 中的内容:

  •  浏览器不支持脚本;
  •  浏览器对脚本的支持被关闭。

任何一个条件被满足,包含在<noscript>中的内容就会被渲染。否则,浏览器不会渲染<noscript>中的内容。

4、总结

JavaScript 是通过 <script> 元素插入到 HTML 页面中的。这个元素可用于JavaScript 代码嵌入到 HTML 页面中,跟其他标记混合在一起,也可用于引入保存在外部文件中的 JavaScript。

  • 要包含外部 JavaScript 文件,必须将 src 属性设置为要包含文件的 URL。文件可以跟网页在同一台服务器上也可以位于完全不同的域
  • 所有<script>元素会依照它们在网页中出现的次序被解释。在不使用 defer 和 async 属性的 情况下,包含在<script>元素中的代码必须严格按次序解释
  • 对不推迟执行的脚本,浏览器必须解释完位于<script>元素中的代码,然后才能继续渲染页面 的剩余部分。为此,通常应该把<script>元素放到页面末尾,介于主内容之后及</body>标签之前。
  • 可以使用 defer 属性把脚本推迟到文档渲染完毕后再执行。推迟的脚本原则上按照它们被列出的次序执行
  • 可以使用 async 属性表示脚本不需要等待其他脚本,同时也不阻塞文档渲染,即异步加载。异步脚本不能保证按照它们在页面中出现的次序执行
  • 通过使用<noscript>元素,可以指定在浏览器不支持脚本时显示的内容。如果浏览器支持并启用脚本,则<noscript>元素中的任何内容都不会被渲染。

三、语言基础 

1、严格模式

ECMAScript 5 增加了严格模式(strict mode)的概念。严格模式是一种不同的 JavaScript 解析和执行模型,ECMAScript 3 的一些不规范写法在这种模式下会被处理对于不安全的活动将抛出错误。

-- --

要对 整个脚本启用严格模式,在脚本开头加上这一行:

"use strict"; 

虽然看起来像个没有赋值给任何变量的字符串,但它其实是一个预处理指令。任何支持的 JavaScript 引擎看到它都会切换到严格模式。选择这种语法形式的目的是不破坏 ECMAScript 3 语法

-- --

也可以单独指定一个函数在严格模式下执行,只要把这个预处理指令放到函数体开头即可:

function doSomething() { 
 "use strict"; 
 // 函数体 
} 

2、for 循环中的 var 和  let 声明 

for (var i = 0; i < 5; ++i) { 
 setTimeout(() => console.log(i), 0) 
}
// 输出:5、5、5、5、5 

之所以会这样,是因为在退出循环时迭代变量保存的是导致循环退出的值:5。在之后执行超时逻辑时,所有的 i 都是同一个变量,因而输出的都是同一个最终值

for (let i = 0; i < 5; ++i) { 
 setTimeout(() => console.log(i), 0) 
} 
// 输出: 0、1、2、3、4

使用 let 声明迭代变量时,JavaScript 引擎在后台会为每个迭代循环声明一个新的迭代变量。 每个 setTimeout 引用的都是不同的变量实例,所以 console.log 输出的是我们期望的值,也就是循环执行过程中每个迭代变量的值

3、数据类型 

简单数据类型(也称为原始类型)

Undefined、Null、Boolean、Number、 String 和 Symbol。

Symbol(符号)是 ECMAScript 6 新增的。

-- --

复杂数据类型

Object(对 象)。Object 是一种无序名值对的集合。

4、数值转换

有 3 个函数可以将非数值转换为数值:Number()parseInt() parseFloat()。 

  • Number()是转型函数,可用于任何数据类型。
  • 后两个函数主要用于将字符串转换为数值。

Number()函数基于如下规则执行转换。

  • 布尔值,true 转换为 1,false 转换为 0。
  • 数值,直接返回。
  • null,返回 0。
  • undefined,返回 NaN。
  • 字符串,应用以下规则。
  1. 如果字符串包含数值字符,包括数值字符前面带加、减号的情况,则转换为一个十进制数值。 因此,Number("1")返回 1,Number("123")返回 123,Number("011")返回 11(忽略前面 的零)
  2. 如果字符串包含有效的浮点值格式如"1.1",则会转换为相应的浮点值(同样,忽略前面的零)。
  3. 如果字符串包含有效的十六进制格式如"0xf",则会转换为与该十六进制值对应的十进制整 数值。
  4. 如果是空字符串(不包含字符),则返回 0。
  5. 如果字符串包含除上述情况之外的其他字符,则返回 NaN。
  • 对象,调用 valueOf()方法,并按照上述规则转换返回的值。如果转换结果是 NaN,则调用 toString()方法,再按照转换字符串的规则转换。

示例:

let num1 = Number("Hello world!"); // NaN 
let num2 = Number(""); // 0 
let num3 = Number("000011"); // 11 
let num4 = Number(true); // 1 

通常在需要得到整数时可以优先使 用 parseInt()函数。parseInt()函数更专注于字符串是否包含数值模式。 

  • 字符串最前面的空格会被忽略,从第一个非空格字符开始转换。
  • 如果第一个字符不是数值字符、加号或减号,parseInt()立即 返回 NaN。这意味着空字符串也会返回 NaN(这一点跟 Number()不一样,它返回 0)。
  • 如果第一个字符 是数值字符、加号或减号,则继续依次检测每个字符,直到字符串末尾,或碰到非数值字符。

比如,"1234blue"会被转换为 1234,因为"blue"会被完全忽略。类似地,"22.5"会被转换为 22,因为小数 点不是有效的整数字符。

console.log(parseInt('1234blue')); // 1234
console.log(parseInt('22.5')); // 22

假设字符串中的第一个字符是数值字符,parseInt()函数也能识别不同的整数格式(十进制、八 进制、十六进制)。

换句话说,如果字符串以"0x"开头,就会被解释为十六进制整数。如果字符串以"0" 开头,且紧跟着数值字符,在非严格模式下会被某些实现解释为八进制整数。

如下示例:

let num1 = parseInt("1234blue"); // 1234 
let num2 = parseInt(""); // NaN 
let num3 = parseInt("0xA"); // 10,解释为十六进制整数 
let num4 = parseInt(22.5); // 22 
let num5 = parseInt("70"); // 70,解释为十进制值 
let num6 = parseInt("0xf"); // 15,解释为十六进制整数 

不同的数值格式很容易混淆,因此 parseInt()也接收第二个参数,用于指定底数(进制数)

如下示例:

let num1 = parseInt("10", 2); // 2,按二进制解析 
let num2 = parseInt("10", 8); // 8,按八进制解析 
let num3 = parseInt("10", 10); // 10,按十进制解析 
let num4 = parseInt("10", 16); // 16,按十六进制解析

parseFloat()函数的工作方式跟 parseInt()函数类似,都是从位置 0 开始检测每个字符。同样, 它也是解析到字符串末尾或者解析到一个无效的浮点数值字符为止。

这意味着第一次出现的小数点是有 效的,但第二次出现的小数点就无效了,此时字符串的剩余字符都会被忽略 。

console.log(parseFloat('22.34.5')); // 22.34

parseFloat()函数的另一个不同之处在于

  • 它始终忽略字符串开头的零
  • 这个函数能识别前面讨论的所有浮点格式,以及十进制格式(开头的零始终被忽略)。
  • 十六进制数值始终会返回 0。因为 parseFloat()只解析十进制值,因此不能指定底数。
  • 最后,如果字符串表示整数(没有小数点或者小 数点后面只有一个零),则 parseFloat()返回整数。

如下示例:

let num1 = parseFloat("1234blue"); // 1234,按整数解析 
let num2 = parseFloat("0xA"); // 0 
let num3 = parseFloat("22.5"); // 22.5 
let num4 = parseFloat("22.34.5"); // 22.34 
let num5 = parseFloat("0908.5"); // 908.5 
let num6 = parseFloat("3.125e7"); // 31250000 

5、String 类型

转换为字符串 

几乎所有值都有的 toString() 方法。

let age = 11; 
let ageAsString = age.toString(); // 字符串"11" 
let found = true; 
let foundAsString = found.toString(); // 字符串"true" 

toString()方法可见于数值、布尔值、对象字符串值。(没错,字符串值也有 toString()方法, 该方法只是简单地返回自身的一个副本。)nullundefined没有 toString()方法

toString()可以接收一个底数参数,即以什么底数来输出数值的字符串表示。

let num = 10; 
console.log(num.toString()); // "10" 
console.log(num.toString(2)); // "1010" 
console.log(num.toString(8)); // "12" 
console.log(num.toString(10)); // "10" 
console.log(num.toString(16)); // "a" 

-- --

String()方法。

不确定一个值是不是 null 或 undefined,可以使用 String()转型函数,它始终会返回表示相应类型值的字符串。

String()函数遵循如下规则:

  • 如果值有 toString()方法,则调用该方法(不传参数)并返回结果。
  • 如果值是 null,返回"null"。
  • 如果值是 undefined,返回"undefined"。
let value1 = 10; 
let value2 = true; 
let value3 = null; 
let value4; 
 
console.log(String(value1)); // "10" 
console.log(String(value2)); // "true" 
console.log(String(value3)); // "null" 
console.log(String(value4)); // "undefined"

因为 null 和 undefined 没有 toString()方法, 所以 String()方法就直接返回了这两个值的字面量文本

6、Symbol 类型

Symbol(符号)是 ECMAScript 6 新增的数据类型。符号是原始值,且符号实例是唯一不可变的。 符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险

7、Object 类型  

对象其实就是一组数据和功能的集合。对象通过 new 操作符后跟对象类型的名称来创建。开发者可以通过创建 Object 类型的实例来创建自己的对象,然后再给对象添加属性和方法

let o = new Object(); 

每个 Object 实例都有如下属性和方法。

  • constructor:用于创建当前对象的函数。在前面的例子中,这个属性的值就是 Object() 函数。
  • hasOwnProperty(propertyName):用于判断当前对象实例(不是原型)上是否存在给定的属性。要检查的属性名必须是字符串(如 o.hasOwnProperty("name"))或符号。
  • isPrototypeOf(object):用于判断当前对象是否为另一个对象的原型。
  • propertyIsEnumerable(propertyName):用于判断给定的属性是否可以使用 for-in 语句枚举。与 hasOwnProperty()一样,属性名必须是字符串。
  • toLocaleString():返回对象的字符串表示,该字符串反映对象所在的本地化执行环境。
  • toString():返回对象的字符串表示。  valueOf():返回对象对应的字符串、数值或布尔值表示。通常与 toString()的返回值相同。

8、操作符 

8.1、一元操作符

1、递增/递减操作符
let age = 29; 
++age; // 30
age++; // 30
--age; // 28
age--; // 28
let anotherAge = --age + 2;  // 30

前缀递增和递减在语句中的优先级是相等的,因此会从左到右依次求值。比如:

let num1 = 2; 
let num2 = 20; 
let num3 = --num1 + num2; // 21 
let num4 = num1 + num2; // 21 

把递增操作符放到变量后面不会改变语句执行的结果,因为递增是唯一的操作。可是,在跟其他操作混合时,差异就会变明显,比如:

let num1 = 2; 
let num2 = 20; 
let num3 = num1-- + num2; // 22
let num4 = num1 + num2; // 21

这里的不同之处在于,计算 num3 时使用的是 num1 的原始值(2),而计算 num4 时使用的是 num1 递减后的值(1)。

-- --

递增和递减操作符遵循如下规则:

  • 对于字符串,如果是有效的数值形式,则转换为数值再应用改变。变量类型从字符串变成数值
  • 对于字符串,如果不是有效的数值形式,则将变量的值设置为 NaN 。变量类型从字符串变成数值
  • 对于布尔值,如果是 false,则转换为 0 再应用改变。变量类型从布尔值变成数值。
  • 对于布尔值,如果是 true,则转换为 1 再应用改变。变量类型从布尔值变成数值。
  • 对于浮点值,加 1 或减 1。
  • 如果是对象,则调用valueOf()方法取得可以操作的值。对得到的 值应用上述规则。
  • 如果是 NaN,则调用 toString()并再次应用其他规则。变量类型从对象变成 数值。
let s1 = "2"; 
let s2 = "z"; 
let b = false; 
let f = 1.1; 
let o = { 
 valueOf() { 
 return -1; 
 } 
}; 
 
s1++; // 值变成数值 3 
s2++; // 值变成 NaN 
b++; // 值变成数值 1 
f--; // 值变成 0.1
 2、一元加和减

将一元加应用到非数值,则会执行与使用 Number()转型函数一样的类型转换

  • 布尔值 falsetrue 转换为 0 1
  • 字符串根据特殊规则进行解析
  • 对象调用它们的 valueOf()和/或 toString() 方法以得到可以转换的值。
let s1 = "01"; 
let s2 = "1.1"; 
let s3 = "z"; 
let b = false; 
let f = 1.1; 
let o = { 
 valueOf() { 
 return -1; 
 } 
}; 

// 加号
s1 = +s1; // 值变成数值 1 
s2 = +s2; // 值变成数值 1.1 
s3 = +s3; // 值变成 NaN 
b = +b; // 值变成数值 0 
f = +f; // 不变,还是 1.1 
o = +o; // 值变成数值-1

// 减号
s1 = -s1; // 值变成数值-1 
s2 = -s2; // 值变成数值-1.1 
s3 = -s3; // 值变成 NaN 
b = -b; // 值变成数值 0 
f = -f; // 变成-1.1 
o = -o; // 值变成数值 1

8.2、布尔操作符

 布尔操作符一共有 3 个:逻辑非、逻辑与和逻辑或

1、逻辑非

这个操作符始终返回布尔值无论应用到的是什么数据类型。逻辑非操作符首先将操作数转换为布尔值,然后再对其取反。

规则如下:

  • 如果操作数是对象,则返回 false
  • 如果操作数是空字符串,则返回 true
  • 如果操作数是非空字符串,则返回 false
  • 如果操作数是数值 0,则返回 true
  • 如果操作数是非 0 数值(包括 Infinity),则返回 false
  • 如果操作数是 null,则返回 true
  • 如果操作数是 NaN,则返回 true
  • 如果操作数是 undefined,则返回 true
console.log(!false); // true 
console.log(!"blue"); // false 
console.log(!0); // true 
console.log(!NaN); // true 
console.log(!""); // true 
console.log(!12345); // false 

同时使用两个叹号(!!),相当于调用了转型函数 Boolean()。无论操作数是什么类型,第一个叹号总会返回布尔值第二个叹号对该布尔值取反, 从而给出变量真正对应的布尔值。

console.log(!!"blue"); // true 
console.log(!!0); // false 
console.log(!!NaN); // false 
console.log(!!""); // false 
console.log(!!12345); // true
2、逻辑与

逻辑与操作符由两个和号(&&)表示,应用到两个值,如下所示:

let result = true && false;

逻辑与操作符可用于任何类型的操作数,不限于布尔值。如果操作数不是布尔值,则逻辑与并不一定会返回布尔值,而是遵循如下规则。

  • 如果第一个操作数是对象,则返回第二个操作数。
  • 如果第二个操作数是对象,则只有第一个操作数求值为 true 才会返回该对象
  • 如果两个操作数都是对象,则返回第二个操作数。
  • 如果有一个操作数是 null,则返回 null
  • 如果有一个操作数是 NaN,则返回 NaN
  • 如果有一个操作数是 undefined,则返回 undefined

逻辑与操作符是一种短路操作符,意思就是如果第一个操作数决定了结果,那么永远不会对第二个操作数求值。对逻辑与操作符来说,如果第一个操作数是 false,那么无论第二个操作数是什么值,结果也不可能等于 true。

let found = true; 
let result = (found && someUndeclaredVariable); // 这里会出错 
console.log(result); // 不会执行这一行 


let found = false; 
let result = (found && someUndeclaredVariable); // 不会出错 
console.log(result); // 会执行
3、逻辑或

逻辑或操作符由两个管道符(||)表示,比如:

let result = true || false; 

与逻辑与类似,如果有一个操作数不是布尔值,那么逻辑或操作符也不一定返回布尔值。它遵循如 下规则。

  • 如果第一个操作数是对象,则返回第一个操作数。
  • 如果第一个操作数求值为 false,则返回第二个操作数。
  • 如果两个操作数都是对象,则返回第一个操作数。
  • 如果两个操作数都是 null,则返回 null
  • 如果两个操作数都是 NaN,则返回 NaN
  • 如果两个操作数都是 undefined,则返回 undefined

同样与逻辑与类似,逻辑或操作符也具有短路的特性。只不过对逻辑或而言,第一个操作数求值为 true,第二个操作数就不会再被求值了。

let found = true; 
let result = (found || someUndeclaredVariable); // 不会出错 
console.log(result); // 会执行 


let found = false; 
let result = (found || someUndeclaredVariable); // 这里会出错 
console.log(result); // 不会执行这一行 

利用这个行为,可以避免给变量赋值 null 或 undefined。比如:

let myObject = preferredObject || backupObject; 

在这个例子中,变量 myObject 会被赋予两个值中的一个。

其中,preferredObject 变量包含首 选的值,backupObject 变量包含备用的值。

如果 preferredObject 不是 null,则它的值就会赋给 myObject;

如果 preferredObject 是 null,则 backupObject 的值就会赋给 myObject。

8.3、乘性操作符

乘法操作符由一个星号(*)表示,可以用于计算两个数值的乘积。

let result = 34 * 56; 
  • 如果操作数都是数值,则执行常规的乘法运算,即两个正值相乘是正值,两个负值相乘也是正值,正负符号不同的值相乘得到负值。如果 ECMAScript 不能表示乘积,则返回 Infinity 或 -Infinity。
  • 如果有任一操作数是 NaN,则返回 NaN。 
  • 如果是 Infinity 乘以 0,则返回 NaN。
  • 如果是 Infinity 乘以非 0的有限数值,则根据第二个操作数的符号返回 Infinity 或-Infinity。
  • 如果是 Infinity 乘以 Infinity,则返回 Infinity。
  • 如果有不是数值的操作数,则先在后台用 Number()将其转换为数值,然后再应用上述规则。

8.4、除法操作符

除法操作符由一个斜杠(/)表示,用于计算第一个操作数除以第二个操作数的商。

let result = 66 / 11;
  • 如果操作数都是数值,则执行常规的除法运算,即两个正值相除是正值,两个负值相除也是正 值,符号不同的值相除得到负值。如果ECMAScript不能表示商,则返回Infinity或-Infinity。
  • 如果有任一操作数是 NaN,则返回 NaN
  • 如果是 Infinity 除以 Infinity,则返回 NaN。
  • 如果是 0 除以 0,则返回 NaN
  • 如果是非 0 的有限值除以 0,则根据第一个操作数的符号返回 Infinity 或-Infinity。
  • 如果是 Infinity 除以任何数值,则根据第二个操作数的符号返回 Infinity 或-Infinity。
  • 如果有不是数值的操作数,则先在后台用 Number()函数将其转换为数值,然后再应用上述规则。

8.5、取模操作符

取模(余数)操作符由一个百分比符号(%)表示。

let result = 26 % 5; // 等于 1 
  • 如果操作数是数值,则执行常规除法运算,返回余数。
  • 如果被除数是无限值,除数是有限值,则返回 NaN。
  • 如果被除数是有限值,除数是 0,则返回 NaN。
  • 如果是 Infinity 除以 Infinity,则返回 NaN
  • 如果被除数是有限值,除数是无限值,则返回被除数。
  • 如果被除数是 0,除数不是 0,则返回 0
  • 如果有不是数值的操作数,则先在后台用 Number()函数将其转换为数值,然后再应用上述规则。

8.6、指数操作符

ECMAScript 7 新增了指数操作符,Math.pow()现在有了自己的操作符**。

console.log(Math.pow(3, 2); // 9 
console.log(3 ** 2); // 9 
 
console.log(Math.pow(16, 0.5); // 4 
console.log(16** 0.5);

不仅如此,指数操作符也有自己的指数赋值操作符**=,该操作符执行指数运算和结果的赋值操作

let squared = 3; 
squared **= 2; 
console.log(squared); // 9

let sqrt = 16; 
sqrt **= 0.5; 
console.log(sqrt); // 4 

8.7、加性操作符  

1、加法操作符

加法操作符(+)用于求两个数的和。

let result = 1 + 2; 

如果两个操作数都是数值,加法操作符执行加法运算并根据如下规则返回结果:

  • 如果有任一操作数是 NaN,则返回 NaN
  • 如果是 Infinity 加 Infinity,则返回 Infinity;
  • 如果是-Infinity 加-Infinity,则返回-Infinity;
  • 如果是 Infinity 加-Infinity,则返回 NaN;
  • 如果是+0 加+0,则返回+0;
  • 如果是-0 加+0,则返回+0;
  • 如果是-0 加-0,则返回-0。

不过,如果有一个操作数是字符串,则要应用如下规则:

  • 如果两个操作数都是字符串,则将第二个字符串拼接到第一个字符串后面
  • 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,再将两个字符串拼接在一起。
  • 如果有任一操作数是对象、数值或布尔值,则调用它们的 toString()方法以获取字符串,然后再 应用前面的关于字符串的规则。
  • 对于 undefined 和 null,则调用 String()函数,分别获取 "undefined"和"null"。
let result1 = 5 + 5; // 两个数值 
console.log(result1); // 10 
let result2 = 5 + "5"; // 一个数值和一个字符串 
console.log(result2); // "55"
let num1 = 5; 
let num2 = 10; 

// 示例一
let message = "The sum of 5 and 10 is " + num1 + num2; 
console.log(message); // "The sum of 5 and 10 is 510" 

// 示例二
let message = "The sum of 5 and 10 is " + (num1 + num2); 
console.log(message); // "The sum of 5 and 10 is 15"
2、减法操作符

减法操作符(-)也是使用很频繁的一种操作符。

let result = 2 - 1; 

规则如下:

  • 如果两个操作数都是数值,则执行数学减法运算并返回结果。
  • 如果有任一操作数是 NaN,则返回 NaN
  • 如果是 Infinity 减 Infinity,则返回 NaN。
  • 如果是-Infinity 减-Infinity,则返回 NaN。
  • 如果是 Infinity 减-Infinity,则返回 Infinity。
  • 如果是-Infinity 减 Infinity,则返回-Infinity。
  • 如果是+0 减+0,则返回+0。
  • 如果是+0 减-0,则返回-0。
  • 如果是-0 减-0,则返回+0。
  • 如果有任一操作数是字符串、布尔值、null 或 undefined,则先在后台使用 Number()将其转换为数值,然后再根据前面的规则执行数学运算。如果转换结果是 NaN,则减法计算的结果是 NaN。
  • 如果有任一操作数是对象,则调用valueOf()方法取得表示它的数值。如果该值是 NaN,则 减法计算的结果是 NaN。如果对象没有 valueOf()方法,则调用 toString()方法,然后将得到的字符串转换为数值

示例:

let result1 = 5 - true; // true 被转换为 1,所以结果是 4 
let result2 = NaN - 1; // NaN 
let result3 = 5 - 3; // 2 
let result4 = 5 - ""; // ""被转换为 0,所以结果是 5 
let result5 = 5 - "2"; // "2"被转换为 2,所以结果是 3 
let result6 = 5 - null; // null 被转换为 0,所以结果是 5 

8.8、关系操作符

关系操作符执行比较两个值的操作,包括小于()、大于(>)、小于等于()和大于等于(>=), 用法跟数学课上学的一样。这几个操作符都返回布尔值,如下所示:

let result1 = 5 > 3; // true 
let result2 = 5 < 3; // false

 在将它们应用到不同数据类型时也会发生类型转换和其他行为:

  • 如果操作数都是数值,则执行数值比较
  • 如果操作数都是字符串,则逐个比较字符串中对应字符的编码
  • 如果有任一操作数是数值,则将另一个操作数转换为数值,执行数值比较。
  • 如果有任一操作数是对象,则调用 valueOf()方法,取得结果后再根据前面的规则执行比较。 如果没有 valueOf()操作符,则调用 toString()方法,取得结果后再根据前面的规则执行比较。
  • 如果有任一操作数是布尔值,则将其转换为数值再执行比较。

字符串而言,关系操作符会比较字符串中对应字符的编码,而这些编码是数值。

let result = "Brick" < "alphabet"; // true 
因为字母 B 的编码是 66,字母 a 的编码 是 97。

let result = "23" < "3"; // true 
因为两个操作数都是字符串,所以会逐个比较它们的字符编码
(字符"2"的编码是 50,而字符"3"的编码是 51)

let result = "23" < 3; // false
因为这次会将字符串"23"转换为数值 23,然后再跟 3 比较

let result = "a" < 3; // 因为"a"会转换为 NaN,所以结果是 false

8.9、相等操作符 

1、等于和不等于

ECMAScript 中的等于操作符用两个等于号(==)表示,如果操作数相等,则会返回 true。不等于 操作符用叹号和等于号(!=)表示,如果两个操作数不相等,则会返回 true。这两个操作符都会先进行类型转换(通常称为强制类型转换)再确定操作数是否相等。

如下规则:

  • 如果任一操作数是布尔值,则将其转换为数值再比较是否相等。false 转换为 0,true 转换 为 1。
  • 如果一个操作数是字符串另一个操作数是数值,则尝试将字符串转换为数值,再比较是否 相等。
  • 如果一个操作数是对象另一个操作数不是,则调用对象的 valueOf()方法取得原始值,再根据前面的规则进行比较。 在进行比较时,这两个操作符会遵循如下规则。
  • null 和 undefined 相等。
  • null 和 undefined 不能转换为其他类型的值再进行比较
  • 如果有任一操作数是 NaN,则相等操作符返回 false,不相等操作符返回 true。记住:即使两个操作数都是 NaN,相等操作符也返回 false,因为按照规则,NaN 不等于 NaN
  • 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象, 则相等操作符返回 true。否则,两者不相等。

2、全等和不全等

全等和不全等操作符与相等和不相等操作符类似,只不过它们在比较相等时不转换操作数。全等操作符由 3 个等于号(===)表示,只有两个操作数在不转换的前提下相等才返回 true。

let result1 = ("55" == 55); // true,转换后相等 
let result2 = ("55" === 55); // false,不相等,因为数据类型不同 

let result1 = ("55" != 55); // false,转换后相等 
let result2 = ("55" !== 55); // true,不相等,因为数据类型不同

null === undefined 是 false,因为它们不是相同的数据类型。

8.10、条件操作符

variable = boolean_expression ? true_value : false_value;

同时也叫做三元运算符。

8.11、赋值操作符

 简单赋值用等于号(=)表示,将右手边的值赋给左手边的变量。

let num = 10; 
num = num + 10;

以上代码的第二行可以通过复合赋值来完成: 

let num = 10; 
num += 10;
  • 乘后赋值(*=)
  • 除后赋值(/=)
  • 取模后赋值(%=)
  • 加后赋值(+=)
  • 减后赋值(-=)
  • 左移后赋值(<<=)
  • 右移后赋值(>>=)
  • 无符号右移后赋值(>>>=)

这些操作符仅仅是简写语法,使用它们不会提升性能

8.12、逗号操作符 

逗号操作符可以用来在一条语句中执行多个操作,如下所示:

let num1 = 1, num2 = 2, num3 = 3;

在一条语句中同时声明多个变量是逗号操作符最常用的场景。不过,也可以使用逗号操作符来辅助 赋值。在赋值时使用逗号操作符分隔值,最终会返回表达式中最后一个值

let num = (5, 1, 4, 8, 0); // num 的值为 0 

9、语句

9.1、if 语句

if 语句是使用最频繁的语句之一,语法如下:

if (condition) statement1 else statement2

这里的条件(condition)可以是任何表达式,并且求值结果不一定是布尔值。

ECMAScript 会自动调用 Boolean()函数这个表达式的值转换为布尔值

如果条件求值为 true,则执行语句 statement1;

如果条件求值为 false,则执行语句 statement2。

这里的语句可能是一行代码,也可能是一个代码块(即包含在一对花括号中的多行代码)。

//示例一
if (i > 25) 
 console.log("Greater than 25."); // 只有一行代码的语句 
else { 
 console.log("Less than or equal to 25."); // 一个语句块 
} 

//示例二
if (condition1) statement1 else if (condition2) statement2 else statement3

//示例三
if (i > 25) { 
 console.log("Greater than 25."); 
} else if (i < 0) { 
 console.log("Less than 0."); 
} else { 
 console.log("Between 0 and 25, inclusive."); 
}

9.2、do-while 语句

do-while 语句是一种后测试循环语句,即循环体中的代码执行后才会对退出条件进行求值。换句话说,循环体内的代码至少执行一次。do-while 的语法如下: 

do { 
 statement 
} while (expression);
 
下面是一个例子: 
let i = 0; 
do { 
 i += 2; 
} while (i < 10);

在这个例子中,只要 i 小于 10,循环就会重复执行。i 从 0 开始,每次循环递增 2。

9.3、while 语句 

while 语句是一种先测试循环语句,即先检测退出条件再执行循环体内的代码

因此,while 循环体内的代码有可能不会执行。下面是 while 循环的语法:

while(expression) statement 

这是一个例子: 
let i = 0; 
while (i < 10) { 
 i += 2; 
} 

在这个例子中,变量 i 从 0 开始,每次循环递增 2。只要 i 小于 10,循环就会继续。

9.4、for 语句

for 语句也是先测试语句,只不过增加了进入循环之前的初始化代码,以及循环执行后要执行的表达式,语法如下:

下面是一个用例: 
let count = 10; 
for (let i = 0; i < count; i++) { 
 console.log(i); 
}

以上代码在循环开始前定义了变量 i 的初始值为 0。然后求值条件表达式,如果求值结果为 true (i < count),则执行循环体。因此循环体也可能不会被执行。如果循环体被执行了,则循环后表达式也会执行,以便递增变量 i。

for 循环跟下面的 while 循环是一样的:

let count = 10; 
let i = 0; 
while (i < count) { 
 console.log(i); 
 i++; 
} 

无法通过 while 循环实现的逻辑,同样也无法使用 for 循环实现。因此 for 循环只是将循环相关的代码封装在了一起而已。

在 for 循环的初始化代码中,其实是可以不使用变量声明关键字的。

初始化、条件表达式和循环后表达式都不是必需的。

for (;;) { // 无穷循环 
 doSomething(); 
} 
如果只包含条件表达式,那么 for 循环实际上就变成了 while 循环: 
let count = 10; 
let i = 0; 
for (; i < count; ) { 
 console.log(i); 
 i++; 
}

9.5、for-in 语句

for-in 语句是一种严格的迭代语句,用于枚举对象中的非符号键属性 。

for (const propName in window) { 
 document.write(propName); 
}

这个例子使用 for-in 循环显示了 BOM 对象 window 的所有属性。

每次执行循环,都会给变量 propName 赋予一个 window 对象的属性作为值,直到 window 的所有属性都被枚举一遍

与 for 循环 一样,这里控制语句中的 const 也不是必需的。但为了确保这个局部变量不被修改,推荐使用 const

ECMAScript 中对象的属性是无序的,因此 for-in 语句不能保证返回对象属性的顺序。换句话说,所有可枚举的属性都会返回一次,但返回的顺序可能会因浏览器而异。

如果 for-in 循环要迭代的变量是 null 或 undefined,则不执行循环体。

9.6、for-of 语句

for-of 语句是一种严格的迭代语句,用于遍历可迭代对象的元素

for (const el of [2,4,6,8]) { 
 document.write(el); 
}

使用 for-of 语句显示了一个包含 4 个元素的数组中的所有元素。循环会一直持续到将所有元素都迭代完。与 for 循环一样,这里控制语句中的 const 也不是必需的。但为了确保 这个局部变量不被修改,推荐使用 const。

for-of 循环会按照可迭代对象的 next()方法产生值的顺序迭代元素

如果尝试迭代的变量不支持迭代,则 for-of 语句会抛出错误

9.7、标签语句

标签语句用于给语句加标签。

start: for (let i = 0; i < count; i++) { 
 console.log(i); 
}

在这个例子中,start 是一个标签可以在后面通过 break 或 continue 语句引用。标签语句的典型应用场景是嵌套循环

9.8、break 和 continue 语句

break 和 continue 语句为执行循环代码提供了更严格的控制手段。

break 语句用于立即退出循环强制执行循环后的下一条语句

continue 语句也用于立即退出循环,但会再次从循环顶部开始执行。 

1、break
let num = 0; 
 
for (let i = 1; i < 10; i++) { 
 if (i % 5 == 0) { 
 break
 } 
 num++; 
} 
 
console.log(num); // 4

当 i 等于 5 时,break 语句会导致循环退出,该次循环不会执行递增 num 的代码。

2、continue
let num = 0; 
 
for (let i = 1; i < 10; i++) { 
 if (i % 5 == 0) { 
 continue; 
 } 
 num++; 
} 
 
console.log(num); // 8

当 i 等于 5 时,循环会在递增 num 之前退出但会执行下一次迭代,此时 i 是 6。然后,循环会一直执行到自然结束,即 i 等于 10。最终 num 的值是 8 而不是 9,是因为 continue 语句导致它少递增了一次。

9.9、with 语句

with 语句的用途是将代码作用域设置为特定的对象。

let qs = location.search.substring(1); 
let hostName = location.hostname; 
let url = location.href;

上面代码中的每一行都用到了 location 对象。如果使用 with 语句,就可以少写一些代码:
 
with(location) { 
 let qs = search.substring(1); 
 let hostName = hostname; 
 let url = href; 
}

with 语句用于连接 location 对象。这意味着在这个语句内部,每个变量首先会被认为是一个局部变量。
如果没有找到该局部变量,则会搜索 location 对象,看它是否有一个同名的属性。
如果有,则该变量会被求值为 location 对象的属性。 

with 语句的主要场景是针对一个对象反复操作,这时候将代码作用域设置为该对象能提供便 利 。

严格模式不允许使用 with 语句,否则会抛出错误。

9.10、switch 语句

switch 语句是与 if 语句紧密相关的一种流控制语句,从其他语言借鉴而来。 

如下示例:

if语句写法:
if (i == 25) { 
 console.log("25"); 
} else if (i == 35) { 
 console.log("35"); 
} else { 
 console.log("Other"); 
}

switch语句写法:
switch (i) { 
 case 25: 
 console.log("25"); 
 break; 
 case 35: 
 console.log("35"); 
 break; 
 default: 
 console.log("Other"); 
}

这里的每个 case(条件/分支)相当于:“如果表达式等于后面的值,则执行下面的语句。”break 关键字会导致代码执行跳出 switch 语句。如果没有 break,则代码会继续匹配下一个条件。default 关键字用于在任何条件都没有满足时指定默认执行的语句(相当于 else 语句)。

如果确实需要连续匹配几个条件,可以不写break 关键字。

注意 :

switch 语句在比较每个条件的值时使用全等操作符,因此不会强制转换数据类型(比如,字符串"10"不等于数值 10)。

10、函数

函数对任何语言来说都是核心组件,因为它们可以封装语句,然后在任何地方、任何时间执行。 ECMAScript 中的函数使用 function 关键字声明后跟一组参数,然后是函数体。 

以下是函数的基本语法:

function sayHi(name, message) { 
 console.log("Hello " + name + ", " + message); 
} 

sayHi("Nicholas", "how are you today?"); // 调用

任何函数在任何时间都可以使用 return 语句来返回函数的值,用法是后跟要返回的值。

function sum(num1, num2) { 
 return num1 + num2; 
}

const result = sum(5, 10); // 15

只要碰到 return 语句,函数就会立即停止执行并退出。因此,return 语句后面的代码不会被执行

严格模式对函数也有一些限制:

  • 函数不能以 eval 或 arguments 作为名称;
  • 函数的参数不能叫 eval 或 arguments;
  • 两个命名参数不能拥有同一个名称。
  • 如果违反上述规则,则会导致语法错误,代码也不会执行

11、总结

 ECMAScript 包含 =所有基本语法、操作符、数据类型和对象,能完成基本的计算任务,但没有提供获得输入和产生输出的 机制。理解 ECMAScript 及其复杂的细节是完全理解浏览器中 JavaScript 的关键。下面总结一下 ECMAScript 中的基本元素。

  • ECMAScript 中的基本数据类型包括 Undefined、Null、Boolean、Number、String 和 Symbol。
  • 与其他语言不同,ECMAScript 不区分整数和浮点值,只有 Number 一种数值数据类型。  Object 是一种复杂数据类型,它是这门语言中所有对象的基类。
  • 严格模式为这门语言中某些容易出错的部分施加了限制。
  • ECMAScript 提供了 C 语言和类 C 语言中常见的很多基本操作符,包括数学操作符、布尔操作符、 关系操作符、相等操作符和赋值操作符等。
  • 这门语言中的流控制语句大多是从其他语言中借鉴而来的,比如 if 语句、for 语句和 switch 语句等。

ECMAScript 中的函数与其他语言中的函数不一样。

  • 不需要指定函数的返回值,因为任何函数可以在任何时候返回任何值。
  • 不指定返回值的函数实际上会返回特殊值 undefined。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值