红宝书学习笔记(1)

红宝书第三章部分笔记

1 var、let、const的区别

letconst都是ES6引入的,下面是它们和var的区别。

  • 使用var声明的变量只有全局作用域和函数作用域,使用letconst声明的变量存在局部作用域。
  • 在全局作用域中使用var声明的变量会保存在window对象中,使用letconst声明的变量不会。
  • 使用var声明的变量具有变量提升,letconst没有。因此letconst必须先声明后使用(在letconst声明之前使用变量的这个瞬间称作暂时性死区,这时会抛出ReferenceError),var声明变量可以在使用变量之后。
  • 同名变量可以使用var声明多次,在变量提升后这些声明会合并。由于letconst没有变量提升,因此无法判断此前是否声明过,所以在同一作用域使用letconst重复声明变量会报错。

此外,在for循环中使用varlet会有不同的行为。

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

出现这种情况的原因是var声明的变量是存在于全局的,而setTimeout是异步调用的,会在for循环结束后调用,此时i的值已经是5了,所以打印了5个5。而对于let来说,由于for循环的圆括号是一个局部作用域,每次循环时JavaScript引擎会在后台声明一个新的j,因此每次setTimeout触发时都是在不同的作用域,引用的是不同的j。为了证明是在不同作用域调用setTimeout,且声明了不同的j,我们可以将代码改造成这样:

for (let j = 0; j < 5; j++) {
	let k = j;
	setTimeout(() => console.log(k), 0);
}
// 0, 1, 2, 3, 4

上面代码输出同样的结果。这说明每次使用let声明k的时候都是在不同的作用域,否则根据let不能同一作用域重复声明变量的规定,就会报错。并且,我们可以推断JavaScript引擎会在后台记录下每次循环时j的值,在下次进入循环体前声明新的j,调用j++做完判断后再进入循环体。下面的代码打印不同的结果。

for (let j = 0; j < 5;) {
	setTimeout(() => console.log(j), 0);
	j++;
}
// 1, 2, 3, 4, 5

这说明圆括号里面的j++和上一次循环体是属于不同的作用域的。
constlet唯一的区别在于const必须在声明的同时初始化,否则会报错。因为const只能赋值一次。所以上面的代码如果改成const会报错(j++会改变j的值)。
由于letconst存在块级作用域,因此下面的做法是无意义的。

if (typeof num === undefined) {
	let num; // 本意是想确保 num 被声明后再赋值,但是由于块级作用域的存在使得 num 是一个局部变量
}
num = 3; // 报错

2 数据类型

JavaScript的数据类型分为基本数据类型复杂数据类型基本数据类型共有6种,分别是NumberStringBooleanNullUndefinedSymbol(ES6新增)。复杂数据类型则指的对象(Object)。

2.1 基本数据类型

1、NullUndefined
null表示空指针,undefined表示未定义。在ES3时只有null而没有undefinedundefined保存在window对象中。

null == undefined; // true
typeof null === "object"; // true
typeof undefined === "undefined"; // true

所有声明但未赋值的变量都有一个默认值为undefined,因此不必特意赋值为undefined。此外,由于这一点也无法使用typeof来判断一个变量是否被声明。

typeof num === "undefined"; // num 未声明,typeof也返回 undefined

因此,应该尽可能保证在赋值的时候初始化,这样可以使用typeof判断变量是否被声明。初始化时,首先使用const,如果确认值需要改变再使用let,这样有利于纠错,防止不知不觉间改变了变量的值。如果确认变量的类型是对象,应该将初始值赋值为null
2、Boolean
Boolean只有两个值:truefalse。在一些判断语句中,会将表达式自动转化成Boolean类型。比如if语句。除了下面这些转化为false,其余都为true

  • null
  • undefined
  • false
  • 0
  • 空字符串
  • NaN

也可以使用Boolean强行转化其他类型的值:

Boolean('123'); // true
Boolean(NaN); // false

3、Number类型
Number常见的进制是十进制、二进制、八进制和十六进制。默认都是十进制,如果有特定前缀则可能为其余进制,如下:

123 // 十进制
0b0101 // 二进制
0o0037 // 八进制
0x0012 // 十六进制

这里面需要注意的是八进制,在浏览器中,如果开头为0,分为两种情况:

  • 如果后面的数字中有大于7的数,则表示十进制
  • 如果后面的数字中没有大于7的数,则为八进制

如下:

034 // 八进制,在 chrome 控制台显示为十进制 28
078 // 十进制 78

注:上述写法只对整数有效,如果是浮点数则会报错。如 00.89会报错。

为了使得表达更明确,我们应该始终避免用047这样的表达方式,而是使用0o47
还有一点需要注意的是,为了节省内存,JavaScript会尽可能的省略掉无意义的0。如下:

00000089 // 显示为 89,前面的 0 被省略
89.000   // 显示为 89, 后面的 0 被省略,内部当做整数保存
89.01000 // 显示为 89.01

JavaScript内部所有数值都使用浮点数保存。在进行运算的时候再尝试转为整型。由于JavaScript使用IEEE754浮点数标准,因此会有精度问题:

0.1 + 0.2 // 0.30000000000000004
0.1 + 0.2 === 0.3 // false

JavaScript保存的数值的范围在Number.MIN_VALUENumber.MAX_VALUE之间。如果超出Number.MAX_VALUE则显示为Infinity,最小值精度超出Number.MIN_VALUE则显示为0。

Number.MIN_VALUE // 5e-324
5e-325 // 0
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MAX_VALUE * 2 // Infinity
-Number.MAX_VALUE * 2 // -Infinity

JavaScript最高精度为17位,超出部分会四舍五入。

0.12345678901234568 // 0.12345678901234568
0.123456789012345688 // 0.12345678901234569
0.123456789012345684 // 0.12345678901234568
0.123456789012345685 // 0.12345678901234569
1.12345678901234568 // 1.1234567890123457
123456789012345678 // 123456789012345680

我们可以使用Number()来强行将其他类型的值转化为Number类型。规则如下:

  • Number类型的直接返回拷贝值(无意义的0会被忽略)
  • Boolean类型的,true返回1,false返回0
  • null返回0
  • undefined返回NaN
  • 字符串类型的,如果是空字符串则返回0,如果是符合数值格式的,则返回对应的十进制值,并忽略掉无意义的0(开头可以有正负号)。其余情况返回 NaN。
Number(23); // 23
Number(000023); // 19 八进制
Number(00099); // 99
Number(00098.8900); // 98.89
Number(true); // 1
Number(false); // 0
Number('0x0012'); // 18
Number('0x0012x'); // NaN
Number(''); // 0
  • 如果是对象,则先调用对象的valueOf()方法,对返回值进行转化,如果无法转化为数值,再调用toString()方法,对返回值进行转化。

parseIntparseFloat用于将字符串转化为数值。它们按照一个字符一个字符进行判断。并且会忽略掉无意义的0.
parseInt接受两个参数,第一个为需要转化的字符串,第二个为进制。默认为十进制。规则如下:

  • 如果第一个字符为非数字且非正号、非负号,则直接返回NaN
  • 空字符串返回NaN
  • 如果第一个字符为数字或正号、负号,则依次判断之后的字符是否为数字,直到碰到第一个非数字字符或者碰到字符串结尾,结束判断并将结果返回
  • 如果字符串符合进制,如0x12则当做对应进制转化为十进制。可以传第二个参数显式的指定进制。如果第一个参数不符合第二个参数指定的进制,则返回NaN
parseInt('a'); // NaN
parseInt('-9'); // -9
parseInt('+34'); // 34
parseInt('0098'); // 98
parseInt('23abc'); // 23
parseInt('0x0011'); // 17
parseInt('a', 16); // 10

parseFloat只存在十进制,和parseInt一样,依次判断每个字符。第一个字符可以为数字,正负号和小数点。第二个字符开始只能为数字和小数点,并且小数点只能出现一次。

parseFloat('8.3float'); // 8.3
parseFloat('.4'); // 0.4
parseFloat('0.44.5'); // 0.44
parseFloat('10.00'); // 10
parseFloat('00234.0'); // 234

这里有一道经典面试题

["1", "2", "3"].map(parseInt); // [1, NaN, NaN]
["1", "2", "3"].map(parseFloat); // [1, 2, 3]

这块考察3个知识点,一个是map的传参,另两个是关于parseIntparseFloat。首先,map需要传入一个回调函数,这个函数可以接收三个参数,第一个参数是数组每一项的值,第二个参数为数组每项下标,第三个参数是数组本身。因此上面两个题可以简化为:

parseInt("1", 0); // 1
parseInt("2", 1); // NaN
parseInt("3", 2); // NaN
parseFloat("1"); // 1
parseFloat("2"); // 2
parseFloat("3"); // 3

4、String类型
在JavaScript中,可以有三种方式表示一个字符串。分别是"'、`。几种表达方式没有太大区别。

'abc' === "abc"; // true
'abc' === `abc`; // true
"abc" === `abc`; // true

这几种引号可以同时使用。

"'abc'"; // 'abc'
'"abc"'; // "abc"
`'abc'`; // 'abc'
`"abc"`; // "abc"

但是以什么引号开头必须以相同类型引号结尾。

"abc'; // 报错
"abc`; // 报错

如果在引号里面需要使用同一类型的引号需要使用\转义。

"\"abc"; // "abc
'\'abc'; // 'abc
`\`abc`; // `abc

将其余类型转化为字符型主要有三种方法,分别是toStringString和使用+号。

// 除了 null 和 undefined 以外都可以用 toString,toString方法是从 Object 上继承过来的
/*
 *	1. 字符类型使用 toString 直接返回自身拷贝
 *	2. 数值、布尔值使用 toString 则先自动转为基本包装类型,再调用 toString 
 *	3. 对象调用 toString 时,如果没有重写 toString 方法,则普通对象返回 [object Object],数组则将每个元素使用toString方法转成字符串,再使用 , 连接成最后的字符串。其余类型各自使用自己内部重写的 toString 方法。
 */
 
// 使用 String() 转字符串时,规则如下
/*
 * 1. 如果是 null 或者 undefined,返回 'null' 或 'undefined'
 * 2. 其余类型调用 toString 方法
 */
 
// 使用 + 号
/*
 * 只要加号两边有一个是字符串,则会对另一个使用 String() 转成字符串,再进行拼接
 */

注:toString()里面还可以传入一个数字,用来表示进制。可以使用这个方法将数字转化为特定的进制。如果不传则默认转为十进制。

2.2 模板字符串

模板字符串可以插入变量

let str = '你好';
console.log(`${str}, 世界!`); // 你好, 世界!

模板字符串会保留空格和换行符

`<table>
	<thead>
		<tr>
			<td>单元格</td>
		</tr>
	</thead>
</table>`
// 输出
/*
<table>
	<thead>
		<tr>
			<td>单元格</td>
		</tr>
	</thead>
</table>
*/

字符串里面的\n之类的转义字符如果需要原样打印,除了可以使用/再次转义外还可以使用String.raw

`first line\nlast line`;
// 输出
/*
first line
last line
*/
String.raw`first line\nlast line`;
//输出
// first line\nlast line

模板字符串还可以使用标签函数。

function fn(strings, ...args) {
	console.log(strings, args);
}
let str1 = "str1",
	str2 = "str2",
	str3 = "str3";
fn`字符串1${str1}字符串2${str2}字符串3${str3}`;
// ["字符串1", "字符串2", "字符串3", "", raw: Array(4)] ["str1", "str2", "str3"]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值