第3章 基本概念

46 阅读16分钟

语法

标识符

ECMAScript中的一切都区分大小写,如变量名、函数名、操作符。

标识符,就是被操作符定义过的字符串,比如var a = 1, a被操作符var定义, a就是标识符。

标识符包括:变量名、函数名、属性名、函数的参数名。

标识符第一个字符:字母、下划线、$

标识符其余字符:字母、下划线、$、数字, 只在第一个字符基础上多了一个数字类型

标识符名字,建议采用小驼峰形式,变量名,函数名,属性名,参数名都采用小驼峰命名方式。

注释

JavaScript的单行注释是双斜杠 //

JavaScript的多行注释是单斜杠+星号开始,星号+单斜杠结束。中间行的星号可选,目的是为了增加可读性。

/*
 * 块级注释
 */

严格模式

严格模式为JavaScript定义了不同的解析和执行模型。主要体现在

  1. ECMAScript3中的不确定行为得到处理
  2. 不安全的操作会抛出错误

严格模式需在代码顶部或者函数顶部写上“use strict”

语句

应在每个语句后面加上分号,有以下3个好处

  1. 避免输入不完整造成错误;
  2. 代码结尾处没有分号会导致压缩错误;
  3. 解析器不用花时间推测应该在哪儿插入分号,可增进代码性能。
var sum = a + b    // 即使没有分号也是有效的语句 ———不推荐
var diff = a - b;  // 有效的语句 ————推荐

始终在控制语句中使用代码块——即使代码块中只有一条语句

  1. 可以让编码意图更加清晰
  2. 降低修改代码时出错的几率
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的值
Booleantruefalse
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()

可用于任何数据类型,只有一个参数

转换规则

  1. 数字值:简单的传入和返回
  2. Boolean类型:true转为1,false转为0
  3. null和undefined: null,转为0;undefined,转为NaN
  4. 字符串(前导0被忽略)
    1. 只包含数值,包括正负号和浮点格式:转为十进制。"123"转为123,“011”转为11,“1.2”转为1.2
    2. 十六进制格式,转为十进制。“0xf”转为15
    3. 空字符串,转为0。 "" 转为0
    4. 其余,转为NaN。"test转为NaN,“12xx”转为NaN
  1. 对象
    1. 调用对象的valueOf(),再按照1~4点规则转换valueOf()返回的值
    2. 如果转换结果是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()
  1. 专门用于字符串
  2. 有两个参数,第一个是要转换的值;第二个是按照什么进制来转换,建议加上
  3. 转换规则
    1. 找到第一个非空字符,第一个非空字符不是数字或负号/正号,就返回NaN
    2. 空字符串,返回NaN
    3. 遇到数值字符,依次解析,直到遇到非数值字符停止
    4. 八进制,转为十进制
    5. 十六进制,转为十进制
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()
  1. 专门用于字符串
  2. 只有一个参数
  3. 转换规则,与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
  1. 转换类型:Number可以转换成整型和浮点型,parseInt只能得到整型。如果要求包括对浮点型的转换,选择Number
  2. 处理非数字字符: 包含非数字字符,Number返回NaN,parseInt会从第一个非空且为数值类型(包括+/-)的字符串提取,遇到非数值字符串停止;如果最开始就是非数字字符串,直接返回NaN
  3. 空字符串处理:Number对空字符串返回0,parseInt对空字符串返回NaN
  4. 基数转换: 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的每个实例都有以下属性:

  1. constructor:保存着用于创建对象的函数,即构造函数
  2. hasOwnProperty(propertyName): 检测属性在当前的对象实例中是否存在不管实例的原型中是否存在
  3. isPrototypeOf(object): 检测传入的对象是否是当前对象的原型
  4. propertyIsEnumerable(propertyName): 属性是否可以用for-in枚举
  5. toLocalString(): 返回对象的字符串表示,与地区对应
  6. toString():返回对象的字符串表示
  7. 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, 先转为数值,再计算
);
  1. 超出返回,返回Infinity,或者-Infinity
  2. NaN和任何数相乘都是NaN
  3. Infinity * 0 ,返回NaN
  4. Infinity 与Infinity相乘,还是Infinity
  5. 如果不是数值,先用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不转换,它们相等

类型不同的情况,强转换。强转换的规则为:

  1. 数值型:简单输入和输出
  2. 布尔型:false转为0,true转为1
  3. null和undefined 不转换,但是它们相等
  4. 字符串:
    1. 有效数值字符,转为数值。包括十六进制
    2. 空字符串,转为0
    3. 其余转为NaN
  1. 对象,先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或多个数值的数组表示的。

没有函数签名,就没有重载

函数签名:函数参数的个数和类型。