语法
标识符
ECMAScript中的一切都区分大小写,如变量名、函数名、操作符。
标识符,就是被操作符定义过的字符串,比如var a = 1, a被操作符var定义, a就是标识符。
标识符包括:变量名、函数名、属性名、函数的参数名。
标识符第一个字符:字母、下划线、$
标识符其余字符:字母、下划线、$、数字, 只在第一个字符基础上多了一个数字类型
标识符名字,建议采用小驼峰形式,变量名,函数名,属性名,参数名都采用小驼峰命名方式。
注释
JavaScript的单行注释是双斜杠 //
JavaScript的多行注释是单斜杠+星号开始,星号+单斜杠结束。中间行的星号可选,目的是为了增加可读性。
/*
* 块级注释
*/
严格模式
严格模式为JavaScript定义了不同的解析和执行模型。主要体现在
- ECMAScript3中的不确定行为得到处理
- 不安全的操作会抛出错误
严格模式需在代码顶部或者函数顶部写上“use strict”
语句
应在每个语句后面加上分号,有以下3个好处
- 避免输入不完整造成错误;
- 代码结尾处没有分号会导致压缩错误;
- 解析器不用花时间推测应该在哪儿插入分号,可增进代码性能。
var sum = a + b // 即使没有分号也是有效的语句 ———不推荐
var diff = a - b; // 有效的语句 ————推荐
始终在控制语句中使用代码块——即使代码块中只有一条语句
- 可以让编码意图更加清晰
- 降低修改代码时出错的几率
if (test) alert(test); // 有效,但容易出错,不要使用
if (test) { // 推荐使用
alert(test);
}
关键字和保留字
关键字具有特定用途,主要分为两大类
一、控制语句的开始/结束:break、continue、switch、case、if、while、for等
二、执行特定操作:var、function、typeof、new、instanceof等
保留字有可能在将来作为关键字,目的是兼容未来ECMAScript版本
class、enum、import、let、const
变量
ECMAScript中的变量是松散类型,可保存任何类型的数据。
用var操作符定义的变量,将成为定义该变量的作用域中的局部变量。
省略var,变量就成了全局变量。只要调用过一次函数,里面的变量就有了定义,在函数外部任何地方都能访问到
如果仅仅在函数中定义了变量,但是没有调用函数,里面的变量是不会被定义的
function fn() {
msg = "hi";
}
// ReferenceError: msg is not defined
console.log("msg ===", msg);
但如果被调用了一次,里面的变量就有了定义,在函数外部任何地方都能被访问
function fn() {
msg = "hi";
}
fn() // 调用了,里面变量才会被定义
console.log("msg ===", msg);
这样定义会造成全局污染,垃圾回收机制也不会标柱并回收。
数据类型
ECMAScript中有5种简单数据类型+1种复杂数据类型
5种简单数据类型:Undefined、Null、Number、String、Boolean
1种复杂数据类型:Object
类型是首字母大写的单词
typeof操作符
undefined——值未定义
boolean——布尔值
string——字符串
number——数值
object——对象或者null
function——函数
注意 ⚠️
- 对typeof null,返回的是object,是因为null被认为是一个空对象的引用。
- 只声明,未初始化;未声明;这两种情况经过typeof检测,都返回undefined
var a;
// var b;
console.log("typeof a : ", typeof a); // undefined 只声明,未初始化
console.log("typeof b : ", typeof b); // undefined 没有声明,未定义
Undefined类型
只有一个值:undefined
只声明,但没有初始化,默认值就是undefined
Null类型
只有一个值:null
表示空指针对象;保存对象的变量还没真正保存对象,就该明确赋值为null
Boolean类型
只有两个字面值:true和false
Boolean()——将任意一个值转为Boolean值
| 数据类型 | 转换为true的值 | 转换为false的值 |
|---|---|---|
| Boolean | true | false |
| String | 任何非空字符串 | 空字符串 |
| Number | 任何数值 | 0和NaN |
| Object | 任何对象 | null |
| Undefined | / | undefined |
Number类型
八进制:第一位必须是0,后面是0~7;如果数值超出,当十进制解析
070 : 对应十进制是56,计算为 7 * 8 = 56
079:9超出范围,直接解析为十进制79
十六进制:前两位必须是0x,后跟0~9及AF/af
0xA : 对应十进制是10,计算为 A=10
0x1f : 对应十进制是10,计算为 1*16+15=31, 其中f = 15
计算时,八进制和十六进制都会被转为十进制数值
浮点数值
浮点数值的最高精度是17位小数,进行算术计算时其精确度远远不如整数。
0.1+0.2=0.30000000000000004
知识点1: JS内部计算都是以二进制进行的
整数部分:除2取余+逆序排列,直到商为0
比如8,不断除以2,
知识点2: Number类型使用IEEE754标准64位存储
双精度浮点数(double)
系统为每个数值分配64位存储空间,以科学记数法的方式存储
知识点3: IEEE754标准64位数值相加
相加时,对第53位进行截取到52位,会发生第二次精度丢失。再把二进制转为十进制,这个值就是0.30000000000000004
为什么0.1+0.2 不等于 0.3
JS 内部计算都是以二进制进行的,Number类型使用64位存储,即双精度浮点数。
64位存储结构:第一位是符号位,紧跟着是11位指数位,剩余是52位小数位。
0.1在内存中的小数部分存储结构是1001的循环,第53位是1。内存中小数部分最多只能存储52位,第53位要进行进位截取,这里第一次丢失精度。
进行计算时,采用标准64位数值相加。此时因为内存大小限制,第二次丢失精度。
所以最后计算出的二进制,转为十进制就是0.30000000000000004
在实际开发中,为了避免计算值不准确,一般会扩大倍数计算,再缩小到原来的倍数。
数值范围
最小值:Number.MIN_VALUE,5e-324
最大值:Number.MAX_VALUE, 1.79e+308
NaN
NaN与任何值操作都是NaN,NaN/10返回NaN
NaN与任何值都不相等,NaN不等于NaN
isNaN(), 判断参数是否为非数值,先把值转为数值类型,不能转的返回true
10,返回false
“10”, 返回false, 因为会先把字符串10,转为数值10,再判断是否为非数值
“blue”, 返回true, 是非数值,因为不能转为数字
true, 返回false, 不是非数值。先转为1,再判断
数值转换
Number()
可用于任何数据类型,只有一个参数
转换规则
- 数字值:简单的传入和返回
- Boolean类型:true转为1,false转为0
- null和undefined: null,转为0;undefined,转为NaN
- 字符串(前导0被忽略)
-
- 只包含数值,包括正负号和浮点格式:转为十进制。"123"转为123,“011”转为11,“1.2”转为1.2
- 十六进制格式,转为十进制。“0xf”转为15
- 空字符串,转为0。 "" 转为0
- 其余,转为NaN。"test转为NaN,“12xx”转为NaN
- 对象
-
- 调用对象的valueOf(),再按照1~4点规则转换valueOf()返回的值
- 如果转换结果是NaN,调用对象的toString(),再按照1~4点规则转换
Number(true); // 1
Number(null); // 0
Number(undefined); // NaN
Number("99"); // 99
Number("99.88"); // 99.88
Number("99Test"); // NaN
Number("99.88Test"); // NaN
Number("0xF"); // 15
Number(""); // 0
var obj = new Date();
console.log(
obj, // Thu Nov 14 2024 13:27:43 GMT+0800 (中国标准时间)
obj.valueOf(), // 1731562063925
Number(obj) // 1731562063925
);
先调用对象的valueOf(),得到一个值,刚好这个值是数值,经过1~4规则对这个值转换后,得到最后的数值。
var obj = [1];
console.log(
obj, // [1]
obj.valueOf(), // [1]
obj.toString(), // "1"
Number(obj) // 1
);
valueOf()转换失败,再用toString()。如果都失败,返回NaN
先用Number()转换的方法
isNaN()
先Number()转为数值,再判断是否是NaN
var obj = new Date();
console.log(
isNaN(null), // 0 false
isNaN(undefined), // NaN true
isNaN(NaN), // 0 true
isNaN(9), // 9 false
isNaN("23"), // 23 false
isNaN("0xf"), // 15 false
isNaN(""), // 0 false
isNaN("xx"), // xx true
isNaN("123xx"), // 123xx true
isNaN(" 123"), // 123 false
isNaN(obj), // 时间戳 false
);
parseInf()
- 专门用于字符串
- 有两个参数,第一个是要转换的值;第二个是按照什么进制来转换,建议加上
- 转换规则
-
- 找到第一个非空字符,第一个非空字符不是数字或负号/正号,就返回NaN
- 空字符串,返回NaN
- 遇到数值字符,依次解析,直到遇到非数值字符停止
- 八进制,转为十进制
- 十六进制,转为十进制
parseInt(" 8"); // 8
parseInt(" +8"); // 8
parseInt(" -8"); // -8
parseInt("9"); // 9
parseInt(""); // NaN
parseInt("99test"); // 99
parseInt("99.88"); // 99
parseInt("070"); // 70
parseInt("0x1F"); // 31
parseFloat()
- 专门用于字符串
- 只有一个参数
- 转换规则,与parseInt() 类似,但只解析十进制
总结对比
开发中,由于parseInt()只能转为整数形式,为了兼容整型和浮点型,用Number()比较多。
但是Numbr()会把boolean类型转为0/1,null转为0,空字符串转为0,undefined转为NaN,十六进制转为十进制
如果确定是整数型,建议使用parseInt()
但是要注意,空字符串转为NaN;八进制和十六进制都会转为十进制
建议加上第二个参数,代表多少进制
console.log(Number("011")); // 11
console.log(parseInt("011")); // 11 结果可能不一样
console.log(Number("0xf")); // 15
console.log(parseInt("0xf")); // 0
console.log(parseInt("0xf", 10)); // 0
console.log(parseInt("0xf", 16)); // 15
- 转换类型:Number可以转换成整型和浮点型,parseInt只能得到整型。如果要求包括对浮点型的转换,选择Number
- 处理非数字字符: 包含非数字字符,Number返回NaN,parseInt会从第一个非空且为数值类型(包括+/-)的字符串提取,遇到非数值字符串停止;如果最开始就是非数字字符串,直接返回NaN
- 空字符串处理:Number对空字符串返回0,parseInt对空字符串返回NaN
- 基数转换: Number可以处理16进制,不能处理八进制;parseInt可以处理二进制、八进制、十进制、十六进制,只需要在第二个参数写上需要按照什么进制来转换。
String类型
1. 字符字面量
特殊的字符字面量,转义序列,用于表示非打印字符,或者具有其他用途的字符。
\n : 换行
\t : 制表
\ : 斜杠\
' : 单引号
" : 双引号
\xnn : \x开头,十六进制代码nn表示的一个字符。\x41表示“A”, 0x41的十进制是65,对应字符“A”
\unnnn : \u开头,十六进制代码nnnn表示的一个Unicode字符。\u03a3表示希腊字符Σ
字符字面量可以出现在字符串中的任意位置,被当作一个字符来解析
6个字符长的转义表示一个字符
字符串.length可以获取字符串的长度
2. 字符串的特点
ECMAScript中的字符串不可变,字符串一旦创建,值就不能改变
要改变一个变量保存的字符串,先要销毁原来的字符串,再用另一个包含新值的字符串填充该变量。
3. 转换为字符串
3.1. toString()
数值、布尔值、对象和字符串都有toString()
null和undefined没有toString()
var num = 11;
var str = "test";
var b = false;
var obj = { a: 123 };
var nullObj = null;
var anyVal;
num.toString(); // '11'
str.toString(); // 'test'
b.toString(); // 'false'
obj.toString(); // '[object Object]'
nullObj.toString(); // TypeError: Cannot read properties of null (reading 'toString')
anyVal.toString(); // TypeError: Cannot read properties of undefined (reading 'toString')
toString()可以传人一个参数,代表输出数值的基数。
数字2,代表以二进制格式返回数值的字符串表示
数字10,代表以十进制格式返回数值的字符串表示,默认
数字8,代表以八进制格式返回数值的字符串表示
数字16,代表以十六进制格式返回数值的字符串表示
3.2. String()
String()转换规则
- 如果值有toString(), 则调用该方法
- 如果值是null,返回字符串“null”
- 如果值是undefined,返回字符串“undefined”
Object类型
对象创建方式:new 对象类型的名称()
对象实例中包含类的属性和方法
Object的每个实例都有以下属性:
- constructor:保存着用于创建对象的函数,即构造函数
- hasOwnProperty(propertyName): 检测属性在当前的对象实例中是否存在(不管实例的原型中是否存在)
- isPrototypeOf(object): 检测传入的对象是否是当前对象的原型
- propertyIsEnumerable(propertyName): 属性是否可以用for-in枚举
- toLocalString(): 返回对象的字符串表示,与地区对应
- toString():返回对象的字符串表示
- valueOf(): 返回对象的字符串、数值或布尔值表示
记忆归类
字符串相关(3):valueOf()、toString()、toLocalString()
属性相关(2):hasOwnProperty()、propertyIsEnumerable()
原型相关(2):constructor、isPrototypeOf()
操作符
操作符 操作 数据值
一元操作符
只能操作一个值
1. 递增和递减操作符
作用于:整数、字符串、布尔值、浮点数值和对象
非数值类型时,先调用Number() 转为数值型,再进行加/减操作
位操作符(未学习)
布尔操作符
1. 逻辑非
! : 对于任何值,先转为布尔值,再取反
!! : 等同于Boolean(),第一个!代表返回一个布尔值再取反,第二个对布尔值求反。得到真正的布尔值。
2. 逻辑与
有一个为false,就返回false
3. 逻辑或
有一个为true,就返回true
逻辑与和逻辑或都不一定返回布尔值,有可能返回多个数据值的其中之一的值
乘性操作符
乘法、除法、求模
如果不是数值,先用Number()转为数值,再进行计算
1. 乘法
console.log(
Number.MAX_VALUE * 2, // Infinity 乘积超出了范围
Number.MAX_VALUE * -2, // -Infinity 乘积超出了范围
8 * NaN, // NaN
Number.MAX_VALUE * 2 * 0, // NaN Infinity 与 0 相乘, 返回NaN
Number.MAX_VALUE * 2 * 5, // Infinity
Number.MAX_VALUE * Number.MAX_VALUE, // Infinity
Number.MAX_VALUE * -Number.MAX_VALUE, // -Infinity
'20' * 4 // 80, 先转为数值,再计算
);
- 超出返回,返回Infinity,或者-Infinity
- NaN和任何数相乘都是NaN
- Infinity * 0 ,返回NaN
- Infinity 与Infinity相乘,还是Infinity
- 如果不是数值,先用Number()转为数值,再计算
2. 除法
与乘法类似,特别的
console.log(
(Number.MAX_VALUE * 2) / (Number.MAX_VALUE * 2), // NaN, Infinity 除以 Infinity 为 NaN
0 / 0 // NaN, 0被0除,是NaN
);
Infinity除Infinity,0除0,都是NaN
关系操作符
关系操作符:小于(<)、大于(>);小于等于(<=)、大于等于(>=)
比较规则
- 两个字符串做比较,比较字符串编码值
- 其余的,都是先Number()转为数值,再做比较
var obj = {
valueOf: () => 12,
};
var obj2 = {
toString: () => "9",
};
console.log(
"test" < "zero", // true 两个值都是字符串,比较字符编码, t < z
true < 2, // true true转为1
false < -1, // false false转为0
10 < 12, // true ,数字直接比较大小
011 > 8, // true,八进制数字
0xf < 16, // true, 十六进制15
null < 1, // true, null转为0
undefined < 1, // false, undefined是NaN
"8" < 7, // false 字符串转为数字8
"011" < 7, // false, 八进制表示9
"0xf" < 16, // true, 十六进制15
"" > 1, // false, 空字符串转为0
obj < 13, // true obj.valueOf() = 12
obj < 11, // false, obj.valueOf() = 12
obj2 < 10, // true, valueOf()返回一个函数字符串,转为数值型为NaN;调用toString(),返回"9"
obj2 < 8 // false
);
相等操作符
相等符:==
先强制转型,再比较相等性
如果类型相同,直接比较。如果都是对象,判断是两个变量是否引用同一个对象。
类型不同时,强转换规则如下:
- 布尔值:有一个是布尔值,true转为1,false转为0
- null和undefined:null和undefined相等,不会转换
- 字符串:有一个是字符串,一个是数值,字符串转为数值
- 对象:其中有一个是对象,调用valueOf(),再按照之前的规则转换
var obj = {
valueOf: () => 12,
};
var obj2 = {
toString: () => "test",
};
console.log(
false == 0, // true, 布尔值false转为0
true == 1, // true, 布尔值true转为1
null == undefined, // true, null == undefined
null == 0, // false, 注意:null没有转为0
null == false, // false, 注意:再次证明null没有转为0
undefined == false, // false, 注意:undefined也没有转
"8" == 8, // true, 有效的数字字符串直接转,整型和浮点型
"011" == 9, // false
"0xf" == 15, // true 十六进制15
"" == 0, // true, 空字符串转为0
obj == 12, // true, obj.valueOf() = 12
obj2 == "test" // true, 没有valueOf()或者valueOf处理后返回NaN,就调用toString(),返回的值按照之前的规则来
);
强转换就是类似于调用Number(),转为数值。但是null和undefined不会转换,而且相等
关系操作符和相等操作符的对比
| **** | 关系操作符 | 相等操作符 |
|---|---|---|
| 符号格式 | >、<、>=、<= | ==、!= |
| 相同类型 | 直接比较如果都是字符串,比较字符串编码值 | 直接比较如果都是对象,判断是否引用同一个对象 |
| 不同类型 | Number() | Number(),但null和undefined不转换,它们相等 |
类型不同的情况,强转换。强转换的规则为:
- 数值型:简单输入和输出
- 布尔型:false转为0,true转为1
- null和undefined 不转换,但是它们相等
- 字符串:
-
- 有效数值字符,转为数值。包括十六进制
- 空字符串,转为0
- 其余转为NaN
- 对象,先valueOf(),如果得到NaN,调用toString()
语句
for-in语句
for-in可以用来枚举对象的属性
var obj = {
name: "Alice",
age: 19,
};
for (const propName in obj) {
console.log(propName); // name age
}
label语句
用法为 label : statement
一般与for循环配合使用
break 和 continue 语句
break : 退出循环,结束循环
continue : 退出当前循环,继续下一轮循环
break 在循环中,结束循环
return 在函数中,终止函数并返回一个值,默认为undefined
函数
理解参数
参数在内部是一个数组,用arguments对象访问参数数组
arguments.length来获取参数个数
没有传递值的命名参数将自动被赋予undefined值
function fn(name,age) {
console.log(arguments[0], arguments[1]); // Alice undefined
}
fn('Alice')
上面函数fn中,只传递了第一个参数,但是形式参数有两个。获取第二个参数可以看到默认为undefined
没有重载
重载在一些强语言中表示名字相同,但是参数个数和类型不同的函数。但是在JavaScript中,函数没有签名(接受的参数和数量),而javaScript的参数是由包含0或多个数值的数组表示的。
没有函数签名,就没有重载
函数签名:函数参数的个数和类型。