- 按照自己的学习进度进行更新;
- 主要分为以下几个部分:(1)JS基础,(2)CSS,(3)数据处理,(4)场景应用,…
JS基础
1. 手写new操作符?
(1)new?
new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例
(2)在new的过程中干了什么?
基本四步走
- 新建一个空对象
obj
- 链接到原型:将
obj
的[[prototype]]
属性指向构造函数的原型,即obj.[[prototype]] = construc.prototype
(不懂的可以看图理解一下【图开源于b站大佬】)- 绑定this:把构造函数内部的this绑定到新建的
obj
上,执行构造函数【显然这里需要用call、apply、bind来更改this指向】- 返回新的对象
function create() {
// 1. 新建空对象
let obj = new Object();
let constructor = Array.prototype.shift.call(arguments); // 获取构造函数
// 2. 链接到原型
obj.__proto__ = constructor.prototype;
// 3. 绑定this
let result = constructor.apply(obj, arguments);
// 4. 返回新对象
return typeof result === 'object' ? result : obj;
}
(3)几个小疑问?
- arguments是什么?
arguments是一个对应于传递给函数的参数的类数组对象,且arguments对象只能在函数内使用 - [].shift.call()干了什么?
获取arguments的第一个参数,改变arguments的length
2. 手写instanceof
instanceof 可以用于正确判断对象的类型,其内部机制为通过判断对象的原型链中是否含有该类型的原型
prototype
所以不难想到instanceof的实现步骤:
- 获取类型的原型 && 获取对象的原型
- 循环判断对象的原型是否等于类型的原型,直到对象的原型为null【原型链最终为null,具体可以看下上面的图】
function isInstanceOf(left, right) {
let proto = Object.hasPrototypeOf(left), // 获取对象的原型
prototype = right.prototype; // 获取构造函数的prototype对象
// 判断构造函数的prototype对象是否在对象的原型链上
while (true) {
if (!proto) return false;
if (proto === prototype) return true;
proto = Object.getPrototypeOf(proto); // 一直循环
};
}
3. 实现深浅拷贝
关于深浅拷贝,引用来自:浅拷贝与深拷贝的区别
(1) 深拷贝
- JSON.stringify()
用于解决浅拷贝拷贝第一层的问题
其基本原理为:是利用JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象
let a = {
age:1,
job:{
first:'FE',
}
};
let b = JSON.parse(JSON.stringify(a));
a.jobs.first = 'native';
console.log(b.job.first) // => 'FE'
但是这个方法存在局限性:
- 会忽略undefined
- 会忽略symbol
- 不能序列化函数
- 不能解决循环引用的对象
- 函数库lodash的_.cloneDeep方法
let _ = require('lodash');
let obj1 = {
a: 1,
b: {
f: {
g: 1
},
},
c:[1,2,3]
};
let obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f) // => false
- MessageChannel_针对要拷贝的对象含有内置类型且不包含函数的情况
此方法是异步的,可以处理undefined和循环引用对象
function structuralClone(obj) {
return new Promise(resolve => {
const {
prot1, port2} = new MessageChannel();
prot2.onmessage = ev => resolve(ev.data);
prot1.postMessage(obj);
});
};
let obj = {
a:1,
b:{
c:b
}
};
(
async() => {
const clone = await structuralClone(obj);
}
)();
- 手写深拷贝
function deepCopy(object) {
if (!object || typeof object !== "object") return;
let newObject = Array.isArray(object) ? [] : {
};
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] =
typeof object[key] === "object" ? deepCopy(object[key]) : object[key];
}
}
return newObject;
}
(2) 浅拷贝
一个新的对象对原始对象的属性值进行精确地拷贝,如果拷贝的是基本数据类型,拷贝的就是基本数据类型的值,如果是引用数据类型,拷贝的就是内存地址。如果其中一个对象的引用内存地址发生改变,另一个对象也会发生变化
- Object.assign()_根据assign分发的含义通俗理解为把
sources
分发到target
中去
let a = {
age:1,
};
let b = Object.assign({
}, a);
a.age = 2;
console.log(b.age); // => 1
- 扩展运算符…
let a = {
age:1,
};
let b = {
...a};
a.age = 2;
console.log(b.age); // => 1
- 手写实现浅拷贝
// 浅拷贝的实现;
function shallowCopy(object) {
// 只拷贝对象
if (!object || typeof object !== "object") return;
// 根据 object 的类型判断是新建一个数组还是对象
let newObject = Array.isArray(object) ? [] : {
};
// 遍历 object,并且判断是 object 的属性才拷贝
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] = object[key];
}
}
return newObject;
}// 浅拷贝的实现;
function shallowCopy(object) {
// 只拷贝对象
if (!object || typeof object !== "object") return;
// 根据 object 的类型判断是新建一个数组还是对象
let newObject = Array.isArray(object) ? [] : {
};
// 遍历 object,并且判断是 object 的属性才拷贝
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] = object[key];
}
}
return newObject;
}// 浅拷贝的实现;
function shallowCopy(object) {
// 只拷贝对象
if (!object || typeof object !== "object") return;
// 根据 object 的类型判断是新建一个数组还是对象
let newObject = Array.isArray(object) ? [] : {
};
// 遍历 object,并且判断是 object 的属性才拷贝
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] = object[key];
}
}
return newObject;
}
4. 防抖 & 节流
- 防抖: 在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时
- 节流:规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效
- 防抖和节流都是为了防止函数多次调用,区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于wait,防抖的情况下只会调用一次,而节流的情况下会间隔一定的时间(wait)调用函数
(1)防抖
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./index.js", defer="defer"></script>
</head>
<body>
<h1>防抖</h1>
<input type="text" id="input">
</body>
</html>
index.js
const inputDOM = document.getElementById('input');
console.log(inputDOM);
// 用户输入完毕的时候,才发送一次HTTP请求
function debounce(fn, delay) {
let timer = null;
return function(event) {
console.log('e',event);
if (timer) {
clearTimeout(timer);
};
timer = setTimeout(() => {
fn.apply(this, arguments);
}, delay);
}
}
inputDOM.addEventListener('input', debounce(function(event) {
console.log('event', event);
console.log('发送搜索请求');
}, 500));
(2)节流
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./index.js", defer="defer"></script>
<style>
#box{
width: 200px;
height: 200px;
background-color:orange;
}
</style>
</head>
<body>
<h1></