JS语言基础
本文最后更新于:2020年11月7日 凌晨
语言基础
区分大小写
ECMAScript中一切区分大小写,无论函数,变量,操作符(虽然 typeof 这种关键字不能作为变量名,但是Typeof可以);
标识符
变量,函数名,属性,函数参数的名称,要求如下:
- 第一个字符必须是字母,$符号,或者下划线_开头;
- 剩下的字符可以是字母,下划线,美元符号,或者是数字;
- 字母可以是Unicode字母或者ASCII字母,但是不推荐使用;
- 关键字,保留字,true, false, null 不能做标识符;
- 一般使用驼峰命名,因为很多JS内置函数都是用驼峰,但是不是强制
注释
1 |
|
严格模式
严格模式会解决ES3或者之前不规范的地方,对于不安全的错误会抛出;
使用方法:
在脚本文件的开头:
"use strict";
或者在函数内部单独使用:
1
2
3
4function dpSomeThing() {
"use strict";
console.log("Hello World!");
}
语句
ECMAScript中语句都是用分号结尾的;由解析器确定语句在哪结尾;
即使分号不是必须的,但是加上分号是有规范意义的;
包括像条件语句中,可以加上{ }
来明确代码块;
变量
ECMAScript中变量是松散类型,即变量可以存储任何类型的数据;
每一个变量只是用于保存任意值的占位符;
存在三个值声明变量:
- var
- const
- let
注意: var 在任何ES版本中都可以使用,但是 const 和 let 只能在ES6以及更晚的版本中使用;
var关键字
var在全局作用域声明会成为window的属性中
1 |
|
语法:
1
2
3
4
5
6// var关键字 + 变量名;
var message;
// 声明并赋值
// 这里不会说明该变量为字符串类型,只是简单的赋值,后续可以更改值甚至值类型;
var name = "hi";当我们不对变量赋值的时候,变量的初始值为
undefiened
;🌟🌟作用域:
当在函数内部使用 var,变量就是局部变量;
函数内部的var定义的变量,在函数推出的时候就被销毁:
1
2
3
4
5function test() {
var a = 10;
}
console.log(a); // undefined当我们在函数内部取消 var 关键字,就会使的变量成为全局变量,在函数外部也会访问到;首先必须调用包含这个变量的函数!
1
2
3
4
5
6function test_2() {
a = 10;
}
// 必须调用这个函数才可以使a成为全局变量;
test_2();
console.log(a); // 10👀:但是不推荐这么做,在局部定义域中定义全局变量不确定是声明还是赋值,很难维护;
声明多个变量:
1
2// 声明多个变量使用逗号隔开
var name = "xxx", age = 12, gen = "男";🌟🌟🌟 var的声明提升 (hoist )
首先请看下面的例子:
1
2
3
4
5function test() {
console.log(age);
var age = 10;
}
test(); // undefined使用 var 关键字,会将该变量自动提升到函数作用域顶部;
这里并不会像上面一样报错,是因为等价于:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31function test_2() {
var age;
console.log(age);
age = 10;
}
test_2();
/*
等价于
先:
var age;
age = 20;
console.log(age);
*/
function test_3() {
age = 20;
var age;
console.log(age);
}
test_3(); // 20
// 反复声明多个var 变量也不会报错,以最后那个最准:
function cmpVarable() {
var age = 10;
var age = 12;
var age = 26;
console.log(age);
}
cmpVarable(); // 26说明,在JS引擎中一般分为两步:
初始化阶段 ( Creation Stage)
- 创建
var
变量,function
函数和函数的arguments
参数 - 如果作用域是函数内部,把函数参数放进前面的
context json
中; - 扫描当前作用域寻找函数(优先寻找函数):
- 每发现一个函数,就把名字和函数指针放进前面的json中;
- 如果函数名已经存在,覆盖之前的函数指针;
- 扫描当前作用域寻找变量:
- 每发现一个变量
var
,就把名字放进前面的json
中,并把值设>成undefined
- 如果变量名已经存在,不会覆盖,忽略然后继续扫描
- 每发现一个变量
- 创建
代码执行阶段 (Activation/Code Execution Stage)
- 给变量和函数赋值,以及执行代码
- 逐行执行代码,并且赋值之前为
undefined
的变量var
let关键字
let关键字和var关键字最大的区别是,let声明范围是块级作用域;var声明的则是函数作用域;
语法:
1
let message;
由于js引擎会记录变量声明的标识符以及所在的作用域,因此嵌套的使用相同let标识符不会报错;
作用域:
块级作用域;
块级作用域是函数作用域的子集;
暂时性死区(temporal dead zone)
在解析代码,js引擎也会注意到let声明,但是在之前不能以任何形式引用未声明的变量;
全局声明
var 在全局声明中会被当成window的属性,但是let不会;
条件声明
1
2
3
4
5
6
7
8
9
10
11
12// let的作用域是块级的,所以不能使用typeof或者try...catch
if(typeof name === 'undefined') {
let name;
}
name = "xxx"; // 相当于全局声明
try {
console.log(age)
}catch(error) {
let age;
}
age = 10; // 相当于全局声明为什么在for循环迭代变量使用let
因为在for循环中使用var会溢出到循环外部;但是let不会;
const关键字
const关键字和let关键字基本使用一样,但是声明const必须初始化变量,并且后续不能修改;
const声明作用域也是块级;不允许重复声明;
const声明的限制只是它指向的变量的引用,const引用一个对象,修改对象的内部属性是不违反限制的;
语法:
1
const message = "xxx";
数据类型
检验数据类型: typeof 操作符
对一个值使用 typeof 会返回一个类型字符串
- “undefined”表示未定义
- “boolean”表示布偶值
- “string”表示字符串
- “number”表述数值
- “object”表示为对象 或者 null (这是因为null会被当成一个空对象的引用)
- “function”表示为函数
- “symbol”表示为符号
1
2
3
4let message = "some thing"
console.log(typeof message); // "string"
// 虽然typeof 不需要参数,但是可以使用参数比如:
console.log(typeof(message));
Undefined 类型
只有一个值就是 undefined;
undefined 是一个假值(在布尔判断时候为假);
当使用 var 或者 let 声明了变量但是没有初始化,变量的值就是 undefined; undefined可以赋值给变量;
1
2let message = undefined;
console.log(message == undefined); // true对于未定义的变量 唯一可以执行的操作就是进行 typeof 操作:
1
2
3
4
5
6
7
8
9let message;
// 未定义变量age
// 对于没有声明的变量返回是 undefined
console.log(typeof message); // "undefined"
// 对于没有定义的变量返回 undefined
// 但是不可以直接判断 age == undefined
console.log(typeof age); // "undefined"
Null 类型
只有一个值是 null;
null 是一个假值(在布尔判断时候为假);
null表示一个空对象的指针,所有使用 typeof 返回的是 “object”;
undefined 是 null 派生来的,所有使用等于操作符返回 true, 两者只是表面相等(==);
1
console.log(undefined == null); // true
Boolean 类型
存在两个字面值: true 和 false;
布尔值转化:
Boolean();
数据类型 转换成true的值 转换成false的值 Boolean true False Number 非0 0,NaN String 非空字符串 “”(空字符串) Object 任意对象(包括 { } ) null undefined / undefined
Number类型
整数可以是八进制也可以是十六进制:
1
2
3
4// 八进制必须以 0o 开头
let num_1 = 0o70;
// 十六进制必须以 0x 开头
let num_2 = 0x1f;浮点数
浮点数的存储空间是整数的两倍(所以ES中都是设法转换成整数保存)
整数部分为0,可以省略整数部分
0.1 => .1
科学计数法用一个整数或者浮点数 + 大写或者小写的e,再加上10的n次幂;
3.12E8
浮点数的值最高高达17位小数,但是并不够,例如:
1
2// 这里并不是JS的问题,而且使用了IEEE754数值就会出现该问题
console.log(.1 + .2) // 0.30000000000000004
ES可以表示的最小数保存在:
Number.MIN_VALUUE
;可能包含-InfintyES可以表示的最大数保存在:
Number.MAX_VALUUE
;可能包含Infinty超过保存的最大数或者最小数会被转换成
Infinity 或者 -Infinity
;并且不能进行任何操作;检测一个数是不是无限大或者无限小的数:
isFinite()
;返回 true 表示该数有限;NaN(Not a Number)
- 涉及到NaN的操作始终返回NaN
- NaN不等于任何值,包括NaN
- 通过
isNaN()判断一个数是不是NaN
;将值传给 isNaN(),该函数会尝试转换成数值;
数值转换:
Number(), parseInt(), parseFloat()
Number():是转型函数,可以用于任何数据类型:
数据类型 转换后 布尔值 true转换成1, false转换成0 数值 直接返回 null 0 undefined NaN 字符串 1.字符串包含数值字符,直接转换,包括加减号,忽略掉数值字符前面无效的0;
2.字符中包含有效浮点数,直接转换相应的浮点数值,同样忽略掉前面无效的0;
3.字符串包含有效的十六进制的数比如: “0xf”, 转换成十进制:
4.空字符串转换成0;
5.其他字符串转换成NaN;对象 想调用对象的 valueOf()方法,如果返回值为NaN,调用 toString()方法,按照字符串转换方式; parseInt():主要用于字符串的转换;
parseInt(number, type)
;第一个参数是转换的数值,第二个可选参数是需要转换的进制字符串最前面的空格会被省略;
转换浮点数字符串只会保留整数部分
如果第一个字符不是数值字符,加号,减号,直接返回NaN;
1
2let str = "1.23blue"
parseInt(str); // 1.23 后续的字符串会被省略遇见空字符串直接返回NaN,与Number()不一样, 包括undefined和null也会转换成 NaN;
当不传入第二个参数的时候,需要在八进制和十六进制前面加上 0o 或者 0x;传入了可以省略;
1
2
3let num = parseInt("AX") // NaN
let num2 = parseInt("AX", 16) // 175
let num3 = parseInt("0xAX") // 175
parseFloat(): 也是主要用于字符串的转换;
- 主要转换方式同parseInt();
- 始终忽略掉字符串开头的0,所以解析十六进制数据只会返回0;
- 只解析十进制数据;
String类型
字符串可以使用单引号(’’),双引号(””), 反引号(`);
可以表示零个或者多个Unicode字符;
以某种引号开头,必须以某种引号结尾;
字符串使用
.length
获取字符串长度;特点:
- 字符串不可变,一旦创建值不可变,要修改某个字符串变量,就是先销毁原来的字符串,再重新把新值赋值;
toString():
toString();
基本所有值可以用,但是null,undefined不可用;- 使用
toString()
转换数值时候,可以传入参数,数值表示转换字符串的进制数;默认是十进制;
String():
String();
当我们不确定转换的值是不是null,undefined时候,可以使用该转换;String()
遵循一些规定:- 值存在
toString();
则使用该方法,不可以穿入参数; - null转换成”null”;
- undefined转换成”undefined”;
- 值存在
模板字面量(``):
严格意义上讲模板字面量不是吧字符串,只是运行后得到字符串的JS语法表达式;
模板字面量会保留换行,即我们可以使用模板字符串定义的模板:
1
2
3
4
5
6
7
8
9
10
11let temp = `
Hello
World
`
/*
Hello
World
*/
console.log(temp)
// true
console.log(temp[0] === '\n')
字符串插值:
使用
${}
来使用插值;被插入的值强制使用
toString()
;1
2
3
4
5
6
7let value =5;
let message = 'send';
// 方式1
let str_value = message + (value * value);
// 方式2
let str_value_2 = `${message} ${value * value}`
模板字面量标签函数(tag function)
对于红宝书上的定义我自己解释如下
标签函数也是正常函数,定义函数时候正常书写,使用时候写法不一样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var a = 1;
var b = 2;
function simpleTag(string, value_1, value_2, value_3) {
console.log(string) // ["get ", " + ", " = ", "", raw: Array(4)]
console.log(value_1) // 1
console.log(value_2) // 2
console.log(value_3) // 3
}
// 简写成
function simpleTag(string, ...value) {
console.log(string) // ["get ", " + ", " = ", "", raw: Array(4)]
console.log(value) // [1, 2, 3]
}
let res = simpleTag`get ${a} + ${b} = ${a + b}`如上所示,标签函数,第一个参数,输出的是除去插值的模板,后续第二个参数开始是插值的数据,可以简写成数组;
输出模版的时候,是以遇见插值前的字符串为一组,包括空格;
原始字符串
即我们在使用字符串会遇见转译字符,我们可以使用
String.row
,保留原本的转义字符;1
2
3
4
5
6console.log(`\u00A9`); // ©
console.log(String.raw`\uooA9`); // \u00A9
// 换行符
// 但是只能控制显式不能转换实际回车的换行符
console.log(Stirng.raw`adc \n edf`) // adc \n edf
Symbol类型
新增一个符号为原始值,并且符号是不可变的,唯一的;
符号的用途是确保对象属性使用了唯一的标识符,确保属性冲突的危险;
符号的基本用法:
1
2
3
4
5
6
7
8
9
10
11let a = Symbol();
let b = Symbol();
console.log(a); // Symbol()
console.log(typeof a); // symbol
console.log(a === b); // false
console.log(a == b); // false
// 我们可以在Symbol中传入一个对符号的描述
let c = Symbol("Hello");
// c.description访问到描述
console.log(c.description); // Hello使用全局符号注册表:
由于之前每次新建Symbol;都是不一样的,当我们需要共享或者重用符号实例的时候,需要在全局符号注册表中实现:
1
2let a = Symbol.for("Hello");
console.log(typeof a); // symbolSymbol.for()
,当我们使用某一个字符调用的时候,它会检查全局运行的注册表,发现不存的会新建一个到全局注册表中;如果发现相同的,那么就会返回该符号实例;
1
2
3
4
5
6
7let a = Symbol.for("Hello");
let b = Symbol.for("Hello");
console.log(a === b); // true
let a = Symbol("Hello");
let b = Symbol.for("Hello");
console.log(a === b); // false查询全局注册表:
1
2let a = Symbol.for("Hello");
console.log(Symbol.keyFor(a); // HelloSymbol.keyFor();接受一个符号参数,返回符号的描述内容
;如果不是符号会抛出异常:
TypeError
;该查询描述内容只能使用于
Symbol.for();
创建的Symbol实例;如果是普通的Symbol("name")
会报错为undefined;
未完待续….
Object类型
新建一个对象:
1
2
3let a = new Object();
let b = {};属性方法:
- 注意:严格来说ECMA-262中的对象和JS的对象不一定一样,比如DOM和BOM,都是宿主环境定义和提供的对象,所以他们可能不会继承Object(可能不会继承以下属性);
constructor:
用于创建当前对象的函数;hasOwnProperty(propertyName):
判断当前对象实例上是否存在给定的属性,属性必须是字符串;isPrototyprof():
判断当前对象是否是另一个对象的原型;propertyIsEnumerable(propertyName):
判断给定的属性是否可以使用for-in枚举;toLocaleString();
返回对象的字符串表示;toString();
返回对象的字符串表示;valueOf();
返回对象的对应的字符串,数值或者是字符串;
操作符
一元操作符
递增/递减操作符:
1 |
|
该操作符可以作用任何值,包括字符串,布尔值,数值,浮点数,对象;
字符串:有效数值变成数值应用(加1或者减1),无效数值变成NaN;
布尔值:true为1再应用(加1或者减1), false为0再应用(加1或者减1);
浮点数:加1或者减1;
对象:调用valueOf(),如果结果是NaN,则调用toString(),再次变化;
一元加和减:
将 一元加 应用到非数值,则会执行与使用 Number()转型函数一样的类型转换:
布尔值 false和 true 转换为 0 和 1;
字符串根据特殊规则进行解析;
对象会调用它们的 valueOf()或 toString()方法以得到可以转换的值。
对数值使用 一元减 会将其变成相应的负值(如上面的例子所示)。在应用到非数值时,一元减会遵
循与一元加同样的规则,先对它们进行转换,然后再取负值;
位操作符:
ECMAScript中的所有数值都以 IEEE 754 64 位格式存储;
位操作并不直接应用到 64 位表示,而是先把值转换为32 位整数,再进行位操作,之后再把结果转换为 64 位;
- 有符号整数使用 32 位的前 31 位表示整数值。第 32 位表示数值的符号,如 0 表示正,1 表示负。这
一位称为符号位(sign bit),它的值决定了数值其余部分的格式。
- 一个数的负数(补码, 二补数)是以二进制编码存储;
- 确定绝对值的二进制表示(如,对于-18,先确定 18 的二进制表示);
- 找到数值的一补数(或反码),换句话说,就是每个 0 都变成 1,每个 1 都变成 0;
- 给结果加 1。
按位非:
按位非 操作符用波浪符(~)表示,它的作用是返回数值的一补数(每位取反)。
1
2
3
4// 在位上进行操作数度较快
let num1 = 25; // 二进制 00000000000000000000000000011001
let num2 = ~num1; // 二进制 11111111111111111111111111100110
console.log(num2); // -26
按位与:
按位与 操作符用和号(&)表示,有两个操作数。本质上,按位与就是将两个数的每一个位按照0对齐;
可以适当将 1 看作 true;
第一位 第二位 结果 1 1 1 1 0 0 0 1 0 0 0 0 1
2let a = 25 & 3;
console.log(a); // 1
按位或:
- 按位或操作符用管道符(|)表示,同样有两个操作数。按位或遵循如下真值表:
- 可以适当将 1 看作 true;
第一位 第二位 结果 1 1 1 1 0 1 0 1 1 0 0 0
1 |
|
按位异或:
- 按位异或用脱字符(^)表示,同样有两个操作数。下面是按位异或的真值表:
- 按位异或与按位或的区别是,它只在一位上是 1 的时候返回 1(两位都是 1 或 0,则返回 0)
第一位 第二位 结果 1 1 0 1 0 1 0 1 1 0 0 0
1 |
|
左移:
左移操作符用两个小于号(<<)表示,会按照指定的位数将数值的所有位向左移动,左移会以 0 填充这些空位;
左移会保留它所操作数值的符号;
1
2let oldValue = 2; // 10
let res = oldValue << 5; // 1000000,十进制的64
有符号右移:
有符号右移由 2 个大于号(>>)表示,会将数值的所有 32 位都向右移,同时保留符号(正或负)。
同样,移位后就会出现空位。不过,右移后空位会出现在左侧,且在符号位之后;
ECMAScript 会用符号位的值来填充这些空位,以得到完整的数值。
1
2let oldValue = 64; // 等于二进制 1000000
let newValue = oldValue >> 5; // 等于二进制 10,即十进制 2
无符号右移:
- 无符号右移用 3 个大于号表示(>>>),会将数值的所有 32 位都向右移。
- 对于正数,无符号右移与有符号右移结果相同。
- 对于负数,与有符号右移不同,无符号右移会给空位补 0,而不管符号位是什么。有符号右移空位的数值是根据符号的数值补充的;
布尔操作符:
逻辑非:
逻辑非操作符由一个叹号(!)表示,可应用给 ECMAScript 中的任何值。
类型 使用逻辑非 数值 0返回true,非0返回false 对象 返回false 字符串 空字符串返回true, 其他非空返回false Null true Undefined true NaN true 同时使用
!!
表示了调用了转型函数Boolean()
;
1 |
|
逻辑与:
逻辑与操作符由两个和号(&&)表示
第一位 第二位 结果 true true true true false false false true false false false false 逻辑与操作符是一种短路操作符,意思就是如果第一个操作数决定了结果,那么永远不会对第二个操作数求值;即使第二个参数未声明定义;
⚠️逻辑与可以适用于任何类型的数据:
- 如果第一个操作数是对象,则返回第二个操作数。
- 如果第二个操作数是对象,则只有第一个操作数求值为 true 才会返回该对象。
- 如果两个操作数都是对象,则返回第二个操作数。
- 如果有一个操作数是 null,则返回 null;
- 如果有一个操作数是 NaN,则返回 NaN;
- 如果有一个操作数是 undefined,则返回 undefined;
逻辑或:
逻辑或操作符由两个管道符(||)表示
第一位 第二位 结果 true true true true false true false true true false false false 同样与逻辑与类似,逻辑或操作符也具有短路的特性。只不过对逻辑或而言,第一个操数求值为true,第二个操作数就不会再被求值了。
⚠️逻辑或可以适用于任何类型的数据:
- 如果第一个操作数是对象,则返回第一个操作数。
- 如果第一个操作数求值为 false,则返回第二个操作数。
- 如果两个操作数都是对象,则返回第一个操作数。
- 如果两个操作数都是 null,则返回 null;
- 如果两个操作数都是 NaN,则返回 NaN;
- 如果两个操作数都是 undefined,则返回 undefined;
乘性操作符:
乘法操作符
乘法操作符由一个星号(*)表示,可以用于计算两个数值的乘积;
1
let a = 21 * 34;
如果操作数都是数值,则执行常规的乘法运算;如果 ECMAScript 不能表示乘积,则返回 Infinity 或-Infinity。
如果有任一操作数是 NaN,则返回 NaN;
如果是 Infinity 乘以 0,则返回 NaN。
如果是 Infinity 乘以非 0的有限数值,则根据第二个操作数的符号返回 Infinity 或-Infinity。
如果有不是数值的操作数,则先在后台用 Number()将其转换为数值,然后再应用上述规则。
除法操作符:
除法操作符由一个斜杠(/)表示,用于计算第一个操作数除以第二个操作数的商:
1
let res = 66 / 11;
如果操作数都是数值,则执行常规的除法运算,即两个正值相除是正值,两个负值相除也是正值,符号不同的值相除得到负值。如果ECMAScript不能表示商,则返回Infinity或-Infinity。
如果有任一操作数是 NaN,则返回 NaN。
如果是 Infinity 除以 Infinity,则返回 NaN。
如果是 0 除以 0,则返回 NaN。
如果是非 0 的有限值除以 0,则根据第一个操作数的符号返回 Infinity 或-Infinity。
如果是 Infinity 除以任何数值,则根据第二个操作数的符号返回 Infinity 或-Infinity。
如果有不是数值的操作数,则先在后台用 Number()函数将其转换为数值,然后再应用上述规则;
取模操作符:
取模(余数)操作符由一个百分比符号(%)表示;
1
let res = 26 % 5;
如果操作数是数值,则执行常规除法运算,返回余数。
如果被除数是无限值,除数是有限值,则返回 NaN。
如果被除数是有限值,除数是无限值,则返回被除数。
如果被除数是有限值,除数是 0,则返回 NaN。
如果是 Infinity 除以 Infinity,则返回 NaN。
如果被除数是 0,除数不是 0,则返回 0。
如果有不是数值的操作数,则先在后台用 Number()函数将其转换为数值,然后再应用上述规则。
指数操作符:
ECMAScript 7 新增了指数操作符,Math.pow()现在有了自己的操作符**
;
1 |
|
加性操作符:
加法操作符:
加法操作符(+)用于求两个数的和:
1 |
|
如果有任一操作数是 NaN,则返回 NaN;
如果是 Infinity 加 Infinity,则返回 Infinity;
如果是-Infinity 加-Infinity,则返回-Infinity;
如果是 Infinity 加-Infinity,则返回 NaN;
如果是+0 加+0,则返回+0;
如果是-0 加+0,则返回+0;
如果是-0 加-0,则返回-0。
如果有一个操作数是字符串,则要应用如下规则:
- 如果两个操作数都是字符串,则将第二个字符串拼接到第一个字符串后面;
- 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,再将两个字符串拼接在一起。
- 如果有任一操作数是对象、数值或布尔值,则调用它们的 toString()方法以获取字符串;
- 对于 undefined 和 null,则调用 String()函数;分别获取”undefined”和”null”
减法操作符:
减法操作符(-)也是使用很频繁的一种操作符:
1 |
|
- 如果两个操作数都是数值,则执行数学减法运算并返回结果。
- 如果有任一操作数是 NaN,则返回 NaN。
- 如果是 Infinity 减 Infinity,则返回 NaN。
- 如果是-Infinity 减-Infinity,则返回 NaN。
- 如果是 Infinity 减-Infinity,则返回 Infinity。
- 如果是-Infinity 减 Infinity,则返回-Infinity。
- 如果是+0 减+0,则返回+0。
- 如果是+0 减-0,则返回-0。
- 如果是-0 减-0,则返回+0;
- 如果有任一操作数是字符串、布尔值、null 或 undefined,则先在后台使用 Number()将其转换为数值,然后再根据前面的规则执行数学运算。如果转换结果是 NaN,则减法计算的结果是NaN。
- 如果有任一操作数是对象,则调用其 valueOf()方法取得表示它的数值。如果该值是 NaN,则减法计算的结果是 NaN。如果对象没有 valueOf()方法,则调用其 toString()方法,然后再将得到的字符串转换为数值。
关系操作符:
关系操作符执行比较两个值的操作,包括小于(<)、大于(>)、小于等于(<=)和大于等于(>=);
- 如果操作数都是数值,则执行数值比较
- 如果操作数都是字符串,则逐个比较字符串中对应字符的编码;注意这里并不会转换成数字而是直接比较字符串编码的大小;
- 如果有任一操作数是数值,则将另一个操作数转换为数值,执行数值比较;
- 如果有任一操作数是对象,则调用其 valueOf()方法,取得结果后再根据前面的规则执行比较。如果没有 valueOf()操作符,则调用 toString()方法,取得结果后再根据前面的规则执行比较。
- 如果有任一操作数是布尔值,则将其转换为数值再执行比较;
- 任何值与NaN比较都会返回false;
"a" < 3
, 该操作也会返回false, 因为 “a”会转换成数值时候返回NaN;
相等操作符:
等于和不等于:
ECMAScript 中的等于操作符用两个等于号(==)表示,如果操作数相等,则会返回 true。
不等于操作符用叹号和等于号(!=)表示,如果两个操作数不相等,则会返回 true。
该操作符是转换成数值;
- 如果任一操作数是布尔值,则将其转换为数值再比较是否相等。false 转换为 0,true 转换为 1。
- 如果一个操作数是字符串,另一个操作数是数值,则尝试将字符串转换为数值,再比较是否相等。
- 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法取得其原始值,再根据前面的规则进行比较。
- null 和 undefined 相等。
- null 和 undefined 不能转换为其他类型的值再进行比较;
- 如果有任一操作数是 NaN,则相等操作符返回 false,不相等操作符返回 true。记住:即使两个操作数都是 NaN,相等操作符也返回 false,因为按照规则,NaN 不等于 NaN。
- 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回 true。否则,两者不相等。
全等和全不等
全等和不全等操作符与相等和不相等操作符类似,只不过它们在比较相等时不转换操作数。
全等操作符由 3 个等于号(===)表示,只有两个操作数在不转换的前提下相等才返回 true;即需也要判断两者数据类型是否相等;
不全等操作符用一个叹号和两个等于号(!==)表示,只有两个操作数在不转换的前提下不相等才返回 true。
null === undefined 返回false;
条件操作符:
1 |
|
赋值操作符:
简单赋值用等于号(=)表示,将右手边的值赋给左手边的变量;
复合赋值使用乘性、加性或位操作符后跟等于号(=)表示。
以上基本操作符都可以复合操作;
1 |
|
逗号操作符:
逗号操作符可以用来在一条语句中执行多个操作;
1 |
|
语句
if语句
1 |
|
1 |
|
do-while语句
do-while 语句是一种后测试循环语句,即循环体中的代码执行后才会对退出条件进行求值;
换句话说,循环体内的代码至少执行一次。
1 |
|
while语句
while 语句是一种先测试循环语句,即先检测退出条件,再执行循环体内的代码。
因此,while 循环体内的代码有可能不会执行。
1 |
|
for语句
for 语句也是先测试语句,只不过增加了进入循环之前的初始化代码,以及循环执行后要执行的表达式;
1 |
|
根据之前定义的变量关键字,最好在for循环中使用let关键字定义变量,这样就可以将这个变量的作用域限定在循环中。
初始化、条件表达式和循环后表达式都不是必需的;
1 |
|
for-in语句
for-in 语句是一种严格的迭代语句,用于枚举对象中的非符号键属性;
ECMAScript 中对象的属性是无序的,因此 for-in 语句不能保证返回对象属性的顺序;
如果 for-in 循环要迭代的变量是 null 或 undefined,则不执行循环体;
1 |
|
for-of 语句
for-of 语句是一种严格的迭代语句,用于遍历可迭代对象的元素 ;
如果尝试迭代的变量不支持迭代,则 for-of 语句会抛出错误。
1 |
|
标签语句
标签语句用于给语句加标签,语法如下:
标签语句的典型应用场景是嵌套循环。
1 |
|
break和continue语句
break 语句用于立即退出循环,强制执行循环后的下一条语句;
而 continue 语句也用于立即退出循环,但会再次从循环顶部开始执行。
1 |
|
break 和 continue 都可以与标签语句一起使用,返回代码中特定的位置;
1 |
|
1 |
|
with语句
使用 with 语句的主要场景是针对一个对象反复操作;
严格模式不允许使用 with 语句,否则会抛出错误;
1 |
|
1 |
|
switch语句
这里的每个 case(条件/分支)相当于:“如果表达式等于后面的值,则执行下面的语句。”
break关键字会导致代码执行跳出 switch 语句。
如果没有 break,则代码会继续匹配下一个条件;
default关键字用于在任何条件都没有满足时指定默认执行的语句(相当于 else 语句)。
switch 语句可以用于所有数据类型(在很多语言中,它只能用于数值),因此可以使用字符串甚至对象。
其次,条件的值不需要是常量,也可以是变量或表达式。看下面的例子:
- ⚠️:switch 语句在比较每个条件的值时会使用全等操作符,因此不会强制转换数据类型(”10” !== 10)
1 |
|