JavaScript语语法基础部分一览,包括面试常见的“深浅拷贝”、数据结构操作(如数组插入等)需熟记于心。
? 条件运算符
1 | let result = condition ? value1 : value2; |
注:如果条件为真,则返回value1
;否则,返回value2
‘??’空值合并
1 | let res = a ?? b; |
- 如果a已定义,则结果为a
- 否则,结果为b
用例
1 | let user = "John"; |
对象克隆
Object.assign(浅拷贝)
1 | Object.assign(target_object, [source_object1, source_object2, source_object3...]) |
用例
1 | let user = { name:"Will"} |
使用for loop实现
1 | let user = { |
深层克隆(深拷贝)
由于上述的对象克隆(无论是for loop还是Object.assign)都是假设所有的属性为原始类型,但是对于对象的引用类型,则会失效。因为它会直接复制其引用,因此双方仍然会共用一个属性值
因此,我们需要深层克隆。
Json.parse(JSON.stringify(xxxx)) — 通常
- Pros:简单易用
- Cons:
1. 会忽略undefined 2. 会忽略symbol 3. 不能序列化函数 4. 不能解决循环引用的问题
1 | let a = { |
MessageChannel(消息通道)
Pros:
1. 能解决undefined 2. 能解决循环引用
Cons:异步函数
1 | function deepClone(obj) { |
垃圾回收
简言之:JS引擎有一个被称作垃圾回收器的东西在后台自动运行,它监控所有对象的状态,然后删除那些已经不可达的对象(即不能访问到它了)
构造器
构造函数
约定:
- 命名以大写字母开头
- 只能由new操作符来执行
可选链 ?.
简言之:如果可选链前面的部分是null或undefined则返回undefined;否则,返回该部分以及后面的访问属性
value?.prop
:
注:这里的不存在指的是该变量为null和undefined
- 如果value存在,则返回value.prop
- 如果value不存在(即为null或undefined),则返回undefined,而不会像直接用
value.prop
一样报错
例子:
1 | let user = {}; |
扩展
obj?.prop
obj?.[prop]
obj.methodName?.()
最后
注意使用?.
时,一定要确保其前面的变量已经声明,否则同样会报错
Symbol类型
定义
“Symbol”表示唯一的标识符,使用Symbol()来创建这种类型的值
1 | let uniqueId = Symbol(); |
Symbol保证唯一性,即使具有相同描述的Symbol,它们的值也不同
Symbol不会被自动转化为字符串
1 | let uniqueId = Symbol('id'); |
Symbol可以用来创建对象的“隐藏”属性
可以作为对象的属性键
注:在对象字面量{…}中使用Symbol作为属性键时,需要将它用方括号括起来
1 | let id = Symbol('id'); |
Symbol在for…in循环中会跳出,但Object.assign方法却不会忽略symbol属性键
全局Symbol注册表
注:使用Symbol.for("key")
的方式从全局注册表中读取描述为key的Symbol,如果不存在则创建一个新的Symbol(Symbol(key)),并通过给定的key将其存储在注册表中。
1 | let id = Symbol.for('id'); |
反向调用
1 | let sym = Symbol('id'); |
数据类型
数字类型
toString(base)
base表示进制,例如base=2,就以二进制的形式进行返回该数的字符串形式
base的范围是2-36,常用的是2、16、36
如果在数字上直接调用该方法,则要使用2个点。如;
100..toString(2)
,第一个点JS识别为小数点
1 | let num = 2; |
舍入rounding
- Math.floor 向下取整
- Math.ceil 向上取整
- Math.round 四舍五入
- Math.trunc 舍去小数点后
- toFixed(n) 四舍五入保留小数点后n位,返回一个字符串
1 | let num = 12.36; |
不精确计算
0.1 + 0.2 !== 0.3
原因:0.1 + 0.2的结果为0.3000…04。这是因为数字以其二进制的形式存储在内存中,即一个1和0的序列,同时在二进制中,可以保证以2为整数次幂能精确存储,因此0.1和0.2在二进制中均为无限二进制,无法被精确存储。
解决方法:使用toFixed()
舍去小数
1 | let sum = 0.1 + 0.2; |
isFinite和isNaN
- Infinity & -Infinity 表示无穷大
- NaN表示
Not-A-Number
isNaN判断一个参数是否为Number(判断之前先将它们专为数字然后再判断)
1 | isNaN("str"); // true |
parseInt & parseFloat
+
和Number()
可以进行数字转换,但如果其不是数字字符串则会转换失败并返回NaN
使用parseInt和parseFloat可以解决这个问题
1 | parseInt('100px'); // 100 |
parseInt(str, base)
1 | parseInt('0xff', 16); // 255 |
内建Math其他函数
- Math.random() — 返回一个0-1的随机数(不包括1)
- Math.max(a, b, c, …) —- 返回一个最大数
- Math.min(a, b, c, …) —- 返回一个最小数
- Math.pow(n, power) — 返回npower
e
en(n为数字)表示 $*10^{n}$
1 | let Twothousand = 2e3; // 2000 |
字符串
引号
1 | let s1 = 's'; |
反引号允许
- 使用
${...}
将任何表达式嵌入 - 换行
1 | let s4 = `s2是${s3}。1+2=${sum(1,2)}`; |
内置函数
length属性
1 | let word = "William"; |
获取某一位置的字符
1 | let word1 = "Hello"; |
可以使用for ... of
遍历字符串
1 | for (let alphabet of 'Hello') { |
字符串unmutable
大小写
$\textcolor{blue}{toUpperCase(), toLowerCase()}$
1 | 'hello world'.toUpperCase(); // HELLO WORLD |
查询子字符串
string.indexOf(substr, pos)
,substr为要查询的子字符串,pos为从指定位置开始查询
1 | let word = 'Hello Word!'; |
Others:
1 | "Hello".includes("el"); // true |
获取子字符串☑️
slice( start[, end) )
1 | let word = "HelloWorld"; |
substr( start[, length )
1 | let word = "HelloWorld"; |
比较字符串
小写字母总是大于大写字母
1 | 'a' > 'Z'; // true |
在JS内部,所有字符串都使用UTF-16编码(即:每个字符都有相应的数字代码)
1 | "zealand".codePointAt(0); // 122 |
数组Array
1. 声明
1 | let arr = new Array(); |
2. 队列Queue和栈Stack
队列Queue — First In First Out
push —— 在末端加一个元素
shift —— 取出队首一个元素,并返回该值
1
2
3
4
5
6
7let fruits = ["Apple", "Orange", "Pear"];
fruits.push("Banana"); //
fruits; // "Apple", "Orange", "Banana"
fruits.shift(); // "Apple"
fruits; // "Orange", "Banana"栈Stack — Last In First Out
push
pop —— 从末端取出一个元素,并返回该值
1
2fruits.pop(); // "Pear"
fruits; // "Apple", "Orange"unshift()
在数组首端添加元素
1
2fruits.unshift("Banana");
fruits; // "Banana", "Apple", "Orange"3.内部
数组本质上也是一个对象,arr[0]
本质上类似obj[key]
,arr是对象,数字是键
4.性能
push/pop
运行较快,而shift/unshift
运行较慢
5.循环
1 | let arr = ["Apple", "orange", "Mango"]; |
6. length 可修改
7. toString()
1 | let arr = ["Apple", "orange", "Mango"]; |
数组的方法‼️
splice( start[, deleteCount, element1, element2, … ) — in-place
可以实现插入、删除
1 | let arr = ["I", "study", "JavaScript", "right", "now"]; |
slice( start[, end) )
1 | let arr = ['h', 'e', 'l', 'l', 'o']; |
concat(arg1, arg2, …)
1 | let arr = [1,2,3]; |
遍历:forEach
array.forEach可以实现为数组中的每个元素都运行一个函数
1 | let arr = [...]; |
用例如下:
1 | let arr = ["Nottingham", "London", "Manchestor"]; |
搜索
indexOf, lastIndexOf, includes
1 | let arr = [1,2,3]; |
find, findindex
1 | let arr = [1,2,3]; |
用例
1 | let users = [ |
filter⭐️
与find的类似,不过区别在于如果其返回true,搜索会继续直到遍历完整个数组,并返回所有匹配元素组成的数组。
1 | let user = users.filter(user => item.id <= 2); |
转换数组
map
对数组中的每个元素调用map参数中的该函数,并返回结果数组
1 | let res = arr.map(function(item, index, array) { |
用例
1 | let names = ["Will", "Dions", "Akshay"]; |
sort — in-place
对数组进行原位(in-place)排序,更改数组中元素的顺序,其语法为数组.sort(排序函数)
1 | let arr = [1,2,9,4]; |
实际上,排序函数可以返回任意数字(正数代表”大于”,负数代表”小于”);同时使用箭头函数会更简洁
1 | let arr = [2,9,8,1]; |
localeCompare
1 | let countries = ['Österreich', 'Andorra', 'Vietnam']; |
reverse — in-place
1 | let arr = [1,2,3]; |
split, join
分割字符串为数组
1
2
3let names = "Will, Dion, Akshay";
let arr = names.split(', ');
arr; // ["Will", "Dion", "Akshay"]合并数组元素为字符串
1
2let strName = arr.join(" ");
strName; // "Will Dion Akshay"
reduce, reduceRight
用于根据数组计算单个值
1 | let value = arr.reduce(function(accumulator, item, index, array) { |
accumulator本质上是个累加器。
应用reduce函数时,它会将上一个参数函数调用的结果作为第一个参数传递给下一个函数
1 | let arr = [1,2,3,4,5]; |
Array.isArray
数组是基于对象的,因此无法使用typeof
进行判断
1 | typeof []; // object |
可迭代对象(Iterable Object)
可迭代对象是数组的泛化,任何对象都可以被定制为可在for...of
循环中使用的对象。
Symbol.iterator
为对象添加一个名为Symbol.iterator
方法,便可使其可迭代
- 当
for...of
循环时,会调用该方法,同时该方法须返回一个迭代器(即有next()方法的对象) - 此后,
for...of
仅作用于这个被返回的迭代器 for...of
会调用next()
方法来取得下一个数值- next()返回的结果对象格式必须为:
{done: Boolean, value: any}
。其中当done=true
时,迭代结束,否则value
为下一个值
1 | let range = { |
字符串是可迭代的
1 | for (let char of 'test') { |
显示调用迭代器
1 | let str = 'test'; |
Array.from — 将可迭代或类数组转化为数组对象(可使用数组的方法)
概念
- Iterable(可迭代): 实现了Symbol.iterator方法的对象
- Array-like(类数组): 有索引和length属性的对象
用例
1 | let arrayLike = { |
Array.from还提供了一个可选参数—映射(mapping)函数
1 | Array.from(obj, mapFunction, thisArg); // thisArg允许我们为设置该函数设置this |
用例
1 | let arr = Array.from(range, num => num * num); // 该函数会被应用于数组中的每个元素 |
字符串转换为数组
1 | let str = 'test'; |
Map and Set — 映射和集合
Map
- Map是带键的数据项,但与对象(会将其转换为字符串)不同,键可以是任意类型
- 使用set、get方法
1 | let map = new Map(); |
链式调用
map.set
调用会返回map本身
1 | map.set('1', 'hello') |
Map迭代
在map中使用循环,可使用如下方法:
- map.keys() — 返回所有的键
- map.values() — 返回所有的值
- Map.entries() — 返回所有的键值对
1 | let recipe = new Map([ |
forEach
1 | recipe.forEach( (value, key, map) => { |
Object.entries — 从对象—>Map
new Map([key, value], [key, value], ...)
Object.entries()可以返回对象的键值对数组(其格式完全符合Map
所需)
1 | let obj = { |
Object.fromEntries — 从Map—>对象
给定一键值对数组返回一个对象
1 | let prices = Object.fromEntries([ |
Set
Set是集合并且无重复值,其方法有:
- new Set(iterable) — 创建一个set,如有iterable,将从其中复制值到set中
- set.add(value) — 添加一个值并返回set
- set.delete(value) — 删除值,如该值存在则返回true,否则返回false
- set.has(value) — 如value存在set中,则返回true;否则返回false
- set.clear() — 清空set
- set.size — 返回元素个数
可迭代(iterable)
可以使用for...of
和forEach
1 | let set = new Set(["oranges", "apples", "bananas"]); |
用于迭代的方法(同Map)
- set.keys()
- set.values()
- set.entries()
弱映射和弱集合(WeakMap & WeakSet)
略
Object.keys, values, entries
- Object.keys(obj) — 返回一个包含该对象的所有键的数组
- Object.values(obj) — 返回一个包含该对象的所有值的数组
- Object.entries(obj) — 返回一个包含该对象所有键值对(即[key, value])的数组
解构赋值
数组(Array)解构
基本结构为:[变量名1, 变量名2, ...]=[...]
变量名无所谓
1
2
3let arr = ["Will", "Koo"];
let [firstName, lastName] = arr;使用逗号忽略不想要的元素
1
2
3
4let [firstName, , thirdOne] = ["Will", "Sam", "Dion", "Ricky"];
firstName; // "Will"
thirdOne; // "Dion"任何可迭代对象都可以用数组解构赋值
1
2
3let [a,b,c] = "USA";
[a,b,c]; //['U','S','A']
let [one, two, three] = new Set(['U','S','A']);交换变量值
1
2
3
4
5
6let guest = "People";
let admin = "Will";
[guest, admin] = [admin, guest];
[guest, admin]; // ['Will', "People"]剩余…
1
2
3let [name1, name2, ...rest] = ["Will", "Dion", "Akshay", "Ricky", "Sam"];
rest; // ["Akshay", "Ricky", "Sam"]默认值
1
2
3
4let [name1="Anonymous", name2="Will"] = ["Dion"];
name1; // "Dion"
name2; // "Will"
对象解构
基本语法:let {var1, var2, ...} = {var1:..., var2:..., ...}
,其中等号左侧的变量名顺序不重要,但变量名必须与右侧对象中的属性名一致。
用例:
1 | let personAdmin = { |
更改左侧的变量名
1
2
3
4let {name: n, age: a, major} = personAdmin;
n; // "Will"
a; // 24
major; // "CS"默认值 =
1
2
3
4
5
6
7
8let person = {
name: "Dave"
};
let {name, age=90, job="Invester"} = person;
name; // "Dave"
age; //90
job; // "Inverster"结合:
1
2
3
4let {name, age: a = 90, job: j = "Invester"} = person;
name; // "Dave"
a; //90
j; // "Inverster"剩余…
1
2
3
4let {name, ...others} = personAdmin;
name; // "Will"
others; // {age: 24, major: "CS"}提前声明变量,之后调用
不使用
let
时进行解构赋值,需要在最外侧加一个圆括号。这是因为JS会将主代码流的{...}
当作一个代码块1
2
3let name, age, major;
({name, age, major} = personAdmin);
JSON
定义:JSON(JavaScript Object Notation)是表示值和对象的通用格式
JSON.stringify — 将对象转为JSON
1 | let student = { |
注:通过
JSON.stringify()
方法将对象转换为字符串的过程称为 JSON编码 或 序列化(serialized)或字符串化 或 编组化JSON
支持一下数据类型:- Object
{...}
- Array
[...]
- Primitive:
- string
- number
- boolean
- null
- Object
不能有循环引用
1
2
3
4
5
6
7
8
9
10
11
12let a = {
rank: 1
};
let b = {
rank: 2
};
b.teacher = a;
a.student = b;
JSON.stringfiy(b); // Error
JSON.stringify完整语法
JSON的完整语法为: JSON.stringify(value, replacer, space)
。
其中:
- value — 要编码的对象
- replacer — 要编码的属性数组(或者使用映射函数)
- space — 用于格式化的空格数量
使用完整语法避免循环引用报错
- 使用属性数组
1
2
3
4
5
6
7
8
9
10
11
12let a = {
rank: 1
};
let b = {
rank: 2
};
b.teacher = a;
a.student = b;
JSON.stringify(b, ['rank', 'teacher']); // '{"rank":2,"teacher":{"rank":1}}'- 使用映射函数
1
2
3JSON.stringify(b, function replacer(key, value) {
return (key === 'student') ? undefined : value;
});
自定义toJSON
说明:如果对象有toJSON
方法,则JSON.stringify
会自动调用该方法
例如:
1 | let a = { |
JSON.parse — 将JSON字符串转换为合适的数据格式
基本语法:JSON.parse(str, reviver)
其中:
- str:要解析的JSON字符串
- reviver:可选函数,该函数对每个键值对
(key, value)
调用,并可改变其值
1 | let nums = "[0,1,2,3]"; |
使用reviver
用例:
1 | let meetingJSON = '{"title": "Conference", "date": "2021-10-04T16:00:00.000Z"}'; |
总结
- JSON是一种数据格式
- JSON支持object, array, string, number, boolean, null
- JavaScript提供序列化(serialize)成JSON的方法
JSON.stringify
和解析JSON的方法JSON.parse
- 如果一个对象有
toJSON
方法,那么JSON.stringify
会自动调用