深拷贝
递归
function deepClone(obj) {
let target = Array.isArray(obj) ? [] : {};
for (i in obj) {
target[i] = typeof obj[i] === "object"?deepClone(obj[i]):obj[i]
}
return target;
}
其他方式
JSON.parse(JSON.stringify(obj));
//对象中包含 function 或 RegExp 这些就不能用这种方法了
Object.assign({}, obj);
//对象一级属性是深拷贝,二级属性以后为浅拷贝
$.extend(true, {}, obj1);
//jQuery的extend方法实现深拷贝
数组去重
indexOf
function uniqueByIndexOf(arr) {
let newArr = [];
for (let i in arr) {
if (newArr.indexOf(arr[i] == -1)) {
newArr.push(arr[i]);
}
}
return newArr;
}
对象属性
function uniqueByObjProp(arr) {
let obj = {},
newArr = [];
for (i in arr) {
if (!obj[i]) {
obj[i] = true;
newArr = obj[i];
}
}
}
filter
function uniqueByfilter(arr) {
let newArr = [];
return arr.fitler((item) => {
!newArr.includes(item) && newArr.push(arr[i]);
});
}
reduce
function uniqueByReduce(arr) {
return arr.reduce((pre, next) => {
if (!pre.includes(next)) {
return pre.concat(next);
} else {
return pre;
}
}, []);
}
Es6
Array.from(new Set(arr))
[...new Set(arr)]
对象数组去重
function uniqueByObjKey(arr) {
let obj = {};
return arr.reduce((pre, next) => {
obj[next.id] ? "" : (obj[next.id] = true && pre.push(next));
}, []);
}
数组平铺
ES6
arr.flat();
递归
function flat(arr) {
let newArr = [];
for (i in arr) {
if (Array.isArray(i)) {
newArr = newArr.concat(flat(i));
} else {
newArr.push(arr[i]);
}
}
return newArr;
}
其他方式
arr.join(",").split(",");
节流、防抖
节流函数
应用场景:
- 滚动事件
- 防止重复提交
function throttle(handler, wait) {
let preTime = 0;
return function () {
let now = Date.now();
if (now - preTime >= wait) {
handler.call(this);
preTime = now;
}
};
}
防抖函数
应用场景:baidu搜索
function debounce(handler, wait) {
let timer = null;
return function () {
let _this = this;
if(timer){
clearTimeout(timer);
}
timer = setTimeout(
handler.call(_this),wait
)
};
}
call、apply、bind实现
call
思路: 将目标函数的this指向传入的第一个对象(把目标函数作为对象的方法执行,this就指向了目标对象),参数为不定长,且立即执行
- 改变this指向:可以将目标函数作为这个对象的属性
- 执行函数,注意arguments参数
- 删除为了改变this的函数
Function.prototype.call = function(context){
let ctx = Object(context)||windows
//给目标函数起一个函数名,作为传入对象的方法
let fn = 'fn'
while(ctx.hasOwnProperty(fn)){
fn = fn+Math.random()
}
//把目标函数(this)作为传入对象的方法
ctx[fn] = this
//执行函数,第一个参数是目标对象,后面的是执行传入的参数
let res = ''
if(arguments.length>1){
res=ctx[fn](...[...arguments].slice(1))
}else{
res= ctx[fn]()
}
//删除目标对象添加的属性
delete ctx[fn];
return res
}
apply
原理同call,只是传参方式不同
Function.prototype.apply = function(context,pa){
let ctx = Object(context)||windows
//给目标函数起一个函数名,作为传入对象的方法
let fn = 'fn'
while(ctx.hasOwnProperty(fn)){
fn = fn+Math.random()
}
//把目标函数(this)作为传入对象的方法
ctx[fn] = this
//执行函数,第一个参数是目标对象,后面的是执行传入的参数
let res = ''
if(arguments.length>1){
res= ctx[fn](...pa)
}else{
res= ctx[fn]()
}
//删除目标对象添加的属性
delete ctx[fn];
return res
}
bind
改变this指向,但是不立即执行,借用apply返回一个方法
Function.prototype.bind = function(context){
context = context ? Object(context) : window;
let _this = this
//保存argumets
let mArguments = arguments
if(arguments.length>1){
return function (){
_this.apply(context,[...mArguments.slice(1)])
}
}else{
return function (){
_this.apply(context,)
}
}
}
object.create
Object.create = function (obj) {
//创建空构造函数F
function F() {}
//F的prottotype指向obj
F.prtotype = obj;
//返回F实例
return new F();
};
new操作符干了什么,实现new
在JavaScript构造函数中:如果return值类型,那么对构造函数没有影响,实例化对象返回空对象;如果return引用类型(数组,函数,对象),那么实例化对象就会返回该引用类型;
function new() {
//创建空对象
let obj = {};
//获取构造函数
const [Constructor,...args] = [...arguments]
//绑定原型关系
obj.__proto__ = Constructor.prototype;
// 执行构造函数,即绑定 this,并且为这个新对象添加属性
let result = Constructor.apply(newObj, args);
if (result && (typeof result === 'object' || typeof result === 'function')) {
return result
}
//返回新对象
return obj;
}
继承
ES5
function Parent (name, age) {
this.name = name
this.age = age
}
Parent.ptototype.sayName = function () {
console.log(this.name)
}
function Child (city) {
Parent.call(this)
this.city = city
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
ES6
class App extends React.Component {
constructor (props) {
super(props)
}
}
export default App
Promise
promise
//判断变量是否是函数
function isFunction(variable) {
return typeof variable === "function";
}
//promise构造函数
function MyPromise(handle) {
if (!isFunction(handle)) {
throw new Error("必须传入一个函数");
}
this.status = "pending"; //状态,默认等待状态
this.value = ""; //值
this.onResolveCallbacks = []; //存放成功回调的数组
this.onRejectCallbacks = []; //存放失败回调的数组
var that = this;
try {
handle(resolve, reject);
} catch (err) {
reject(err); //有报错,状态直接变为rejected
}
function resolve(val) {
if (that.status == "pending") {
that.status = "resolved";
that.value = val;
that.onResolveCallbacks.forEach((fn) => {
fn();
});
}
}
function reject(err) {
if (that.status == "pending") {
that.status = "rejected";
that.value = err;
that.onRejectCallbacks.forEach((fn) => {
fn();
});
}
}
}
promise.then
MyPromise.prototype.then = function (sucFun, faildFun) {
//可选参数
//如果没有传,只负责把值往后传
sucFun = isFunction(sucFun) ? sucFun : (value) => value;
faildFun = isFunction(faildFun) ? faildFun : (value) => value;
var that = this;
//先改变了状态
if (this.status === "resolved") {
sucFun(this.value);
}
if (this.status === "rejected") {
faildFun(this.value);
}
//先指定了回调函数,状态还没改变
if (this.status === "pending") {
//存住回调函数,在异步操作完成时调用
this.onResolveCallbacks.push(() => {
sucFun(this.value);
});
this.onRejectCallbacks.push(() => {
faildFun(this.value);
});
}
return this;
};
promise.all
Promise.allFun = (arr) => {
return new Promise((resolve, reject) => {
var resolveArr = [],
rejectArr = [];
for (var i = 0; i < arr.length; i++) {
Promise.resolve(arr[i]).then((res) => {
resolveArr.push(res);
if (resolveArr.length == arr.length) {
resolve(resolveArr);
}
})
.catch((err) => {
rejectArr.push(err);
reject(rejectArr);
});
}
});
};
promise.race
myPromise.race = function (arr) {
return new Promise(function (resolve, reject) {
for (var i in arr) {
arr[i].then(resolve, reject);
}
});
};
异步并发控制
async function sendRequest(list, max = 4) {
return new Promise((resolve, reject) => {
let index = 0; //
let count = 0; //上传成功个数
const start = () => {
while (max > 0 && index < list.length) {
//剩余位置-1
max--;
let chunk = list[index];
index++;
request().then((res) => {
max++; //释放暂用位置
count++; //上传成功数量+1
if (count == list.length) {
resolve("完成了");
} else {
start();
}
});
}
};
start();
});
}
function request() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1);
resolve(1);
}, 2000);
});
}
var list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1];
sendRequest(list, 4)
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log("上传失败了");
});
数组方法
push
从后面增加n个元素,返回长度
Array.prototype.push = function(){
for(i in arguments){
this[this.length-1] = arguments[i]
}
return this.length
}
pop
从后面删除一个元素,返回删除项
Array.prototype.pop = function(){
let item
if(this.length>0){
item = this[this.length-1]
}
this.length = this.length-1
return item
}
shift
从前面删除一个,返回删除元素
Array.prototype.shift = function(){
let item
if(this.length>0){
item = this[0]
for(i in this){
this[i] = this[i+1]
}
this.length = this.length-1
}
return item
}
unshift
从前面添加n个元素,返回数组长度
Array.prototype.unshift = function(){
let length = arguments.length
if(this.length>0){
//整体后移
this[i+length] = this[i]
}
//赋值前面的空位
for(let i =0;i<length;i++){
this[i] = arguments[i]
}
return this.length
}
filter
Array.prototype.filter = function(handler){
let newArr = []
for(i in this){
if(handler(i,this[i]),this){
newArr[newArr.length] = this[i]
}
return newArr
}
reduce
Array.prototype.myReduce = function (callback, init) {
if (!init) init = 0;
for (var i = this.length - 1; i >= 0; i--) {
init = callback(init, this[i], i, this);
}
return init;
};
setTimeout模拟定时器
function mInterval() {
//要执行的逻辑
console.log(1);
//每次逻辑执行完,才会向队列添加事件
setTimeout(mInterval, 1000);
}
mInterval();