《JavaScript高级程序设计(第四版)》精读(二)第3章 | 8月更文挑战

1,463 阅读11分钟
  • 📢 大家好,我是小丞同学,最近在刷红宝书,这是一篇学习笔记
  • 📢 愿你我一起在这肆意生活里大放光彩
  • 这是阅读《JavaScript高级程序设计(第四版)》的第二天,本书已阅读 56/865

第三章:语言基础

3.1 语法

个人感觉 ECMAScript 的语法挺简单的,学过 C 之类都能很容易的上手

3.1.1 区分大小写

ECMAScript 中一切都区分大小写

例如:testTest是两个不同的变量

注意:typeof是关键字,不能做函数名,而Typeof可以

3.1.2 标识符

  1. 第一个字符必须是一个字母,下划线_或一个美元符号$
  2. 其他字符可以是字母,_$,数字。
  3. 标识符需要用驼峰大小写格式。

关键字、保留字、true、false 和 null 不能作为标识符。

3.1.3 注释

单行注释采用//

多行注释采用/* */

3.1.4 严格模式

在严格模式下一些不安全的操作会抛出错误

开启严格模式的方法

全局开启在文件开头添加"use strict";语句

单独一个函数开启,写在函数体上分

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

3.1.5 语句

这一点相对于我学过的 C 语言就特别的友好

单条语句末尾可以不添加分号

let sum = a + b
let sum = a + b; //均可

3.2 关键字与保留字

有特殊用途的关键字,比如ifbreak之类的

一些还未正式使用,但是在未来会使用的叫保留字,例如enum

关键字和保留字都不能作为标识符或属性名

3.3 变量

在 JS 中定义变量是很方便的,不需要考虑变量保存数据的类型,每个变量只不过是一

个用于保存任意值的命名占位符。

最开始采用 var、在 ES6 后更多的采用letconst关键字,它们的不同在后面会写到

3.3.1 var 关键字

采用var操作符定义变量

var message = 'hi';
message = 100

上面的代码合理

1. var 声明作用域

这部分内容很重要,虽然以后用var的机会很少,但是在一些题中,这常常会是烦人的考点

image-20210714233504966

在上面的代码中,相差之处在于message变量是否通过var被声明

原因在于,通过var定义的变量作为局部变量存在于函数当中,而右图中,未声明message直接使用,会被创建成一个全局变量,因此能够打印出来

注意:当未声明的变量直接使用时,会被声明到全局

2. var 声明提升

对于 var而已,最恶心的地方就是变量提升

例如下列代码

function foo() {
    console.log(age); 
 	var age = 26;
}
foo() // undefined

初学时,可能会很疑惑,为什么没报错呢,这就是变量提升的魅力

在函数执行的前一刻,会将所有的变量声明提到最前面

注意:仅仅是声明噢

上面的代码就可以转化成

function foo() { 
 	var age; 
	console.log(age); 
 	age = 26; 
} 
foo(); // undefined

因此输出undefined

还有很多有关 var 的问题记得后面闭包啥的部分应该有

3.3.2 let 关键字

let 声明的范围是块级作用域,而 var 声明的范围是函数作用域

可以简单理解为let声明的变量只在最近的一对{}内有效

if (true) { 
 let age = 26; 
 console.log(age); // 26 
} 
console.log(age); // ReferenceError: age 没有定义
//书上的例子

age变量的生存区仅在于if的括号内,因此外部无法访问

注意

  1. 在一个块级作用域中,不允许一个变量被多次声明
  2. 在不同的块级作用域内,同一个变量名可以随意使用
1. 暂时性死区

与 var 的重要区别之一,在于 let 没有变量提升

console.log(age); // ReferenceError:age 没有定义
let age = 26;

在解析时,会发现后面有age的声明,只不过在前面无法使用,在let声明前的执行瞬间被称为“暂时性死区”,并抛出语法错误

2. 全局声明

特别注意

let 在全局作用域中声明的变量不会成为window·中的对象

3. for循环中的 let 声明
for (let i = 0; i < 5; ++i) {
	// 循环逻辑
}
console.log(i); // ReferenceError: i 没有定义

相对于 var而言,使用let后,迭代变量i的作用域仅限于for循环块内部

使用varfor循环嵌套异步事件最常见的问题

for (var i = 0; i < 5; ++i) { 
 setTimeout(() => console.log(i), 0) 
} 
// 你可能以为会输出 0、1、2、3、4 
// 实际上会输出 5、5、5、5、5

原因:for 循环中的事件是异步事件,会在for循环结束时才输出,而这时的变量i已经是5

3.3.3 const 声明

const 用于定义不变的量

const age = 26;
age = 16; // TypeError

很显然报错了,因为age变量是const定义的

而对于引用数据类型又不同,const 声明的限制只适用于它指向的变量的引用。例如对象,我们可以改变它的值,以及不引起地址改变的操作

3.3.4 代码风格

不使用var,多使用let,不变的值使用const

大多数的值都是不变的,要多用用噢!

3.4 数据类型

ECMAScript 的数据类型很灵活,一种数据类型可以当作多种数据类型来使用

3.4.1 typeof 操作符

  • "undefined"表示值未定义;

  • "boolean"表示值为布尔值;

  • "string"表示值为字符串;

  • "number"表示值为数值;

  • "object"表示值为对象(而不是函数)或 null;

  • "function"表示值为函数;

  • "symbol"表示值为符号。

注意:typeof null返回object,因为 null被认为是一个空对象

3.4.2 undefined 类型

当变量为初始化时,相当于给变量赋予了 undefined 值

let message; 
console.log(message == undefined); // true

利用 typeof 来检测为声明的变量时,不会报错,会得到 undefined

3.4.3 Null 类型

Null 类型只有一个值 null,逻辑上,null值表示一个空指针对象

在定义将来要保存对象值的变量时,建议使用 null 来初始化,不要使用其他值

关于 nullundefinedundefined值是由 null值派生而来的。因此

null等于undefined但是不全等于(===)undefined

3.4.4 Boolean 类型

布尔类型,trueorfalse,一般用于条件的判断或者节流阀之类的

image-20210715122052251

在 Boolean 类型来说,类型转换是非常重要的

3.4.5 Number 类型

表示整数和浮点数值。

关于八进制,第一个数值是0,如果数字的值超出一定范围,则会忽略前缀0

let oNum1= 070 //有效  70八进制,十进制56
let oNum2 = 079 // 无效 79
1. 浮点值

浮点数值:该数值中必须包含一个小数点,并且小数点后面必须至少有一位数字

let floatnum1 = 1.1; 
let floatnum2 = 1.; //小数点后面没有数字——解析为1
let floatnum3 = .1 ;//解析为0.1,不推荐这种写法
let floatnum4 = 10.0;//整数——解析为10

tips:浮点值的精确度最高是17位,但是由于计算机组成原理,0.1 + 0.2的结果不会是0.3,因此不要比较某个特定的值

2. 值的范围

在多数浏览器中,最小数值是 5e-324,最大数值是 1.797 693 134 862 315 7e+308,当超出这个范围时,会转化为infinity或者-inifinity

3. NaN

意思是“不是数值”(Not a Number),NaN 不是报错!!

用 0 除以任何数都会返回 NaN。分子是非 0 ,分母是 0 ,则会是 infinity

注意:任何涉及 NaN 的操作都会返回 NaN,例如(NaN / 10)

但是离谱的是, NaN 不等于包括 NaN 在内的任何值

console.log(NaN == NaN); // false

isNaN():确定一个值是否为NaN;

isNaN()接收一个值后,第一步会将这个值转换为数值,任何不能被转换为数值的值都会返回true

console.log(isNaN("10")); // false,可以转换为数值 10 
console.log(isNaN("blue")); // true,不可以转换为数值
4. 数值转化

有 3 个函数可以将非数值转换为数值:

  • Number()
  • parseInt()
  • parseFloat()

Number()是转型函数,可用于任何数据类型。

  • undefined 转为 NaN
  • null 转为 0
  • 对于字符串的转换比较复杂
    • 有数值就是数值本身,八进制,十六进制注意转为十进制
    • 空字符串("")转为 0
  • 对象先调用valueOf()方法,如果为NaN再调用toString转换

parseInt()函数更专注于字符串是否包含数值模式

非常重要 如果第一个字符是数值字符、加号或减号,则继续依次检测每个字符,直到字符串末尾,或碰到非数值字符

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,解释为十六进制整数

这个函数特殊的一点在于可以接收2个参数,第二个参数表示第一个参数是多少进制

let num1 = parseInt("AF", 16); // 175 
let num2 = parseInt("AF"); // NaN

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

记得之前看过一到面试题

请问parseInt()parseFloat()Number()的区别?(其实不是这道的,但是找不到了)

答:

  1. parseInt()字符串转换成整型,parseFloat()字符串转换成浮点型,Number()字符串转换成数字型
  2. Number()看的是整体,只要字符串内的内容不是合法的数字,则结果为NaN;否则,就会正常转换为数字类型。
  3. parseInt()parseFloat()的转换规则比较接近如果第一个字符是非数字,那么,结果为NaN,如果第一个字符是数字:
    • parseInt():如果遇到小数点或者其它非数字字符或结尾,那么就把前面的内容正常转换为数字
    • parseFloat():如果遇到第二个小数点或者其它非数字字符或结尾,那么就把前面的内容正常转换为数字

3.4.6 String 类型

三种表示方法,双引号,单引号以及反引号,但是不能混用

1. 字符字面量

用来打印一些特殊字符

字符字面量

太简单,记一记

2. 字符串的特点

从我的理解来看,修改字符串实际上是一个重构的过程,首先给原值和需要连接的值分配足够的空间,然后填充。再销毁原值

3. 转化为字符串

第一种方法也是最常用最通用的方法toString

多数情况下,toString()不接收任何参数,当操作的值为数值时,传入的参数表示转化为的数值对应的进制

let num = 10; 
console.log(num.toString()); // "10" 
console.log(num.toString(2)); // "1010"

对于String方法我的理解是,对于toString方法的补充,当不确定是否为 null 或 undefined 时,可以采用String方法,

如果值为为null 则返回null,为undefined返回undefined,如果该值可以使用toString方法则返回值相同

4. 模板字面量

ES6中非常好用的一个玩意,可以替代创建元素的复杂操作,直接通过模板字面量来创建

let pageHTML = ` 
<div> 
 <a href="#"> 
 <span>Jake</span> 
 </a> 
</div>`;

直接将上述文本插入 html 即可

5. 字符串插值

普通字符串插值

let interpolatedString = 
 value + ' to the ' + exponent + ' power is ' + (value * value);

模板字面量插值

let interpolatedTemplateLiteral = 
 `${ value } to the ${ exponent } power is ${ value * value }`;

通过${}来插值

所有插入的值都会使用 toString()强制转型为字符串

6. 模板字面量标签函数

这个是第一次见,通过下面的例子来理解吧

<script>
        let name = "ljc";
        let age = 19;
        // 标签函数strings(第一个参数)为以${}分割的数组,后面的参数对应${}的值
        function myTag(strings, name, age) {
            console.log(strings);
            //["", "my name is ,age is ", ""]
            console.log(name);
            //ljc,对应表达式${name}的结果
            console.log(age);
            //19
            return "successful";
        }
        const res = myTag `${name}my name is ,age is ${age}`
        console.log(res);
</script>

首先函数的调用在第14行,strings接收以${}分隔的成的数组,例如${a}asdfa${b}s,可以分隔为数组["", 'asdfa', "", s]

7. 原始字符串

可以使用默认的 String.raw 标签函数:

// Unicode 示例
// \u00A9 是版权符号
console.log(`\u00A9`); // © 
console.log(String.raw`\u00A9`); // \u00A9 

好奇怪,但是不知道哪里奇怪

3.4.7 Symbol 类型

ES6 新增的数据类型。表示唯一的值

1. 符号的基本用法

通过Symbol函数初始化

let sym = Symbol();
console.log(typeof sym); // symbol

调用 Symbol 函数时,可以传入对符号的描述,但是这个不会影响字符串本身

这种定义方法是唯一的方法

2. 使用全局符号注册表

(卑微跳过)等二刷 ES6 的时候再重新写

3.4.8 Object 类型

在 ECMAScript 中,Object 类型是所有它的实例的基础,即 Object 类型所具有的任何属性和方法都是存在更具体的对象中。

  • constructor:保存着用于创建当前对象的函数。
  • hasOwnProperty:用于检查给定的属性在当前对象实例中是否存在。参数的属性名必须以字符串形式指定。
  • isPrototypeOf:用于检查传入的对象是否是当前对象的原型。
  • propertyIsEnumerable:用于检查给定的属性是否能够使用for-in语句累枚举。参数的属性名必须以字符串形式指定。
  • toLocaleString():返回对象的字符串,该字符串与执行环境的地区对应。
  • toString():返回对象的字符串表示。
  • valueOf():返回对象的string、number、boolean表示。通常与toString()方法的返回值相同。