数据类型
简单数据类型(原始类型):Undefined、Null、Boolean、Number、String和Symbol
复杂数据类型:Object
深拷贝与浅拷贝
浅拷贝
Object.assign
Array.prototype.slice()
,Array.prototype.concat()
- 使用拓展运算符实现的复制
浅拷贝是创建一个新对象。然后将原始对象的非引用类型数据成员复制到新对象中。但是对于引用类型数据成员,新对象只是复制了原始对象中的引用,而不是创建一个新的对象。这意味着,如果原始对象和新对象中的引用类型数据成员指向同一个对象,那么对于这个对象的修改将会影响到原始对象和新对象。
我们说拷贝的深浅是区别引用类型的。
实现一个浅拷贝:
function shallowClone(obj){
const newObj={};
for(let prop in obj){
if(obj.hasOwnProperty(prop)){//是自有属性
newObj[prop]=obj[prop];
}
}
return newObj;
}
深拷贝
深拷贝是创建一个新对象,然后将原始对象的所有数据成员,包括引用类型数据成员中的数据,全部复制到新对象中。这意味着,即使原始对象和新对象中的引用类型数据成员指向同一个对象,对于这个对象的修改也不会影响到原始对象和新对象。
_.cloneDeep()
jQuery.extend()
JSON.stringify()
- 手写循环递归
//hash默认值类型WeakMap,防止出现循环引用导致死循环
function deepClone(obj, hash = new WeakMap()) {
//如果是null或者undefined就不进行拷贝操作
if (obj === null||obj===undefined) return obj;
//如果是日期和正则表达式,返回新构造的Date和RegExp实例
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
//基本类型和函数的话不需要深拷贝
if (typeof obj !== "object") return obj;
//已拷贝过,直接返回拷贝值
if(hash.get(obj)) return hash.get(obj);
//找到的是所属类原型上的constructor,
//而原型上的 constructor指向的是当前类本身
let cloneObj = new obj.constructor();
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
//实现一个递归拷贝
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
循环引用导致的死循环
循环引用是指两个对象相互引用,导致内存泄漏和死循环问题。
考虑以下代码:
const obj1={prop:null};
const obj2={prop:obj1};
obj1.prop=obj2;
使用简单递归算法实现深拷贝obj1就会出现死循环:
function deepClone(obj){
if(obj === null||typeof obj!=='object') return obj;
const cloneObj = new obj.constructor();
for(let key in obj){
if(obj.hasOwnProperty(obj)){
cloneObj[key] = deepClone(obj[key]);
}
}
return cloneObj;
}
const obj3 = deepClone(obj1); //死循环,卡在obj1.prop=obj2上
防抖和节流
防抖指 规定n秒后才执行的事件提前触发,则重新计时。
实现防抖:
节流指 事件n秒内只执行一次,若n秒内重复触发,只有一次生效。
实现节流:
ES6新特性
- const和let,出现块级作用域;
- 模板字符串,使用反引号(``)和变量或表达式界定符(
${}
); - 箭头函数(
()=>{}
); - 参数默认值(
function fn(param = 'defaultValue'){}
); - 扩展操作符(
...
); - 二进制(
0b
或0B
前缀)和八进制(0o
或0O
前缀)字面量; - 对象和数组结构;
- 对象超类(super);
for of
和for in
;- 类(class);
- Promise;
- Symbol;
- Set和Map;
- 模块化 export和import;
- 迭代器(Iterator);
- 生成器(Generator);
数组方法
数组方法分为操作、转换、排序和迭代。
操作方法
分为增、删、查、改来记忆。
一、增
- splice(<startPos>,0,arg1,arg2,...),返回空数组;
- push(),添加到数组末尾,返回数组新长度;
- unshift(),添加到数组开头,返回数组新长度;
- concat(),不影响原始数组,返回新数组。
二、删
- splice(<startPos>,<delNums>),返回被删除的项组成的数组;
- slice(<startPos>,<endPos>),返回[startPos,endPos)下标区间的元素组成的数组,不影响元素组;
- pop(),删除数组最后一项,返回删除元素;
- shift(),删除数组开头,返回删除元素。
三、查
- indexOf(),返回查找项的坐标,没有就返回1;
- includes(),查询是否包含查找项,有就返回true,否则返回false;
- find(),返回第一个匹配的元素。
四、改
例如:splice(1,1,'modifiedValue')
,原数组受影响
转换方法
- join,接受一个参数,即字符分隔符
const arr = [1,2,3]
console.log(arr.join('-'))//1-2-3
排序方法
- sort和reverse。
const arr = [3,2,4]
function cmp(v1,v2){
if(v1<v2) return -1;
else if(v1>v2) return 1;
else return 0;
}
arr.sort(cmp);//直接影响arr
console.log(arr);
迭代方法
- some()
- every()
- forEach()
- filter()
- map()
字符串方法
concat()
slice()
substr()
subString()
trim()、trimLeft()、trimRight()
padStart()、padEnd()
toLowerCase()、toUpperCase()
正则表达式RegExp
语法:/pattern/flags
pattern(模式)包括字符类、限定符、分组、向前查找和反向引用。
flags用于控制正则表达式的行为,有如下标记:
g
:全局模式,查找全部内容,而不是只查找第一个匹配的;i
:不区分大小写,查找匹配时忽略pattern和字符串的大小写;m
:多行模式,查找到一行末尾继续查找;y
:粘附模式,只查找从lastIndex开始及之后的字符串;u
:Unicode模式,启用Unicode匹配;s
:dotAll模式,表示元字符.
匹配任何字符(包括换行符\n
和\r
)。
特殊字符:
*
的意思是前一项出现零次或者多次;+
意思是前一项至少出现1次;^
限定开头,如果出现在字符集合模式时,表示不取;$
限定结尾;?
前一项出现0或1次;.
默认匹配除换行符之外的任何字符;x|y
匹配x或y;()
,可单独匹配;{n}
表示前面一项出现n次;{n,}
表示前面一项至少出现n次;{n,m}
表示前面一项出现n到m次;[]
,字符集合,用破折号(-)来指定一个字符范围,字符集中*
和.
没有特殊含义;[^]
,反字符集合,意思是不包括该字符集;\d
匹配一个数字,等价于[0-9]
;D
匹配一个非数字字符,等价于[^0-9]
;\s
匹配一个空白字符;\S
匹配一个非空白字符;\w
匹配一个单字字符(字母、数字或下划线);\W
匹配一个非单词字符。
使用正则表达式
exec
,一个在字符串中执行查找匹配的 RegExp 方法,它返回一个数组(未匹配到则返回 null),RegExp方法;test
,返回true或false,RegExp方法;match
,string方法
DATE类型的API
话都在代码里了
let start = new Date();
let end = new Date(2024,1,1,11,20);
console.log('start: '+start);
console.log('start.toString(): '+start.toString());
console.log('start.toLocaleString(): '+start.toLocaleString());
console.log('start.valueOf(): '+start.valueOf());
console.log("start.toDateString(): " + start.toDateString());
console.log("start.toTimeString(): " + start.toTimeString());
console.log("start.toLocaleDateString(): " + start.toLocaleDateString());
console.log("start.toLocaleTimeString(): " + start.toLocaleTimeString());
console.log(`end-start: ${end-start}`);
console.log("start.getTime(): " + start.getTime());
console.log("几年 start.getFullYear(): " + start.getFullYear());
console.log(`几月 start.getMonth(): ${parseInt(start.getMonth())+1}`);
console.log("几日 start.getDate(): " + start.getDate());
console.log("星期几 start.getDay(): " + start.getDay());//周日为0,周一为1
console.log("几点 start.getHours(): " + start.getHours());
console.log("几分 start.getMinutes(): " + start.getMinutes());
console.log("几秒 start.getSeconds(): " + start.getSeconds());
console.log("几毫秒 start.getMilliseconds(): " + start.getMilliseconds());
输出:
start: Fri Mar 10 2023 13:16:14 GMT+0800 (中国标准时间)
start.toString(): Fri Mar 10 2023 13:16:14 GMT+0800 (中国标准时间)
start.toLocaleString(): 2023/3/10 13:16:14
start.valueOf(): 1678425374941
start.toDateString(): Fri Mar 10 2023
start.toTimeString(): 13:16:14 GMT+0800 (中国标准时间)
start.toLocaleDateString(): 2023/3/10
start.toLocaleTimeString(): 13:16:14
end-start: 28332225059
start.getTime(): 1678425374941
几年 start.getFullYear(): 2023
几月 start.getMonth(): 3
几日 start.getDate(): 10
星期几 start.getDay(): 5
几点 start.getHours(): 13
几分 start.getMinutes(): 16
几秒 start.getSeconds(): 14
几毫秒 start.getMilliseconds(): 941
==
和===
一、相等操作符==
- 两个都为简单类型,字符串和布尔值都会转换成数值,再比较
- 简单类型与引用类型比较,对象转化成其原始类型的值,再比较
- 两个都为引用类型,则比较它们是否指向同一个对象
- null 和 undefined 相等
- 存在 NaN 则返回 false
二、全等操作符===
不进行类型转换
DOM操作
DOM(文档对象模型,Document Object Model),是HTML和XML文档的编程接口。
每个HTML文档都可以表示为一个层级的树形结构。
document
节点表示每个文档的根节点,根节点的唯一子节点是<html>
,称之为文档元素。
节点属性和方法
基本属性:nodeName、nodeValue、nodeType。
父子节点关系图:
childNodes属性是包含一个NodeList的实例,NodeList是实时的活动对象,而不是第一次访问得到内容的快照。
appendChild(node) //添加到childNodes末尾,返回添加的节点
removeChild(node) //返回被移除节点
replaceChild(new,old) //返回替换的节点
insertBefore(new,old) //返回插入的节点
cloneNode(flag) //flag决定是否深拷贝节点
原型和原型链
每个 JavaScript 对象都有一个原型对象。原型对象可以看做是对象的一个模板,包含了共享的属性和方法。
原型链(prototype chain)是指通过对象的原型对象来查找属性或方法的过程,即如果访问一个对象的属性或方法时,在该对象本身找不到,就会沿着原型链一直向上查找,直到找到该属性或方法或者到达原型链的顶部,即 Object.prototype。
作用域和作用域链
作用域决定了代码区中变量和其他资源的可见性。
分为全局作用域、函数作用域、块级作用域。
词法作用域,又叫静态作用域,变量被创建时就确定好了,而非执行阶段确定的。
当在Javascript
中使用一个变量的时候,首先Javascript
引擎会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到找到该变量或是已经到了全局作用域。
可视区域判断
SVG用途
闭包
事务
JSON操作
判断数据类型
一、typeof
console.log("typeof null: "+ typeof null);
console.log("typeof undefined: " + typeof undefined);
console.log("typeof 1: " + typeof 1);
console.log("typeof 'str': " + typeof "str");
console.log("typeof true: " + typeof true);
console.log("typeof []: " + typeof []);
console.log("typeof {}: " + typeof {});
console.log("typeof function () {}: " + typeof function () {});
输出:
typeof null: object
typeof undefined: undefined
typeof 1: number
typeof 'str': string
typeof true: boolean
typeof []: object
typeof {}: object
typeof function () {}: function
结论:typeof不能判断对象,除了函数!
二、instanceof
instanceof能精准判断对象类型,但不能判断基本类型。
console.log(null instanceof Object);//false
console.log(function(){} instanceof Function);//true
console.log(new Set() instanceof Set);//true
console.log(1 instanceof Number);//false
三、constructor
所有对象(使用
Object.create(null)
创建的对象除外)都将具有constructor
属性。在没有显式使用构造函数的情况下,创建的对象(例如对象和数组文本)将具有constructor
属性,这个属性指向该对象的基本对象构造函数类型。
---MDN
console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true
四、Object.prototype.toString.call()
生成器
Generator控制函数的启停。
function *foo(x) {
let y = 2 * (yield (x + 1))
let z = yield (y / 3)
return (x + y + z)
}
let it = foo(5)
console.log(it.next()) // => {value: 6, done: false}
console.log(it.next(12)) // => {value: 8, done: false}
console.log(it.next(13)) // => {value: 42, done: true}
graph
调用Generator函数返回一个迭代器 -->
第一次调用next,忽略传参,返回表达式结果6-->
第二次调用next,传参12作为上一个yield返回结果,故y=24,返回8 -->
第三次调用next,传参13作为上一个yield返回结果,故z=13,返回42
可以通过Generator函数解决回调地狱。
字符串的排列组合
不重复字符串的组合
function getCombination(str){
if(str.length === 1){
return [str]
}
// 不包含首字母的各种切片
let res1 = arguments.callee(str.slice(1));
// 加上首字母的切片
let res2 = res1.map(x => str[0]+x);
// 只有首字母
let res3 = [str[0]];
// 合并
return res3.concat(res1,res2);
}
console.log(getCombination("abc"));
不重复字符串的排列
function getPerputation(str){
if(str.length===1) return [str];
let res=[];
// 包含除去第一个字符的各种排列
let arr=arguments.callee(str.slice(1));
for(let i=0;i<arr.length;i++){
// 记录新排列
let partArr=[];
for(let j=0;j<arr[i].length+1;j++){
// 切片与前一个字母的排列
let newStr=arr[i].slice(0,j)+str[0]+arr[i].slice(j);
// 存储新排列
partArr.push(newStr);
}
// 将新排列数组合并到res
res=res.concat(partArr);
}
return res;
}
console.log(getPerputation('abc'));
不重复字符串的排列组合
function getComAndPer(str){
let comArr=getCombination(str);
let res=[];
for(let i of comArr){
let perArr=getPerputation(i);
res=res.concat(perArr);
}
return res;
}
console.log(getComAndPer('abc'));
解析URL的参数成对象格式
题目:/myurl?key0=&key1=val1&key2[]=0&key2[]=1#page1
解析返回 {“key0”:””, “key1”:”val1”, ”key2”:[“0”, “1”] }
的Object对象。
function parseURL(urlStr) {
//去掉非查询参数部分
let paramStr = urlStr.split("?")[1].split("#")[0];
let urlObj = {};
paramStr.split("&").forEach((item, index, arr) => {
//匹配方括号
if (item.match(/(\[\])/g)) {
let keyValue = item.split("[]=");
if (urlObj.hasOwnProperty(keyValue[0])) {
urlObj[keyValue[0]].push(keyValue[1]);
} else {
urlObj[keyValue[0]] = [keyValue[1]];
}
} else {
urlObj[item.split("=")[0]] = item.split("=")[1];
}
});
return urlObj;
}
console.log(parseURL("/myurl?key0=&key1=val1&key2[]=0&key2[]=1#page1"));