3.1 语法

1.区分大小写
2.标识符:变量、函数、属性或函数参数的名称。 驼峰大小写:firstSecond
 第一个字符必须是一个字母、下划线(_)或美元符号($);
 剩下的其他字符可以是字母、下划线、美元符号或数字。
3.注释: // /**/
4.严格模式:
function doSomething() {
“use strict”; //预处理指令
// 函数体
5.语句:分号、代码块{}

3.2 关键字和保留字

关键字:不能用作标识符或属性名

break do in typeof
case else instanceof var
catch export new void
class extends return while
const finally super with
continue for switch yield
debugger function this
default if throw
delete import try

保留字:不能用作标识符 还可用作对象属性名

始终保留:
enum
严格模式下保留:
implements package public
interface protected static
let private
模块代码中保留:
await

3.3 变量

变量是任何数据的命名占位符,由var、let、const三个关键字来声明

3.3.1 var 关键字

1.var声明作用域:函数作用域 函数调用后(退出时)被销毁
2.var声明提升:把所有变量声明都拉到函数作用域顶部 undefined
对比函数提升https://blog.csdn.net/kontar123/article/details/83508740
函数提升只会提升函数声明如:function a(){},不会提升函数表达式如:var a = function (){}
函数提升优于变量提升

3.3.2 let 声明

1)块级作用域(if(){}属于块级作用域 不属于函数作用域 块作用域属于函数作用域子集)
2)同一个块中不能出现冗余声明 (无论var、let)报错:SyntaxError
3)暂时性死区:不存在变量提升 报错:ReferenceError
4)全局声明不挂载到window对象:var —成为window对象的属性 let—undefined
5)for循环中(for in、for of):var—渗透到循环体外部,迭代变量保存的是导致循环退出的值
let—仅作用于循环块内部 每次迭代循环声明一个新的迭代变量

3.3.3 const 声明

1)与let相似 块级作用域、暂时性死区、全局声明不挂载到window对象、不能冗余声明
2)const声明必须初始化 let可以只声明
3)const表示常量 所以不能尝试修改const的值 但是const是对象 可以修改对象的属性 因为没有修改对象的引用
4)const不能向let一样在for循环体中声明迭代变量(因为是常量)

3.3.4 声明风格和最佳实践

不使用var const优先,let次之

3.4 数据类型

3.4.1 分类

  1. 简单数据类型:六种

Null、undefined、Boolean、string、number、Symbol(ES6) (BigInt(ES10))
复杂数据类型:一种 Object—对象 :实质上是一种无序的名值对集合

  1. 原始数据类型(基本类型)(值类型):六种

    引用数据类型(派生数据类型):对象(Object)、数组(Array)、函数(Function)
    3.js中对象分类:
    (1)内部对象
    包括 Array、Function、Date、RegExp、Object、 Boolean、String、Number
    错误类对象:Error、EvalError、RangeError、ReferenceError、SyntaxError和TypeError
    两种内置对象:Global、Math 在脚本程序初始化时被创建,不必实例化两个对象
    (2)宿主对象(通常指浏览器对象)
    执行js脚本的环境提供的对象
    对于嵌入到网页中的js来说,其宿主对象就是浏览器提供的对象所以又称为浏览器对象
    浏览器对象有很多,如window、navigator、screen、location、document、history等
    (3)自定义对象:六种方法
    1>创建一个Object实例(new)
    2>对象字面量({})
    以上是创建对象的两种基本方式
    3>工厂模式
    4>构造函数方式
    5>原型模式
    6>组合使用构造函数模式和原型模式(最常用)

3.4.2 typeof操作符

(可扩充instance of、原型链、区分Object、Array的方法等相关知识点)

用途:来确定任意变量的数据类型 (不是函数,不需要参数)(但可以使用参数)。
返回值:七种返回值(五种基本数据类型和Object 、 Function)
注:null返回object null被认为是一个空对象的引用(空对象指针)

3.4.3 Undefined类型

只有一个值 undefined 表示声明了但没有初始化的值 是一个假值

3.4.4 Null类型

只有一个值null 逻辑上表示空对象指针 是一个假值

3.4.5 Boolean类型

两个字面值:true、false
只有五种值会转换成false:null、undefined、+-0、NaN、空串“”
注意任意对象转化为Boolean类型都是true

3.4.6 Number类型

1.浮点值(双精度值) 精确度最高可达17位小数
0.1+0.2 != 0.3问题: 0.1+0.2 == 0.300 000 000 000 000 04
原因:
如何解决:

2.值的范围
Number.MIN_VALUE: 5e-324
Number.MAX_VALUE: 1.798e+308
isFinite()函数来判断Infinity:Infinity(Number.POSITIVE_INFINITY)、-Infinity(Number.NEGATIVE_INFINITY)

3.NaN
表示本来要返回数值的操作失败了(而不是抛出错误)

在ECMAScript 中,0、+0 或0 相除会返回NaN:
console.log(0/0); // NaN
console.log(-0/+0); // NaN

如果分子是非0 值,分母是有符号0 或无符号0,则会返回Infinity 或-Infinity:
console.log(5/0); // Infinity
console.log(5/-0); // -Infinity

NaN 不等于包括NaN 在内的任何值
console.log(NaN == NaN); // false

isNaN()函数。该函数接收一个参数,可以是任意数据类型,然后判断这个参数是否“不是数值”
isNaN()可以用于测试对象。此时,首先会调用对象的valueOf()
方法,然后再确定返回的值是否可以转换为数值。如果不能,再调用toString()方法,
并测试其返回值

4.数值转换
3个函数实现非数值转换成数值:Number()、parseInt()、parseFloat()
Number():任何数据类型->Number
Number(null)->0 Number(undefined)->NaN Number(“”)->0
Number(对象) 调用valueof()-> 转化结果为NaN -> 调用toString()->字符串规则

parseInt(): 主要 字符串->整数 (字符串是否包含数值模式)能识别八进制、十六进制
parseInt(“”)->NaN
接收第二个参数 用于指定底数(进制数)(基数)2~36之间 超出范围返回NaN

parseFloat():字符串->浮点数 十六进制数始终返回0 let num2 = parseFloat(“0xA”); // 0
始终忽略字符串开头的0 let num5 = parseFloat(“0908.5”); // 908.5
可以识别科学计数法let num6 = parseFloat(“3.125e7”); // 31250000

3.4.7 String类型

String(字符串)数据类型表示零或多个16 位Unicode 字符序列。字符串可以使用双引号(”)、
单引号(’)或反引号(`)表示

1.字符字面量:
\xnn 以十六进制编码nn 表示的字符(其中n 是十六进制数字0~F),例如\x41 等于”A”
\unnnn 以十六进制编码nnnn 表示的Unicode 字符(其中n 是十六进制数字0~F),例如\u03a3 等于希腊字
符”Σ”
转义序列表示一个字符,所以只算一个字符
字符串的长度可以通过其length 属性获取:
2.字符串的特点:不可变
3.转化为字符串:.toString(基数)方法、String()转型函数->解决null、undefined没有toString()
4.模板字面量:反引号 ` ->跨行定义字符串<br />5.字符串插值:${} 所有插入的值都会使用toString()强制转型为字符串<br />6.模板字面量标签函数:(不是很理解)<br />7.原始字符串:String.raw 标签函数:console.log(String.raw\u00A9); // \u00A9<br />或者字符串数组的.raw属性:for (const rawString of strings.raw) console.log(rawString);<br />printRaw\u00A9${ ‘and’ }\n;<br />\u00A9${ ‘and’ }\n` —属于模板字符串 模板字符串传参 函数第一个参数是字符串数组
第二个参数以后是—对应${}里面的值

3.4.8 Symbol类型

1.符号的基本用法
(1)创建的实例是基本数据类型:symbol
let sym = Symbol();
console.log(typeof sym); // symbol
(2)symbol实例可以接受一个字符串参数来作为描述 但是一定各不相同 所以可用作对象属性
(3)不可以和new一起作为构造函数使用
let mySymbol = new Symbol(); // TypeError: Symbol is not a constructor
(4)想要一个包含Symbol的对象 Object()
let mySymbol = Symbol();
let myWrappedSymbol = Object(mySymbol);
console.log(typeof myWrappedSymbol); // “object”
2.使用全局符号注册表
(1)Symbol.for() —实现全局创建和重用
let fooGlobalSymbol = Symbol.for(‘foo’); // 创建新符号
let otherFooGlobalSymbol = Symbol.for(‘foo’); // 重用已有符号
console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true
(2)Symbol.keyFor() —返回symbol对应地字符串键
// 创建全局符号
let s = Symbol.for(‘foo’);
console.log(Symbol.keyFor(s)); // foo
// 创建普通符号
let s2 = Symbol(‘bar’);
console.log(Symbol.keyFor(s2)); // undefined
3.使用符号作为属性
(1)创建对象属性
let s1 = Symbol(‘foo’),
s2 = Symbol(‘bar’),
s3 = Symbol(‘baz’),
s4 = Symbol(‘qux’);
let o = {
[s1]: ‘foo val’
};
// 这样也可以:o[s1] = ‘foo val’; —对象字面量
console.log(o);
// {Symbol(foo): foo val}
Object.defineProperty(o, s2, {value: ‘bar val’}); —Object.defineProperty
console.log(o);
// {Symbol(foo): foo val, Symbol(bar): bar val}
Object.defineProperties(o, { —Object.defineProperties
[s3]: {value: ‘baz val’},
[s4]: {value: ‘qux val’}
});
console.log(o);
// {Symbol(foo): foo val, Symbol(bar): bar val,
// Symbol(baz): baz val, Symbol(qux): qux val}
(2)获取对象属性
let s1 = Symbol(‘foo’),
s2 = Symbol(‘bar’);
let o = {
[s1]: ‘foo val’,
[s2]: ‘bar val’,
baz: ‘baz val’,
qux: ‘qux val’
};
console.log(Object.getOwnPropertySymbols(o));返回对象实例的符号属性数组
// [Symbol(foo), Symbol(bar)]
console.log(Object.getOwnPropertyNames(o));返回对象实例的常规属性数组
// [“baz”, “qux”]
console.log(Object.getOwnPropertyDescriptors(o));返回同时包含常规和符号属性描述符的对象
// {baz: {…}, qux: {…}, Symbol(foo): {…}, Symbol(bar): {…}}
console.log(Reflect.ownKeys(o));
// [“baz”, “qux”, Symbol(foo), Symbol(bar)]返回两种类型的键:
(3)没有显式的保存对这些属性的引用,必须遍历对象的所有符号属性才能找到相应的属性键
let o = {
[Symbol(‘foo’)]: ‘foo val’,
[Symbol(‘bar’)]: ‘bar val’
};
console.log(o);
// {Symbol(foo): “foo val”, Symbol(bar): “bar val”}
let barSymbol = Object.getOwnPropertySymbols(o)
.find((symbol) => symbol.toString().match(/bar/));
console.log(barSymbol);
// Symbol(bar)
4.常用内置符号
用于暴露语言内部行为,开发者可以直接访问、重写或模拟这些行为。它们就是全局函数Symbol 的普通字符串属性,指向一个符号的实例。所有内置符号属性都是不可写、不可枚举、不可配置的。
(1)Symbol.asyncIterator
(2)Symbol.hasInstance
function Foo() {}
let f = new Foo();
console.log(FooSymbol.hasInstance); // true
(3)Symbol.isConcatSpreadable
数组对象: 默认直接追加元素 设置为false—整个对象被追加到数组末尾
let initial = [‘foo’];
let array = [‘bar’];
console.log(array[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(array)); // [‘foo’, ‘bar’]
array[Symbol.isConcatSpreadable] = false;
console.log(initial.concat(array)); // [‘foo’, Array(1)]
类数组对象:默认追加整个对象 设置为true—追加元素
let arrayLikeObject = { length: 1, 0: ‘baz’ };
console.log(arrayLikeObject[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(arrayLikeObject)); // [‘foo’, {…}]
arrayLikeObject[Symbol.isConcatSpreadable] = true;
console.log(initial.concat(arrayLikeObject)); // [‘foo’, ‘baz’]
非类数组的对象:默认追加整个对象 设置为true—忽略、不操作
let otherObject = new Set().add(‘qux’);
console.log(otherObject[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(otherObject)); // [‘foo’, Set(1)]
otherObject[Symbol.isConcatSpreadable] = true;
console.log(initial.concat(otherObject)); // [‘foo’]
(4)Symbol.iterator
(5)Symbol.match
(6)Symbol.replace
(7)Symbol.search
(8)Symbol.species
(9)Symbol.split
(10)Symbol.toPrimitive
(11)Symbol.toStringTag
(12)Symbol.unscopables

3.4.9 object类型

ECMAScript 中的Object 也是派生其他对象的基类。Object 类型的所有属性和方法在派生
的对象上同样存在。
每个Object 实例都有如下属性和方法。
 constructor:用于创建当前对象的函数。在前面的例子中,这个属性的值就是Object()
函数。
 hasOwnProperty(propertyName):用于判断当前对象实例(不是原型)上是否存在给定的属
性。要检查的属性名必须是字符串(如o.hasOwnProperty(“name”))或符号。
 isPrototypeOf(object):用于判断当前对象是否为另一个对象的原型。(第8 章将详细介绍
原型。)
 propertyIsEnumerable(propertyName):用于判断给定的属性是否可以使用(本章稍后讨
论的)for-in 语句枚举。与hasOwnProperty()一样,属性名必须是字符串。
 toLocaleString():返回对象的字符串表示,该字符串反映对象所在的本地化执行环境。
 toString():返回对象的字符串表示。
 valueOf():返回对象对应的字符串、数值或布尔值表示。通常与toString()的返回值相同

3.5 操作符

3.5.1 一元操作符

1.递增/递减操作符 ++/—
2.一元加和减
如果将一元加应用到非数值,则会执行与使用Number()转型函数一样的类型转换:布尔值false
和true 转换为0 和1,字符串根据特殊规则进行解析,对象会调用它们的valueOf()和/或toString()
方法以得到可以转换的值

3.5.2 位操作符

ECMAScript中的所有数值都以IEEE 754 64 位格式存储,但位操作并不直接应用到64 位表示,而是先把值转换为
32 位整数,再进行位操作,之后再把结果转换为64 位。
有符号整数使用32 位的前31 位表示整数值。第32 位表示数值的符号,如0 表示正,1 表示负
负数的二进制表示:
(1)绝对值的二进制表示
(2)找到数值的补数(反码) 取反
(3)给结果+1
let num = -18;
console.log(num.toString(2)); // “-10010”

1.按位非:~
按位取反 最终效果:对数值取反并减1
2.按位与: &
3.按位或: |
4.按位异或 ^

5.左移:<< 会保留操作数值的符号
6.有符号右移:>> 符号位的值来填充左边的空位
7.无符号右移:>>> 不管符号位 一律补0

3.5.3 布尔操作符

1.逻辑非 !->只有5种值为取反为true:null/undefined/NaN/0/“” !!->Boolean()给出变量真正对应的布尔值
2.逻辑与 && 具有false短路特性
不一定返回布尔值,返回值遵循的规则
如果第一个操作数是对象,则返回第二个操作数。
 如果第二个操作数是对象,则只有第一个操作数求值为true 才会返回该对象。
 如果两个操作数都是对象,则返回第二个操作数。
 如果有一个操作数是null,则返回null。
 如果有一个操作数是NaN,则返回NaN。
 如果有一个操作数是undefined,则返回undefined
3.逻辑或 || 具有true短路特性
不一定返回布尔值,返回值遵循的规则:
如果第一个操作数是对象,则返回第一个操作数。
 如果第一个操作数求值为false,则返回第二个操作数。
 如果两个操作数都是对象,则返回第一个操作数。
 如果两个操作数都是null,则返回null。
 如果两个操作数都是NaN,则返回NaN。
 如果两个操作数都是undefined,则返回undefined。

3.5.4 乘性操作符

1.乘法操作符: *
如果操作数都是数值,则执行常规的乘法运算,即两个正值相乘是正值,两个负值相乘也是正
值,正负符号不同的值相乘得到负值。如果ECMAScript 不能表示乘积,则返回Infinity 或
-Infinity。
 如果有任一操作数是NaN,则返回NaN。
 如果是Infinity 乘以0,则返回NaN。
 如果是Infinity 乘以非0 的有限数值,则根据第二个操作数的符号返回Infinity 或-Infinity。
 如果是Infinity 乘以Infinity,则返回Infinity。
 如果有不是数值的操作数,则先在后台用Number()将其转换为数值,然后再应用上述规则。
2.除法操作符:/
 如果操作数都是数值,则执行常规的除法运算,即两个正值相除是正值,两个负值相除也是正
值,符号不同的值相除得到负值。如果ECMAScript不能表示商,则返回Infinity 或-Infinity。
 如果有任一操作数是NaN,则返回NaN。
 如果是Infinity 除以Infinity,则返回NaN。
 如果是0 除以0,则返回NaN。
 如果是非0 的有限值除以0,则根据第一个操作数的符号返回Infinity 或-Infinity。
 如果是Infinity 除以任何数值,则根据第二个操作数的符号返回Infinity 或-Infinity。
 如果有不是数值的操作数,则先在后台用Number()函数将其转换为数值,然后再应用上述规则。
3.取模操作符:% —取余数
 如果操作数是数值,则执行常规除法运算,返回余数。
 如果被除数是无限值,除数是有限值,则返回NaN。
 如果被除数是有限值,除数是0,则返回NaN。
 如果是Infinity 除以Infinity,则返回NaN。
 如果被除数是有限值,除数是无限值,则返回被除数。
 如果被除数是0,除数不是0,则返回0。
 如果有不是数值的操作数,则先在后台用Number()函数将其转换为数值,然后再应用上述规则。

3.5.5 指数操作符 **=Math.pow()

3.5.6 加性操作符

1.加法操作符
如果两个操作数都是数值,加法操作符执行加法运算并根据如下规则返回结果:
 如果有任一操作数是NaN,则返回NaN;
 如果是Infinity 加Infinity,则返回Infinity;
 如果是-Infinity 加-Infinity,则返回-Infinity;
 如果是Infinity 加-Infinity,则返回NaN;
 如果是+0 加+0,则返回+0;
 如果是-0 加+0,则返回+0;
 如果是-0 加-0,则返回-0。
let num1 = 5;
let num2 = 10;
let message = “The sum of 5 and 10 is “ + (num1 + num2);
console.log(message); // “The sum of 5 and 10 is 15”
2.减法操作符
 如果两个操作数都是数值,则执行数学减法运算并返回结果。
 如果有任一操作数是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()方法,然后再
将得到的字符串转换为数值。
以下示例演示了上面的规则:
let result1 = 5 - true; // true 被转换为1,所以结果是4
let result2 = NaN - 1; // NaN
let result3 = 5 - 3; // 2
let result4 = 5 - “”; // “”被转换为0,所以结果是5
let result5 = 5 - “2”; // “2”被转换为2,所以结果是3
let result6 = 5 - null; // null 被转换为0,所以结果是5

3.5.7 关系操作符

1.大写字母的编码都小于小写字母的编码
let result = “Brick” < “alphabet”; // true
要得到确实按字母顺序比较的结果,就必须把两者都转换为相同的大小写形式(全大写或全小写),
然后再比较:
let result = “Brick”.toLowerCase() < “alphabet”.toLowerCase(); // false

2.任何关系操作符在涉及比较NaN 时都返回false
let result1 = NaN < 3; // false
let result2 = NaN >= 3; // false

3.5.8 相等操作符

1.== / != 判断强制类型转换后的值 是否相等
在进行比较时,这两个操作符会遵循如下规则。
 null 和undefined 相等。
 null 和undefined 不能转换为其他类型的值再进行比较。
 如果有任一操作数是NaN,则相等操作符返回false,不相等操作符返回true。记住:即使两
个操作数都是NaN,相等操作符也返回false,因为按照规则,NaN 不等于NaN。
 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,
则相等操作符返回true。否则,两者不相等。
2.===/!==判断类型和值都相等
null == undefined //true
null === undefined //false

3.5.9 条件操作符

variable = boolean_expression ? true_value : false_value;

3.5.10 赋值操作符

*=等等 简写语法不会提升性能

3.5.11 逗号操作符

let num = (5, 1, 4, 8, 0); // num 的值为0

3.6 语句—流控制语句

3.6.1 if语句

3.6.2 do-while语句

后测试循环经常用于这种情形:循环体内代码在退出前至少要执行一次。

3.6.3 while语句

先测试语句 循环体内代码可能不执行

3.6.4 for语句

3.6.5 for-in语句

for-in 语句是一种严格的迭代语句,用于枚举对象中的非符号键属性,语法如下:
for (property in expression) statement
下面是一个例子:
for (const propName in window) {
document.write(propName);
}
for-in 语句不能保证返回对象属性的顺序。换句话说,所有可枚举的属性都会返回一次,但返回的顺序可能会因浏览器而异。
如果for-in 循环要迭代的变量是null 或undefined,则不执行循环体。

3.6.6 for-of语句

for-of 语句是一种严格的迭代语句,用于遍历可迭代对象的元素,语法如下:
for (property of expression) statement
下面是示例:
for (const el of [2,4,6,8]) {
document.write(el);
}
如果尝试迭代的变量不支持迭代,则for-of 语句会抛出错误。

3.6.7 标签语句

标签语句用于给语句加标签,语法如下:
label: statement
下面是一个例子:
start: for (let i = 0; i < count; i++) {
console.log(i);
}
在这个例子中,start 是一个标签,可以在后面通过break 或continue 语句引用。标签语句的
典型应用场景是嵌套循环。

3.6.8 break 和continue 语句

break 语句用于立即退出循环,强制执行循环后的下一条语句。
continue 语句也用于立即退出循环,但会再次从循环顶部开始执行。

3.6.9 with语句

with(location) {
let qs = search.substring(1);
let hostName = hostname;
let url = href;
}
将代码作用域设置为特定的对象 严格模式不允许使用with 语句,否则会抛出错误

3.6.10 switch 语句

switch语句可以用于所有数据类型
条件的值不需要是常量 case后 可跟变量或表达式
比较条件的值时会使用全等操作符 不进行数据类型转换

3.7 函数

function functionName(arg0, arg1,…,argN) {
statements
}

3.8 小结

ECMAScript 中的基本数据类型包括Undefined、Null、Boolean、Number、String 和Symbol。
 与其他语言不同,ECMAScript 不区分整数和浮点值,只有Number 一种数值数据类型。
 Object 是一种复杂数据类型,它是这门语言中所有对象的基类。
 严格模式为这门语言中某些容易出错的部分施加了限制。
 ECMAScript 提供了C 语言和类C 语言中常见的很多基本操作符,包括数学操作符、布尔操作符、
关系操作符、相等操作符和赋值操作符等。
 这门语言中的流控制语句大多是从其他语言中借鉴而来的,比如if 语句、for 语句和switch
语句等。
ECMAScript 中的函数与其他语言中的函数不一样。
 不需要指定函数的返回值,因为任何函数可以在任何时候返回任何值。
 不指定返回值的函数实际上会返回特殊值undefined。