红宝书学习笔记【第3章】

这一章记得太乱了,有点儿没分重点,之后把重点分出来重写一下笔记吧。。。
主要的重点就是:

  1. let const var
  2. 类型转换 和 运算规则
  3. Symbol

语法

  1. js 区分大小写
  2. 标识符: (字母|_|$) + (字母|_|$|数字)
  3. 注释: 可使用 单行注释//, 也可以使用块注释/* */
  4. 严格模式: 在脚本开头或函数体开头加上"use strict";, 这是一条预编译指令;
  5. 如果语句末尾不加分号, 解析器会尝试在合适的位置补上分号以纠正语法错误.

变量

var 关键字

基础用法不赘述了

1. var 声明作用域

使用 var 声明的变量会成为包含它的函数的局部变量, 并跟随函数退出而销毁

function test() {
  var message = 1
  console.log(message) // 1
}
test()
console.log(message) // ReferenceError: message is not defined

注: 在 js 中可以不写声明操作符, 直接定义变量, 这种情况会创建全局的变量, 但这种情况在严格模式下会报错(ReferenceError), 并且在局部作用域定义全局变量很难维护, 不推荐这种用法.

function test() {
  message = 1
}
test()
console.log(message) // 1

2. var 声明提升

所谓"提升", 就是把所有使用 var 的变量声明放在函数作用域的顶部, 并将多个同名的变量声明合并为一个, 所以在同一作用域中反复用 var 声明一个相同的变量名, 不会报错

function test() {
  var message = 1;
  var message = 2;
  var message = 3;
  console.log(message);
}
test() // 3

let 关键字

1. 作用域

let 和 var 最明显的区别是: let 声明的范围是块作用域, var 声明的范围是函数作用域.

let 不允许在同一块作用域内出现其他相同名称的变量声明 (SyntaxError).

2. 暂时性死区

let 声明的变量不会在作用域内提升.
在解析代码时, js 引擎会注意到块后面的 let 声明, 但在 let 声明之前的执行瞬间被称为 “暂时性死区”, 在此阶段引用任何之后才声明的变量都会抛出 ReferenceError.

3. 全局声明

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

4. for 循环中的 let

for (var i = 0; i < 10; i++) {
  setTimeout(() => console.log(i), 0); // 全是10
}
console.log(i) // 10

for 循环使用 var 迭代变量 会渗透到循环体外部, 而且因为 变量合并 和 事件循环 的原因, for 内部的输出结果也会都输出为 10.

for (let i = 0; i < 10; i++) {
  setTimeout(() => console.log(i), 0); // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
}
console.log(i) //  ReferenceError: i 没有定义

使用 let 声明迭代变量时, js 引擎会在后台为每个迭代循环声明一个新的迭代变量, 所以输出结果与使用 var 时不同.

const 关键字

const 的行为与 let 基本相同. 区别在于 const 声明变量时必须同时初始化变量, 且不能重新赋值.

此处重新赋值指不修改变量的引用. 如果使用 const 声明一个对象, 修改对象内的属性不会改变对象的引用.

数据类型

es 有6中简单数据类型(原始类型): Undefined, Null, Boolean, Number, String, Symbol(ES6新增).
以及一种复杂数据类型: Object.

typeof 操作符

typeof 操作符返回一个表示数据类型的字符串.

  • “undefined” 表示未定义
  • “boolean” 表示布尔值
  • “string” 表示字符串
  • “number” 表示数值
  • “object” 表示对象或null
  • “function” 表示函数
  • “symbol” 表示符号

注:

  1. typeof null会返回 “object”, 因为 null 被认为是对空对象的引用.
  2. 函数在 es 中也被认为是对象, 而不是一种数据类型.

Undefined 类型

Undefined 类型只有 undefined 一个值.
当使用 var 或 let 声明一个值, 但没有初始化时, 该变量值为 undefined.

一般来讲, undefined 只用来比较, 不用来赋值. 该值的目的是为了正式明确 空对象指针 null 和 未初始化变量 的区别.

Null 类型

Null 类型只有 null 一个值.
null 值表示一个空对象指针, 所以 typeof null 会返回 “object”.

Boolean 类型

Boolean 类型只有 truefalse 两个字面值.

任何类型的数据都可以通过 Boolean() 转换为布尔值.
转换规则:

  • String类型: 非空字符串 => true, “” => false;
  • Number类型: 非零数值(包括无穷值) => true, 0 和 NaN => false;
  • Object类型: 任何对象 => true, null => false;
  • Undefined类型: undefined => false;

Number 类型

js 中的证书和浮点数都采用 IEEE 754 格式表示.

8进制数在 js 中用0o作为前缀(非严格模式下可采用0作为前缀). 如果数字部分出现了超出范围的数字, js 会忽略前缀, 将数字当做十进制数.

16进制数在 js 中用0x作为前缀.

1. 浮点数

es 会尝试将浮点数转换为整数. 例如小数点后没有数字, 或小数点后只有0, 数值会变成整数.

2. 值的范围

可表示的最大数保存在Number.MAX_VALUE, 最小数保存在Number.MIN_VALUE.

超出该范围的数, 会转换为Infinity-Infinity.
Infinity 无法参与数学运算, 如果要判断一个数是否是Infinity, 可使用isFinite()函数.

3. NaN

NaN是啥
NaN 表示"不是数值", 表示本该返回数值的操作失败(不会抛出异常).
其他语言中, 0/0 会报错, 而在 js 中会返回 NaN.

如果分子(被除数)为非0的数, 此处会返回Infinity.

NaN 的特性

  1. 任何涉及 NaN 的数学运算都会返回 NaN.
  2. NaN == NaN会返回false

isNaN()
isNaN()函数用来判断传入的参数是否"不是数值".
该函数会先尝试将参数转换为数值类型, 然后返回判断结果.

4. 数值转换

将非数值转为数值的函数: Number()parseInt()parseFloat()

Number()

  • 数值类型 直接返回.
  • 布尔类型 true转为1, false转为0
  • null 返回0
  • undefined 返回NaN
  • 字符串
    • 空字符串 返回0.
    • 字符串中只包含数字字符, 或以正负号开头, 剩余部分只包含数字字符, 返回字符串对应的数值, 忽略开头的0.
    • 字符串表示为有效的浮点数, 返回对应的浮点数值.
    • 字符串表示为有效的十六进制数, 返回该十六进制数的十进制格式数值.
    • 其他情况返回NaN.
  • 对象 先调用对象的valueOf()方法, 将返回值根据上述规则转换, 如果转换结果为NaN, 调用对象的toString()方法, 将返回的字符串根据字符串的规则转换.

parseInt()

专用于字符串转换整数.

转换规则:

  1. 去除开头的非空格字符, 从第一个非空格字符开始转换
  2. 第一个字符不是数字或正负号, 返回NaN.
  3. 当第一个字符是数字或正负号, 依次检查之后的字符, 知道遇到非数字字符或结尾. 例如: “1234blue”=>1234

注: parseInt会将空字符串转换为NaN, 而Number会将空字符串转换为0.

其他进制转换方式:
parseInt的第二个参数接收一个数字类型的参数, 表示解析字符串的底数. 不传时表示让js自己判断解析的进制.

parseFloat()

与parseInt()类似, 都是从位置 0 开始检测每个字符, 解析到字符串结尾或第一个无效的浮点数值字符.
只有第一个小数点有效, 第二个小数点和之后的内容都无效.

只解析十进制值,因此不能指定底数.

如果没有小数部分, 该函数返回整数.

String 类型

字符串类型表示 零或多个16位 Unicode 字符序列.
可以用( ’ ), ( " ), ( ` )包裹字符串.

字符串的特点

es中的字符串是不可变的. 例如:

let lang = "java";
lang = lang + "script";

上述代码中, js 先创建了一个字符串"java". 在之后的拼接操作中, js 会先分配足够容纳10个字符的空间, 然后填充上"java" 和 “script”. 最后销毁原有的"java" 和 "script"字符串.

该特性导致早期的浏览器拼接字符串非常慢.

类型转换

将数据转换为字符串类型有两种方式.

  1. toString(), 该函数几乎所有值都有, 除了null和undefined. 用于返回当前值的字符串等价物.
    除了数值类型, 其他类型的toString方法都不能传参.
    数值类型的toString可传入转字符串时的进制, 例如: let num = 10; num.toString(2) => “1010”.
  2. String()
    如果值有toString()方法, 则调用该方法并返回结果.
    如果值是null, 返回"null".
    如果值是undefined, 返回"undefined".

模板字面量

es6 新增了模板字面量定义字符串的能力.

该方式定义的字符串保持换行和内部空格, 需额外注意字符串长度和内容.

模板字面量插值

模板字面量不是字符串, 而是一种特殊的 js 句法表达式, 只是求值后返回了字符串.

模板字面量在定义时立即求值并转换为字符串实例,任何插入的变量也会从它们最接近的作用域中取值。

let a = "123"
let b = "456"
let c = a + b;
let d = `${a}${b}`
console.log(c === d)  // true

所有插入的值会使用toString()转换为字符串.

插值中可以写任意合法的 js 表达式, 包括函数调用、对象属性访问等.

模板字符串标签函数

标签函数本身是一个常规函数,通过前缀到模板字面量来应用自定义行为.
标签函数收到的参数一次是原始字符串数组和每个表达式求职的结果.

let a = 6; 
let b = 9; 
function simpleTag(strings, aValExpression, bValExpression, sumExpression) { 
 console.log(strings); 
 console.log(aValExpression); 
 console.log(bValExpression); 
 console.log(sumExpression); 
 return 'foobar'; 
} 
let untaggedResult = `${ a } + ${ b } = ${ a + b }`; 
let taggedResult = simpleTag`${ a } + ${ b } = ${ a + b }`; 
// ["", " + ", " = ", ""] 
// 6 
// 9 
// 15 
console.log(untaggedResult); // "6 + 9 = 15" 
console.log(taggedResult); // "foobar"

Symbol 类型

es6 新增的数据类型.
原始值类型, 且符号实例是唯一、不可变的.
用于确保对象属性使用唯一标识符, 不会发生属性冲突的危险.

1. 基本用法

符号需要用Symbol()函数初始化.
可以传入一个字符串参数作为符号的描述, 但该参数与符号定义或表示无关.

let genericSymbol = Symbol(); 
let otherGenericSymbol = Symbol(); 
let fooSymbol = Symbol('foo'); 
let otherFooSymbol = Symbol('foo'); 
console.log(genericSymbol == otherGenericSymbol); // false
console.log(fooSymbol == otherFooSymbol); // false

符号没有字面量语法,这一特性保证了用它作为对象的新属性时不会和已有的对象属性冲突.

Symbol() 函数不能用 new 的方式调用, 它不是构造函数, 无法像其他数据类型一样通过 new 的方式创建包装对象.

2. 全局符号注册表

如果需要再不同部分重用符号实例, 可使用Symbol.for()方法, 在全局符号注册表中创建和重用符号.

let fooGlobalSymbol = Symbol.for('foo'); 
let otherFooGlobalSymbol = Symbol.for('foo'); 
console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true

即使采用相同符号描述, 通过全局注册表定义的符号和使用Symbol()定义的符号也不同.

let localSymbol = Symbol('foo'); 
let globalSymbol = Symbol.for('foo'); 
console.log(localSymbol === globalSymbol); // false

全局注册表的符号必须用字符串键来创建, 因此传入Symbol.for()的参数都会转为字符串, 且该键值同事被用作符号描述.

还可以通过Symbol.keyFor()来查询全局注册表中的符号. 该方法接收符号, 返回字符串键, 如果符号不是全局符号, 则返回undefined.

// 创建全局符号
let s = Symbol.for('foo'); 
console.log(Symbol.keyFor(s)); // foo 
// 创建普通符号
let s2 = Symbol('bar'); 
console.log(Symbol.keyFor(s2)); // undefined

3. 使用符号作为属性

凡是可以使用字符串或数值作为属性的地方, 都可以使用符号.

Object.getOwnPropertyNames()返回对象实例的常规属性数组.
Object.getOwnPropertySymbols()返回对象实例的符号属性数组.
这两个函数的返回值, 彼此互斥.

Object.getOwnPropertyDescriptors()会返回同时包含常规和符号属性描述符的对象.
Reflect.ownKeys()会返回两种类型的键.

可以通过符号的toString()方法获取符号的描述, 从而在函数执行时区分符号.

4. 常用内置符号

用于暴露语言内部行为, 开发者可以直接访问、重写或模拟这些行为.这些内置符号都以 Symbol 工厂函数字符串属性的形式存在。
所有内置符号属性都是不可写、不可枚举、不可配置的。

例如:

  • Symbol.asyncIterator
    对象的该属性是一个方法, 返回对象默认的 AsyncIterator, 由for-await-of语句使用.

  • Symbol.hasInstance
    对象的该属性是一个方法, 该方法决定了一个构造器是否认可一个对象是它的实例, 由instanceof使用.

  • Symbol.iterator
    返回对象默认的迭代器, 由for-of语句使用.

Object类型

es 中的对象是一组数据和功能的集合.

es 中的 Object 是派生其他对象的基类.
Object 类型的所有属性和方法, 在其他对象中同样存在. 例如:

  • constructor: 记录创建当前对象的函数.
  • hasOwnProperty(propertyName): 判断当前对象实例上是否存在给定的属性.
  • isPrototypeOf(object): 判断当前对象是否是另一个对象的原型.
  • propertyIsEnumerable(propertyName): 判断给定的属性是否可用for-in语句枚举.
  • toLocaleString: 返回对象的字符串表示, 该字符串与执行环境地区对应.
  • toString: 返回对象的字符串表示.
  • valueOf: 返回对象的字符串、数值或布尔值表示, 通常与toString()方法的返回值相同.

注: 浏览器环境的 BOM 和 DOM 对象是由宿主环境定义和提供的宿主对象, 宿主对象不受es约束, 所以可能不会继承自Object.

操作符

一元操作符

1. 自增自减操作符

基本用法不赘述

自增减操作符可用于任何值, 不只整数.
作用规则:

  • 对于字符串, 转为数值类型后自增减, 如果无法转为有效数值返回NaN;
  • 对于布尔值, true转为1, false转为0, 然后自增减;
  • 对于对象类型, 调用其valueOf方法后, 将得到的值进行自增减, 如果调用valueOf方法得到的是NaN, 再调用toString方法, 然后对于字符串规则.

2. 一元加和减

转换正负

如果操作数不是数字, 执行和使用Number()一样的类型转换后, 再做运算.

位操作符

js中整数用原码保存, 负数用补码存

  1. 按位非 (~)
  2. 按位与 (&)
  3. 按位或 (|)
  4. 按位异或 (^)
  5. 左移 (<<) 符号位不移动
  6. 有符号右移 (>>) 符号位不移动
  7. 无符号右移 (>>>) 符号位移动

布尔操作符

  1. 逻辑非 (!)
    如果操作数不是布尔值, 将操作数根据Boolean()函数的规则转换为布尔值, 然后取反.
  2. 逻辑与 (&&)
    逻辑与运算不一定返回布尔值.
    该运算符会产生短路现象, 即如果第一个操作数能决定表达式的结果, 就不会对第二个操作数求值.
  3. 逻辑或 (||)
    逻辑或运算也不一定返回布尔值.
    逻辑或运算同样存在短路现象.

乘性操作符

es中的乘性操作符包括: 乘法, 除法, 和取模.

当操作数为非数值时, 会自动的将操作数用Number()函数转换为数值.

1. 乘法 (*)

计算两个数值的乘积.

特殊值的处理:

  • 如果操作数都是数值, 则按照常规的乘法计算. 如果乘积超出范围, 用无穷大表示.
  • 任意操作数是NaN, 则结果为NaN.
  • Infinity * 0 = NaN
  • Infinity * Infinity = Infinity
  • Infinity * 非0数字 = Infinity

2. 除法 (/)

特殊值的处理:

  • 超出范围用无穷大表示, 且任一操作数为NaN, 则结果为NaN.
  • Infinity / Infinity = NaN
  • 0 / 0 = NaN
  • 非0有限值 / 0 = ±Infinity
  • Infinity / 非0有限值 = ±Infinity

3. 取模(%)

取得除法的余数.

特殊值的处理:

  • Infinity % 有限非0值 = NaN
  • Infinity % Infinity = NaN
  • 0 % 0 = NaN
  • 有限非0值 % 0 = NaN
  • 有限值 % Infinity = 被除数

指数操作符

es7 之前计算指数需借助Math.pow()函数.
es7 引入了指数操作符**.
也可以使用指数赋值操作符**=.

加性操作符

即加减法操作符.
与乘性操作符类似,加性操作符在后台会发生不同数据类型的转换。

  • NaN + 任何值 = NaN
  • Infinity + -Infinity = NaN
  • ±0 一律按 0 算
  • 任一操作数是字符串, 都表示字符串拼接操作.
  • 加法运算: 非数字和非字符串的操作数, 对其进行String()转化后获取字符串, 然后拼接.
  • 减法运算: 非数字的操作数, 先在后台使用Number()转换为数值; 如果任意操作数是对象, 先调用其valueOf()方法, 没有valueOf()方法就调用toString(), 再根据上述规则进行计算.

关系操作符

包括 大于, 小于, 大于等于, 小于等于.
返回值都是布尔值.

比较规则:

  • 如果操作数都是数值, 则数值比较.
  • 如果操作数都是字符串, 则比较字符串中对应字符的编码值( 大写字母小于小写字母 ).
  • 如果任一操作数为数值, 将另一操作数转数值后比较.
  • 如果任一操作数是对象,则调用其valueOf()方法, 没有则调用toString()方法, 然后根据上述规则比较.
  • 布尔值需要转换为数值比较.
  • 涉及到 NaN, 一律返回false

相等运算符

等于和不等于

== 和 != 操作符会进行类型转换后比较.
两个操作符都会先进行强制类型转换.

类型转换规则:

  • 布尔值 转换为 数值 比较.
  • 字符串 和 数值比较时, 将字符串转数值比较.
  • 一个操作数是对象, 另一个不是时, 调用valueOf()方法取得原始值再比较.
  • null == undefined true
  • null 和 undefined 不做转换.
  • 存在 NaN, 等式值为false
  • 两个操作数都是对象时, 比较是不是同一对象.

全等和不全等

全等和不全等不做类型转换, 直接比较.

条件操作符/三目运算符( ? : )

赋值运算符

将右侧的值赋给左侧的变量.

let a = 1

每个数学操作符都有对应的符合赋值操作符:
+=, -=, *=, /=, %=, <<=, >>=, >>>=, &=, ^=, **=
但这些操作符只是简写, 不能提升性能

逗号操作符

逗号操作符可以用来在一条语句中执行多个操作

let num1 = 1, num2 = 2, num3 = 3
let num = (1, 2, 3, 4, 5);  // num = 5

语句

switch语句分支的判断条件是全等.

函数

函数名和参数不能叫evalarguments.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值