这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战
数据结构与算法的关系
- 数据结构:计算机存储,组织数据的方式,就像锅碗瓢盆。
- 算法:一系列解决问题的清晰指令,就像食谱。
- 程序 = 数据结构 + 算法
- 数据结构为算法提供服务,算法围绕数据结构操作。
数据结构分类
- 有序:栈,队列,链表
- 无序:集合,字典
- 节点关联性:树,堆,图
时间 / 空间复杂度
数学知识
log2
在数学中,对数是对求幂的逆运算,正如除法是乘法的逆运算,反之亦然。 [6] 这意味着一个数字的对数是必须产生另一个固定数字(基数)的指数。 在简单的情况下,乘数中的对数计数因子。更一般来说,乘幂允许将任何正实数提高到任何实际功率,总是产生正的结果,因此可以对于b不等于1的任何两个正实数b和x计算对数。如果a的x次方等于N(a>0,且a≠1),那么数x叫做以a为底N的对数(logarithm),记作x=loga N。其中,a叫做对数的底数,N叫做真数。 [1]
矩阵
在数学中,矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合 [1] ,最早来自于方程组的系数及常数所构成的方阵。这一概念由19世纪英国数学家凯利首先提出。
对应在前端领域,是我们的多维数组,和双重for循环
时间复杂度
在计算机科学中,时间复杂性,又称时间复杂度,算法的时间复杂度是一个函数,它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,亦即考察输入值大小趋近无穷时的情况。
一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。
function traverse(arr) {
var n = arr.length; // 执行1次
for(var i=0;i<n;i++) { // var i = 0; 执行1次 | i < n 执行n次 | i++ 执行 n+1次
console.log(arr[i]) // 执行n次
}
}
执行次数:1 + 1 + n + (n + 1) + n = 3n + 3
一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f (n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
时间复杂度计算:基于n趋向于无限大,3n + 3在n趋向于无限大的时候,3n + 3趋向于n。所以这里的时间复杂度为O(n)。
空间复杂度
空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n))。比如直接插入排序的时间复杂度是O(n^2),空间复杂度是O(1) 。而一般的递归算法就要有O(n)的空间复杂度了,因为每次递归都要存储返回信息。一个算法的优劣主要从算法的执行时间和所需要占用的存储空间两个方面衡量。
一个算法的空间复杂度只考虑在运行过程中为局部变量分配的存储空间的大小,它包括为参数表中形参变量分配的存储空间和为在函数体中定义的局部变量分配的存储空间两个部分。
function traverse(arr) {
var n = arr.length; // n占用1空间
for(var i=0;i<n;i++) { // arr占用n空间
console.log(arr[i]) // i占用1空间
}
}
空间复杂度为O(n)。
数据结构简介
数组
js原生支持的数据结构
const array = [1, 2, 3, 4, 5];
队列
栈是一种先进先出(FIFO,First In First Out)的数据结构。 先进先出,js数组支持,限定使用方法:头部弹出元素【shift】,尾部新增元素【push】
const array = [1, 2, 3, 4, 5];
array.push(6)
array.shift();
console.log(array); // [2, 3, 4, 5, 6]
栈
栈是一种后进先出(LIFO,Last In First Out)的数据结构。 后进先出,js数组支持,限定使用方法:尾部弹出元素【pop】,尾部新增元素【push】
const array = [1, 2, 3, 4, 5];
array.push(6);
array.pop()
console.log(array); // [1, 2, 3, 4, 5]
链表
多个元素组成的列表 元素存储不连续,通过next指针连接一起。
var node1Item = {
value: 'value',
next: node2Item
};
var node2Item = {
value: 'value',
next: null
}
数组 VS 链表
集合
一组无序且唯一的数据结构。 ES6 集合:Set。 集合的常用操作:去重,判断某元素是否在集合中,求交集。
const array = [1, 2, 3, 4, 5];
// 去重
const set1 = [...new Set(array)];
// 判断元素是否在集合中
const set2 = new Set(array);
set2.has(2);
// 求取交集
const set3 = new Set(array);
const set4 = new Set([2, 4, 6]);
const joinSet = new Set([...set3].filter(item => set4.has(item)))
字典
与集合类似,字典也是一种存储谓一值的数据结构,但是以键值对的形式来存储。 ES6中有字典,名为Map。 字典的常用操作:键值对的增删改查。
const s = new Map();
s.set('name', 'rodchen');
s.set('age', '28');
s.get('name'); // 'rodchen'
s.get('age');
s.delete('age');
s.get('age');
// 修改数据值
s.set('name', 'chenyun');
s.clear();
树
一种分层数据的抽象模型 前端工作中用到的树结构:DOM树,树形控件。
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
children: [
{
value: 'xihu',
label: 'West Lake',
},
],
},
],
}
常用操作:广度优先遍历,深度优先遍历
图
图示网络结构的抽象模型,是一组由边链接的节点。 图可以表示任何的二元关系,比如道路,航班。 js中没有图,通过Object和Array构件图。
图的表示法:邻接矩阵,邻接表
常用操作:广度优先遍历,深度优先遍历
堆
堆是一种完全二叉树
从第一层到倒数第二层,每一层都是满的,也就是说每一层的结点数都达到了当前层所能达到的最大值最后一层的结点是从左到右连续排列的,不存在跳跃排列的情况(也就是说这一层的所有结点都集中排列在最左边)。
下面这种就不行
辅助知识
数组
ES5及之前方法
创建数组
let colors = new Array();
let colors = new Array(20);
let colors = new Array("red", "blue", "green");
let colors = ["red", "blue", "green"];
判断数组
value instanceof Array
Array.isArray(value)
转换方法
所有对象都有 toLocaleString()、toString()和 valueOf()方法。
- valueOf()返回的还是数组本身
- toString()返回由数组中每个值的等效字符串拼接而成的一个逗号分隔的字符串,内部调用每个item的toString方法
- toLocaleString()则是调用每个item项目的toLocaleString()
let colors = ["red", "blue", "green"];
console.log(colors.toString()); // red,blue,green
console.log(colors.valueOf()); // red,blue,green
console.log(colors); // ["red", "blue", "green"]
栈方法
ECMAScript 数组提供了 push()和 pop()方法,以实现类似栈的行为。
队列方法
ECMAScript 数组提供了 push()和 shift()方法,以实现类似栈的行为。
排序方法
数组有两个方法可以用来对元素重新排序:reverse()和 sort()。
操作方法
- 合并方法:concat
- 从现有数组截取创建新数组:slice
- 在现有数组当中删除 & 新增元素,返回删除的数组:splice
// slice
let colors = ["red", "green", "blue", "yellow", "purple"];
let colors2 = colors.slice(1);
let colors3 = colors.slice(1, 4); // 返回元素的开始索引和结束索引
alert(colors2); // green,blue,yellow,purple
alert(colors3); // green,blue,yellow
// splice
let colors = ["red", "green", "blue"];
let removed = colors.splice(0,1); // 删除第一项
alert(colors); // green,blue
alert(removed); // red,只有一个元素的数组
removed = colors.splice(1, 0, "yellow", "orange"); // 在位置 1 插入两个元素
alert(colors); // green,yellow,orange,blue
alert(removed); // 空数组
removed = colors.splice(1, 1, "red", "purple"); // 插入两个值,删除一个元素
alert(colors); // green,red,purple,orange,b
搜索和位置方法
indexOf()、lastIndexOf()
- indexOf()和 includes()方法从数组前头(第一项)开始向后搜索,而 lastIndexOf()从数组末尾(最后一项)开始向前搜索。
- indexOf()和 lastIndexOf()都返回要查找的元素在数组中的位置,如果没找到则返回 -1。
- includes()返回布尔值,表示是否至少找到一个与指定元素匹配的项。
迭代方法
- every():对数组每一项都运行传入的函数,如果对每一项函数都返回 true,则这个方法返回 true。
- filter():对数组每一项都运行传入的函数,函数返回 true 的项会组成数组之后返回。
- forEach():对数组每一项都运行传入的函数,没有返回值。
- map():对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组。
- some():对数组每一项都运行传入的函数,如果有一项函数返回 true,则这个方法返回 true。
这些方法都不改变调用它们的数组。
归并方法
reduce()和 reduceRight()。这两个方法都会迭代数组的所有项,并在此基础上构建一个最终返回值。reduce()方法从数组第一项开始遍历到最后一项。而 reduceRight()从最后一项开始遍历至第一项。
- reduce()和 reduceRight()的函数接收 4 个参数:上一个归并值、当前项、当前项的索引和数组本身。这个函数返回的任何值都会作为下一次调用同一个函数的第一个参数。
- 如果没有给这两个方法传入可选的第二个参数(作为归并起点值),则第一次迭代将从数组的第二项开始,因此传给归并函数的第一个参数是数组的第一项,第二个参数是数组的第二项。
let values = [1, 2, 3, 4, 5];
let sum = values.reduce((prev, cur, index, array) => prev + cur);
alert(sum); // 15
ES6+方法
创建数组
from()用于将类数组结构转换为数组实例,而 of()用于将一组参数转换为数组实例。
from
// 字符串会被拆分为单字符数组
console.log(Array.from("Matt")); // ["M", "a", "t", "t"]
// 可以使用 from()将集合和映射转换为一个新数组
const m = new Map().set(1, 2)
.set(3, 4);
const s = new Set().add(1)
.add(2)
.add(3)
.add(4);
console.log(Array.from(m)); // [[1, 2], [3, 4]]
console.log(Array.from(s)); // [1, 2, 3, 4]
// Array.from()对现有数组执行浅复制
const a1 = [1, 2, 3, 4];
const a2 = Array.from(a1);
console.log(a1); // [1, 2, 3, 4]
alert(a1 === a2); // false
// 可以使用任何可迭代对象
const iter = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
yield 4;
}
};
console.log(Array.from(iter)); // [1, 2, 3, 4]
/* Array.from()还接收第二个可选的映射函数参数。这个函数可以直接增强新数组的值,而无须像
* 调用 Array.from().map()那样先创建一个中间数组。还可以接收第三个可选参数,用于指定映射函
* 数中 this 的值。但这个重写的 this 值在箭头函数中不适用。
*/
const a1 = [1, 2, 3, 4];
const a2 = Array.from(a1, x => x**2);
const a3 = Array.from(a1, function(x) {return x**this.exponent}, {exponent: 2});
console.log(a2); // [1, 4, 9, 16]
console.log(a3); // [1, 4, 9, 16]
of
console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4]
console.log(Array.of(undefined)); // [undefined]
迭代器方法
在 ES6 中,Array 的原型上暴露了 3 个用于检索数组内容的方法:keys()、values()和entries()。keys()返回数组索引的迭代器,values()返回数组元素的迭代器,而 entries()返回索引/值对的迭代器:
const a = ["foo", "bar", "baz", "qux"];
const aKeys = Array.from(a.keys());
const aValues = Array.from(a.values());
const aEntries = Array.from(a.entries());
console.log(aKeys); // [0, 1, 2, 3]
console.log(aValues); // ["foo", "bar", "baz", "qux"]
console.log(aEntries); // [[0, "foo"], [1, "bar"], [2, "baz"], [3, "qux"]]
复制 & 填充方法
fill方法
使用 fill()方法可以向一个已有的数组中插入全部或部分相同的值。开始索引用于指定开始填充的位置,它是可选的。如果不提供结束索引,则一直填充到数组末尾。负值索引从数组末尾开始计算。也可以将负索引想象成数组长度加上它得到的一个正索引。
// 用 5 填充整个数组
zeroes.fill(5);
console.log(zeroes); // [5, 5, 5, 5, 5]
zeroes.fill(0); // 重置
// 用 6 填充索引大于等于 3 的元素
zeroes.fill(6, 3);
console.log(zeroes); // [0, 0, 0, 6, 6]
zeroes.fill(0); // 重置
// 用 7 填充索引大于等于 1 且小于 3 的元素
zeroes.fill(7, 1, 3);
console.log(zeroes); // [0, 7, 7, 0, 0];
zeroes.fill(0); // 重置
copyWithin方法
copyWithin()会按照指定范围浅复制数组中的部分内容,然后将它们插入到指定索引开始的位置。开始索引和结束索引则与 fill()使用同样的计算方法
let intArray = [1, 2, 3, 4, 5];
intArray.copyWithin(2, 0, 3); // [1, 2, 1, 2, 3]
搜索和位置方法
includes() ES7 includes()返回布尔值,表示是否至少找到一个与指定元素匹配的项
find & findIndex find()和 findIndex()方法使用了断言函数。这两个方法都从数组的最小索引开始。find()返回第一个匹配的元素,findIndex()返回第一个匹配元素的索引。这两个方法也都接收第二个可选的参数,用于指定断言函数内部 this 的值。
数组拉平
Array.prototype.flat()用于将嵌套的数组“拉平”,变成一维的数组。flat()默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()方法的参数写成一个整数,表示想要拉平的层数,默认为1.
[1, 2, [3, [4, 5]]].flat()
// [1, 2, 3, [4, 5]]
[1, 2, [3, [4, 5]]].flat(2)
// [1, 2, 3, 4, 5]
[1, [2, [3]]].flat(Infinity)
flatMap()方法对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]
字符串
ES5及之前方法
创建字符串
let stringObject = new String("hello world");
let message = "abcde";
合并字符串
concat:用于将一个或多个字符串拼接成一个新字符串。
截取字符串
slice()、substr()和 substring()。
第一个参数表示子字符串开始的位置,第二个参数表示子字符串结束的位置。对 slice()和 substring()而言,第二个参数是提取结束的位置(即该位置之前的字符会被提取出来)。对 substr()而言,第二个参数表示返回的子字符串数量。
let stringValue = "hello world";
console.log(stringValue.slice(3)); // "lo world"
console.log(stringValue.substring(3)); // "lo world"
console.log(stringValue.substr(3)); // "lo world"
console.log(stringValue.slice(3, 7)); // "lo w"
console.log(stringValue.substring(3,7)); // "lo w"
console.log(stringValue.substr(3, 7)); // "lo worl"
从上面看到slice,substring好像表现的一样,不同的点在于参数是负数。
- slice()方法将所有负值参数都当成字符串长度加上负参数值。
- substr()方法将第一个负参数值当成字符串长度加上该值,将第二个负参数值转换为 0。
- substring()方法会将所有负参数值都转换为 0。
第一个参数为负数的时候:
let stringValue = "hello world";
console.log(stringValue.slice(-3)); // "rld"
console.log(stringValue.substring(-3)); // "hello world"
console.log(stringValue.substr(-3)); // "rld"
两个参数都是负数的时候:
console.log(stringValue.slice(3, -4)); // "lo w"
console.log(stringValue.substring(3, -4)); // "hel"
console.log(stringValue.substr(3, -4)); // "" (empty string)
位置方法
indexOf()和 lastIndexOf()。这两个方法从字符串中搜索传入的字符串,并返回位置(如果没找到,则返回-1)。两者的区别在于,
- indexOf()方法从字符串开头开始查找子字符串
- 而 lastIndexOf()方法从字符串末尾开始查找子字符串
let stringValue = "hello world";
console.log(stringValue.indexOf("o")); // 4
console.log(stringValue.lastIndexOf("o")); // 7
清空两边空格
trim():返回新的字符串
let stringValue = " hello world ";
let trimmedStringValue = stringValue.trim();
console.log(stringValue); // " hello world "
console.log(trimmedStringValue); // "hello world"
字符串具有@@iterator特性
可使用for-of和解构操作
for (const c of "abcde") {
console.log(c);
}
// a
// b
// c
// d
// e
let message = "abcde";
console.log([...message]); // ["a", "b", "c", "d", "e"]
匹配功能
search / match / replace方法,具体可查看之前的文章:juejin.cn/post/697103…
比较方法
localeCompare(),这个方法比较两个字符串,返回如下 3 个值中的一个。
- 如果按照字母表顺序,字符串应该排在字符串参数前头,则返回负值。(通常是-1,具体还要看与实际值相关的实现。)
- 如果字符串与字符串参数相等,则返回 0。
- 如果按照字母表顺序,字符串应该排在字符串参数后头,则返回正值。(通常是 1,具体还要看与实际值相关的实现。)
下面是一个例子:
let stringValue = "yellow";
console.log(stringValue.localeCompare("brick")); // 1
console.log(stringValue.localeCompare("yellow")); // 0
console.log(stringValue.localeCompare("zoo")); // -1
ES6+方法
包含方法
startsWith()、endsWith()和 includes()。
- includes():返回布尔值,表示是否找到了参数字符串。
- startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
let message = "foobarbaz";
console.log(message.startsWith("foo")); // true
console.log(message.startsWith("bar")); // false
console.log(message.endsWith("baz")); // true
console.log(message.endsWith("bar")); // false
console.log(message.includes("bar")); // true
console.log(message.includes("qux")); // false
重复字符串
repeat方法返回一个新字符串,表示将原字符串重复n次。
'x'.repeat(3) // "xxx"
规则如下:
- 参数如果是小数,会被取整。
- 如果repeat的参数是负数或者Infinity,会报错。但是,如果参数是 0 到-1 之间的小数,则等同于 0,这是因为会先进行取整运算。0 到-1 之间的小数,取整以后等于-0,repeat视同为 0。
- 参数NaN等同于 0。
- 如果repeat的参数是字符串,则会先转换成数字。
ES8:字符串补全方法
ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。
padStart()和padEnd()一共接受两个参数,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
ES10 移除空格方法
ES2019 对字符串实例新增了trimStart()和trimEnd()这两个方法。它们的行为与trim()一致,trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。
const s = ' abc ';
s.trim() // "abc"
s.trimStart() // "abc "
s.trimEnd() // " abc"
正则方法
- matchAll():返回正则表达式在字符串的所有匹配
- ES12 replaceAll(): 替换字符串里所有的匹配项
对象
ES5及之前方法
创建对象
a. 对象直接量 b. 通过new的方式创建对象 c. Object.create() 是一个静态的方法,而不是提供给某个对象调用的方法。 一个参数是新建对象的原型,第二个为可选参数,用以对对象的属性进行说明。
定义属性
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。 Object.defineProperties() 方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
const object1 = {};
Object.defineProperty(object1, 'property1', {
value: 42,
writable: false
});
Object.defineProperties(obj, {
'property1': {
value: true,
writable: true
},
'property2': {
value: 'Hello',
writable: false
}
});
检测属性
- hasOwnProperty 判断一个属性是定义在对象本身还是继承自原型链。自身对象返回true,继承属性返回false。
- propertyIsEnumable 是hasOwnProperty()的增强版,只有检测是自有属性的可枚举性,如果是自有属性并且可枚举的返回true。
查询自有可枚举属性
Object.keys() 获取自有属性中枚举属性的合集字符串
查询自有属性属性
Object.getOwnPropertyDescriptor()方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
var o, d;
o = { get foo() { return 17; } };
d = Object.getOwnPropertyDescriptor(o, "foo");
// d {
// configurable: true,
// enumerable: true,
// get: /*the getter function*/,
// set: undefined
// }
Object.getOwnPropertyNames()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
var arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"]
原型操作
isPrototypeOf(): p.isPrototypeOf(o) 用来检测p是否是o的原型。
function Foo() {}
function Bar() {}
function Baz() {}
Bar.prototype = Object.create(Foo.prototype);
Baz.prototype = Object.create(Bar.prototype);
var baz = new Baz();
console.log(Baz.prototype.isPrototypeOf(baz)); // true
console.log(Bar.prototype.isPrototypeOf(baz)); // true
console.log(Foo.prototype.isPrototypeOf(baz)); // true
console.log(Object.prototype.isPrototypeOf(baz)); // true
ES6+方法
合并对象
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。
dest = {};
src = { id: 'src' };
result = Object.assign(dest, src);
// Object.assign 修改目标对象
// 也会返回修改后的目标对象
console.log(dest === result); // true
console.log(dest !== src); // true
console.log(result); // { id: src }
console.log(dest); // { id: src }
dest = {};
result = Object.assign(dest, { a: 'foo' }, { b: 'bar' });
console.log(result); // { a: foo, b: bar }
判断相等
Object.is() ECMAScript 6 规范新增了 Object.is(),这个方法与===很像,但同时也考虑到了上述边界情形。这个方法必须接收两个参数: 不同之处只有两个:一是+0不等于-0,二是NaN等于自身
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
查询属性
ES5 的Object.getOwnPropertyDescriptor()方法会返回某个对象属性的描述对象(descriptor)。ES2017 引入了Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象。
const obj = {
foo: 123,
get bar() { return 'abc' }
};
Object.getOwnPropertyDescriptors(obj)
// { foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: get bar],
// set: undefined,
// enumerable: true,
// configurable: true } }
原型操作
Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值)。 Object.setPrototypeOf() 方法设置一个指定的对象的原型 ( 即, 内部[[Prototype]]属性)到另一个对象或null。
// 格式
Object.setPrototypeOf(object, prototype)
Object.getPrototypeOf(obj);
// 用法
const o = Object.setPrototypeOf({}, null);
Object.setPrototypeOf方法的作用与__proto__相同,用来设置一个对象的原型对象(prototype)。
迭代器方法
Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。Object.entries()方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
const obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
// ["foo", "baz"]
Object.values(obj)
// ["bar", 42]
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]
ES6运算符扩展
指数运算符
ES2016 新增了一个指数运算符(**)
2 ** 2 // 4
2 ** 3 // 8
// 右结合运算 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
链判断运算符
编程实务中,如果读取对象内部的某个属性,往往需要判断一下,属性的上层对象是否存在。 ES2020 引入了“链判断运算符”(optional chaining operator)?.,简化上面的写法。
const firstName = message?.body?.user?.firstName || 'default';
const fooValue = myForm.querySelector('input[name=foo]')?.value
NUll判断运算符
读取对象属性的时候,如果某个属性的值是null或undefined,有时候需要为它们指定默认值。常见做法是通过||运算符指定默认值。但是属性的值如果为空字符串或false或0,默认值也会生效。
ES2020 引入了一个新的 Null 判断运算符??。它的行为类似||,但是只有运算符左侧的值为null或undefined时,才会返回右侧的值。
const headerText = response.settings.headerText ?? 'Hello, world!';
const animationDuration = response.settings.animationDuration ?? 300;
const showSplashScreen = response.settings.showSplashScreen ?? true;
??本质上是逻辑运算,它与其他两个逻辑运算符&&和||有一个优先级问题,它们之间的优先级到底孰高孰低。如果多个逻辑运算符一起使用,必须用括号表明优先级,否则会报错。
逻辑赋值运算符
// 或赋值运算符
x ||= y
// 等同于
x || (x = y)
// 与赋值运算符
x &&= y
// 等同于
x && (x = y)
// Null 赋值运算符
x ??= y
// 等同于
x ?? (x = y)
// 老的写法
user.id = user.id || 1;
// 新的写法
user.id ||= 1;
学习路线
来源于Lucifer在早早聊的分享其中的一页ppt。
- 复杂度分析:如何衡量算法的性能
- 基础的数据结构:线性数据结构 (数组,链表,栈,队列,哈希表)
- 基础的数据结构:非线形数据结构 (树,图)
- 排序算法:经典排序算法
- 递归:一种高级的思维技巧
- 暴力搜索篇:回溯,BFS,DFS
- 暴力优化篇:剪枝,滑动窗口,双指针,单调栈
- 高级搜索篇:二分法和位运算
- 动态规划篇:记忆化搜索与动态规划
- 分治:大事化小,小事化小
- 贪心:简单高效但不靠谱的算法
- 逆向思考
算法的掌握程度
来源于叶蒙蒙在早早聊分享的知识,也是一种自我在对于算法掌握的层次考量。
- 听说
- 知道名称和功能
- 了解
- 知道原理和优缺点
- 理解
- 知道过程和实现细节
- 能描述数据结构和控制流程
- 知道时空复杂度
- 实现
- 能够编程实现
- 应用
- 能够运⽤解决实际问题