这是一篇合作博文,感谢在原来文档的基础上添加了一些不是特别重要的内容@X·Y·C-NiFox同学。
一、基本数据操作
1、数值取整
Math.ceil(v); //向上整除 4/3=2;
Math.floor(v); //向下整除 4/3=1;
Math.round(v); //四舍五入
Math.abs(v); //绝对值
num.toFixed(2); //四舍五入2位
2、整数、浮点数数值转换
parseInt(v); //丢弃小数部分,保留整数部分
parseFloat("2.333"); //得到一个字符串的浮点数形式
3、常量——最大值、最小值
//最大值
Number.MAX_VALUE; //1.7976931348623157e+308
Number.MAX_SAFE_INTEGER; //9007199254740991
Infinity; //Infinity
//最小值
Number.MIN_VALUE; //5e-324
Number.MIN_SAFE_INTEGER; //-9007199254740991
4、大数操作(BigInt)
将数据类型转化为BigInt,可以进行大数加减乘除,除一元加号(+
)运算符外,所有算术运算符都可用于BigInt,在输出的时候需要去除最后的'n'。
var a = BigInt(1);
for(var i = BigInt(1); i < 1000; i++){
a = a*i;
}
二、数组
2.1 增删改查
2.2.1 增
- push——尾增(返回新数组长度)
- unshift——首增(返回新数组长度)
- splice(返回空数组)
- concat(用于合并两个或多个数组,返回新构建的数组,不影响原数组)
// 增-splice(开始位置, 要增加的元素数量, 要插入的元素) 返回空数组
let colors = ["red", "green", "blue"];
let removed = colors.splice(1, 0, "yellow", "orange")
console.log(colors) // [ 'red', 'yellow', 'orange', 'green', 'blue' ]
console.log(removed) // []
// 增-concat 返回新构建的数组
let colors1 = ['red', 'green', 'blue'];
let colors2 = colors1.concat('yellow', ['black', 'brown']);
console.log(colors1); // [ 'red', 'green', 'blue' ]
console.log(colors2); // [ 'red', 'green', 'blue', 'yellow', 'black', 'brown' ]
2.2.2 删
- pop——尾删(返回被删除项)
- shift——首删(返回被删除项)
- splice(返回被删除元素的数组)
- slice(返回新切片构建的数组,但不影响原数组)
// 删-splice(开始位置,要删除元素的数量) 返回删除元素的数组
let colors = ["red", "green", "blue"];
let removed = colors.splice(0,1); // 删除第一项
console.log(colors); // [ 'green', 'blue' ]
console.log(removed); // [ 'red' ]
// 删-slice(新构建数组的开始元素位置, 新构建数组的结束元素位置边界) 不包括结束元素
let colors1 = ["red", "green", "blue", "yellow", "purple"];
let colors2 = colors1.slice(1);
let colors3 = colors1.slice(1, 4);
console.log(colors1); // ['red', 'green', 'blue', 'yellow', 'purple']
console.log(colors2); // ['green', 'blue', 'yellow', 'purple']
console.log(colors3); // ['green', 'blue', 'yellow' ]
2.2.3 改
- 改:splice(删除部分元素,同时插入元素,影响原数组)
// 改-splice(开始位置, 要删除的元素数量, 要插入的元素) 返回删除元素的数组
let colors = ['red', 'green', 'blue'];
let removed = colors.splice(1, 1, 'purple'); // 删除一个元素,插入一个元素
console.log(colors); // [ 'red', 'purple', 'blue' ]
console.log(removed); // [ 'green' ],只有一个元素的数组
2.2.4 查
- indexOf
- includes
- find(传入逻辑函数,返回使得逻辑函数为真的第一个元素)
- filter(传入逻辑函数,返回所有使得逻辑函数为真的数组)
let numbers = [1, 2, 3, 4, 5, 4];
console.log(numbers.indexOf(4)); // 3
console.log(numbers.includes(4)); // true
console.log(numbers.find((d, index) => d >= 5)); //5
console.log(numbers.filter((d, index) => d >= 5)); //[5];
以下函数接收一个函数作为参数,(element, index, array)=>{},均不会改变原数组,除非手动对数组进行修改(比如调用原数组或使用array参数修改)
- some:对数组每一项都运行函数,如果至少有1个元素返回 true ,则这个方法返回 true
- every:对数组每一项都运行函数,如果所有元素都返回 true ,则这个方法返回 true
- forEach:对数组每一项都运行传入的函数,没有返回值
- filter:对数组每一项都运行传入的函数,返回函数结果为 true 的项构成的数组
- map:对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组
let numbers = [1, 2, 3, 4, 5];
console.log(numbers.some((d, index, array) => d > 2)); // true
console.log(numbers.every((d, index, array) => d > 2)); // false
console.log(numbers.filter((d, index, array) => d > 2)); // [ 3, 4, 5 ]
console.log(numbers.map((d, index, array) => d ** 2)); // [ 1, 4, 9, 16, 25]
2.2 初始化和拷贝
> 判断一个数组是否为数组
Array.isArray(arr);
2.2.1 初始化
1. 一维数组的初始化
var arr = new Array(len).fill(0);
var arr = [];
2. 二维数组的初始化(很容易产生浅拷贝的问题),要生成一个二维数组:[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
//易犯错的浅拷贝:改一个值,每一行这个值都会变,这个bug让了一个小时
var arr = new Array(matrix.length).fill(new Array(matrix.length[0]).fill(0));错误
//正确写法:
var arr = Array.from({length:matrix.length}, x=>Array.from({length:matrix[0].length}, y=>0));
var arr = new Array(matrix.length).fill(0).map(() => new Array(matrix[0].length).fill(0));
2.2.2 拷贝
1. 单层(一维)数组拷贝
arr = [1, 2, 3];
arrCopy = [...arr];
numbersCopy = arr.map((x) => x);
2. 多层(多维)数组拷贝,很容易出现浅拷贝的问题,拷贝的是引用而不是赋值。
nestedNumbers = [[1], [2]];
numbersCopy = JSON.parse( JSON.stringify(nestedNumbers));
2.3 遍历
1. ES5——forEach()
const arr = ['a', 'b', 'c'];
arr.prop = 'property value';
arr.forEach((elem, index) => {
console.log(elem, index);
});
// Output:
// 'a', 0
// 'b', 1
// 'c', 2
2. ES6——for...of :for-of 不仅可以遍历数组,还可以遍历可迭代对象,例如遍历 Map
const arr = ['a', 'b', 'c'];
arr.prop = 'property value';
for (const elem of arr) {
console.log(elem);
}
//map
for (const [key, value] of myMap) {
console.log(key, value);
}
//数组方法.entries() 返回一个可迭代的[index,value]对
//keys()、values()类同
for (const [index, elem] of arr.entries()) {
console.log(index, elem);
}
2.4 排序
- reverse:反转
- sort:接受一个比较函数,用于判断哪个元素在前面(比如 a, b 比较,如果比较函数返回 -1 则表示a在b前面,返回1表示b在a前面,返回0表示不改变顺序)
arr.sort((x, y) => x-y);//升序排序
arr.sort((x, y) => y-x);//降序排序
//自定义排序
function cmp(x, y){
if(x < y) return -1;
eles if(x == y) return 0;
else if(x > y) return 1;
}
arr.sort(cmp)
2.5 返回数组里某个特定的值
2.5.1 返回数组的最小值和最大值
var min = Math.min(...arr);
var max = Math.max(...arr);
//因为Math.min()只接受两个参数,而这里的...为扩展运算符
//相当于依次把arr中的数值作为参数传入到Math.min()函数中
引申:返回数组对象中某个属性的最大值和最小值
Math.min.apply(Math, this.list.map(d => { return d.id }))
Math.max.apply(Math, this.list.map(d => { return d.id }))
2.5.2 返回数组的和
var sum = eval(arr.join('+'));
//eval()函数的作用:将普通字符串当做js代码解释执行
var sum = arr.reduce((a,b)=>(a+b));
//reduce()方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
2.6 数组高级操作
2.6.1 reduce
reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,回调函数接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。
arr.reduce(callback, [initialValue])
如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。
三、字符串
3.1 增删改查
js字符串一旦创建则不能改变,因此关于字符串操作方法,都是创建一个新的副本再操作。
3.1.1 增
- +操作符
- ${}模板操作符
- concat
const stringValue = ' hello world ';
console.log(stringValue + '2'); // ' hello world 2'
console.log(`${stringValue} 456`); // ' hello world 456'
console.log(stringValue.concat('456')); // ' hello world 456'
3.1.2 删
- slice(切片开始位置, [切片结束位置])、substr(切片开始位置,[切片结果字符串的长度])、substring(切片开始位置, [切片结束位置])
- slice和substring区别是:slice遇到负索引将从末尾开始切片,而substring遇到负索引将会将其转化为0然后再判断位置大小正向切片
- substr在es5中不推荐使用
var str = 'hello world';
console.log(str.slice(-3)); // rld
console.log(str.substring(-3));// hello world //substring(-3) => substring(0)
console.log(str.substr(-3));// rld
console.log(str.slice(3, -4)); // lo w
console.log(str.substring(3, -4)); // hel
//substring(3, -4) => substring(3, 0) =>substring(0, 3)
console.log(str.substr(3, -4));// ""(空字符串)
//substr(3, -4) => substr(3, 0)
3.1.3 改
- trim、trimLeft、trimRight:返回去除空格符后的字符串
- repeat:将原始字符串重复拼接然后返回新的字符串
- padStart、padEnd:对字符串进行填充直至符合指定长度
const stringValue = ' hello world ';
console.log(stringValue.trim());// 'hello world'
console.log('123'.repeat(2)); // '123123'
console.log('123'.padStart(10, '-')); // '-------123'
console.log('123'.padEnd(10, '-')); // '123-------'
3.1.3.1 ASCII(大小写)
//将字母转为ASCII码
"A".charCodeAt(); // 65
//将ASCII码转为对应字母
String.fromCharCode(97); // 'a'
str = str.toUpperCase(); //大写
str = str.toLowerCase(); //小写
3.1.4 查
- charAt、indexOf、startWith、endWith、includes
const stringValue = ' hello world ';
console.log(stringValue.trim());// 'hello world'
console.log('123'.repeat(2)); // '123123'
console.log('123'.padStart(10, '-')); // '-------123'
console.log('123'.padEnd(10, '-')); // '123-------'
3.1 字符串与数组的互相转换
1. 字符串转数组
var arr=s.split('');
2. 将数组以字符串的形式输出,默认使用","隔开。
//toString()方法不仅适用于数组,而且还可以用于其他类型的对象,它用于将对象的值转换为字符串。
var str = arr.toString();
var str = arr.join();
3.3 正则表达式
3.3.1 匹配方法
- match:接收字符串、正则表达式字符串或RegExp对象,返回匹配到的第一个元素构成的正则结果数组
- search:接收字符串、正则表达式字符串或RegExp对象,返回匹配到的第一个元素索引
- replace:接收两个参数,第一个为匹配的内容(可为字符串或正则表达式),第二个要替换的内容(可以使用函数),返回替换后的字符串 ,但该方法一次只能替换第一个匹配的,如果需要替换所有则需要使用全局模式的正则表达式
const text = 'cat, bat, sat, fat';
console.log(text.match(/.at/)); // [ 'cat', index: 0, input: 'cat, bat, sat, fat', groups: undefined ]
console.log(text.search(/.at/)); // 0
console.log(text.replace(/.at/, '-')); // "-, bat, sat, fat"
console.log(text.replace(/.at/g, '-')); // "-, -, -, -"
3.3.2 匹配符
- / /:用来包含一个正则表达式
- 特殊字符要用\转义
- .表示除了换行符之外的任何字符
- *用来表示匹配0个字符或无数个字符
- ^字符串开头
- $字符串结尾
1. 判断字符串是不是只包括大小写字母和数字
var a = "123abcA";
var re = /^[0-9a-zA-Z]*$/
console.log(re.test(a))
四、JS数据结构
4.1 Map
1. 大小:map.size;
2. 方法:
- set(key, val): 向Map中添加新元素
- get(key): 通过键值查找特定的数值并返回
- has(key): 判断Map对象中是否有Key所对应的值,有返回true,否则返回false
- delete(key): 通过键值从Map中移除对应的数据
- clear(): 将这个Map中的所有元素删除
3. 遍历方法:
- keys():返回键名的遍历器
- values():返回键值的遍历器
- entries():返回键值对的遍历器
- forEach():使用回调函数遍历每个成员
const map = new Map([['a', 1], ['b', 2]])
for (let key of map.keys()) {
console.log(key)
}// "a" // "b"
for (let value of map.values()) {
console.log(value)
}// 1 // 2
for (let item of map.entries()) {
console.log(item)
}// ["a", 1] // ["b", 2]
for (let [key, value] of map.entries()) {
console.log(key, value)
}// "a" 1 // "b" 2
// for...of...遍历map等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value)
}// "a" 1 // "b" 2
4. Map转化为二维数组后排序
var arr = Array.from(map);
arr.sort((a, b) => { return a[0]-b[0]; }) //对key进行从小到大排序
4.2 Set
1. 大小:map.size;
2. 方法:
- add(value):添加某个值,返回 Set 结构本身(可以链式调用)。
- delete(value):删除某个值,删除成功返回true,否则返回false。
- has(value):返回一个布尔值,表示该值是否为Set的成员。
- clear():清除所有成员,没有返回值。
3. 遍历方法:
- keys():返回键名的遍历器。
- values():返回键值的遍历器。
- entries():返回键值对的遍历器。
- forEach():使用回调函数遍历每个成员。
const set = new Set(['a', 'b', 'c'])
for (let item of set.keys()) {
console.log(item)
}// a // b // c
for (let item of set.values()) {
console.log(item)
}// a // b // c
for (let item of set.entries()) {
console.log(item)
} // ["a", "a"] // ["b", "b"] // ["c", "c"]
// 直接遍历set实例,等同于遍历set实例的values方法
for (let i of set) {
console.log(i)
} // a // b // c
set.forEach((value, key) => console.log(key + ' : ' + value))
// a: a // b: b // c: c
五、JS底层函数(像算法一样复杂的JS)
5.1 手写call(), apply(), bind()
5.2 手写repeat(), setInterval()
function repeat(func, times, wait){
return function(){
var arg = arguments;
var handle = function(i){
setTimeout(function(){
func.apply(this, arg);
}, wait*i);
}
for(let i = 0; i < times; i++){
handle(i);
}
}
}
var repeatFunc = repeat(console.log, 4, 3000);
repeatFunc("hellworld");
function mySetInterval(func, time){
function f(){
func();
setTimeout(function(){
func();
}, time)
}
setTimeout(f, time);
}
mySetInterval(() => {
console.log(new Date())
}, 1000)
六、无聊的小技巧
6.1 实时打印JS数据内容
由于I/O异步化,浏览器可能会认为需要把控制台的I/O延迟到后台,在这种情况下,等到浏览器控制台输出对象内容时,已经执行了其他内容,造成console.log()的内容不准确。
如果想要实时打印内容,最好将其内容作为字符串保留下来,通常使用JSON.stringify()
方法将一个 JavaScript 对象或值转换为 JSON 字符串,但JSON.stringify()
方法无法处理对象中自己引用自己的问题,比如说data = { 'myself':data }; 通过下面这个方法可以解决这个问题。
function cleanStringfy(object) {
if (object && typeof object === 'object') {
object = copyWithoutCircularReferences([object], object);
}
return JSON.stringify(object);
function copyWithoutCircularReferences(references, object) {
var cleanObject = {};
Object.keys(object).forEach(function(key) {
var value = object[key];
if (value && typeof value === 'object') {
if (references.indexOf(value) < 0) {
references.push(value);
cleanObject[key] = copyWithoutCircularReferences(references, value);
references.pop();
} else {
cleanObject[key] = '###_Circular_###';
}
} else if (typeof value !== 'function') {
cleanObject[key] = value;
}
});
return cleanObject;
}
}
6.2 不使用额外的空间交换两个数
//不利用中间变量交换两个值的方法
function swap1(x, y) {
x = x + y;
y = x - y; //将上一步的x+y代入x
x = x - y;
return [x, y];
}
function swap2(x, y) {
x = x ^ y;
y = x ^ y;
x = x ^ y; //在异或中相同为0,不同为1;原理就是a^b^a在各种情况下均=b
return [x, y];
}
function swap3(x, y){
x = x * y;
y = x / y;
x = x / y;
return [x, y];
}
七、ES6
7.1 Let和Const
var
、let
、const
三者区别可以 围绕下面五点展开:
- 变量提升:
var
变量存在变量提升,即变量可以在声明之前调用,值为undefined;而let
和const
不存在变量 提升,即它们所声明的变量一定要在声明后使用,否则报错。 - 暂时性死区:
var
不存在暂时性死区,let
和const
存在暂时性死区,只有等到声明变量的那一行代码出现,才可以获取和使用该变量 - 块级作用域:
var
不存在块级作用域;let
和const
存在块级作用域 - 重复声明:
var
允许重复声明变量;let
和const
在同一作用域不允许重复声明变量 - 修改声明的变量:
var
和let
可以;而const
声明一个只读的常量,值不能改变 - 使用:能用
const
的情况尽量使用const
,其他情况 下大多数使用let,避免使用var
7.2 数组新增拓展
- 扩展运算符
- 构造函数新增的方法
- 实例对象新增的方法
- 数组的空位:ES6明确将空位转为undefined
- 排序稳定性:ES6将自定义排序设置为稳定排序
7.3 对象新增拓展
- 属性的简写:ES6中,当对象键名与对应值名相等的时候,可以进行简写
- 属性名表达式:ES6允许字面量定义对象时,将表达式放在括号内
- super:this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象
- 扩展运算符的应用:在解构赋值中,未被读取的可遍历的属性,分配到指定的对象上面
- 属性的遍历
- 对象新增的方法:
- Object.assign()方法用于对象的合并,将源对象source的所有可枚举属性,复制到目标对象target;Object.assign()方法的第一个参数是目标对象,后面的参数都是源对象
- Object.setPrototypeOf方法用来设置一个对象的原型对象
- Object.getPrototypeOf用于读取一个对象的原型对象
7.4 函数新增扩展
- 参数:ES6允许为函数的参数设置默认值
- 属性:函数的length属性将返回没有指定默认值的参数个数;函数name属性将返回函数名
- 作用域:一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域
- 严格模式:只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错
- 箭头函数:
- 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
- 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误
- 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可用 rest 参数代替
- 不可以使用yield命令,因此箭头函数不能用作 Generator 函数
7.5 Promise
八、面试考点
8.1 数组扁平化(字节)
方法一:递归,用res保存结果
function flatten(a){
var len = a.length;
for (let i = 0; i < len; i++){
if (Array.isArray(a[i])){
flatten(a[i]);
}else{
res.push(a[i]);
}
}
}
方法二:使用reduce一直累加数组
function flatten(arr) {
return arr.reduce((result, item)=> {
return result.concat(Array.isArray(item) ? flatten(item) : item);
}, []);
}
8.2 数据去重
方法一:set
var a = [1, 2, 3, 4, 5, 3];
var set = new Set();
a.forEach(key=>{set.add(key)});
console.log(set);
//简化版
[...new Set(arr)]
方法二:reduce+includes
var a = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
var res = a.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]);
// [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]