各种手写
###手写new
######new操作符做了什么事情:
1.创建一个空对象 将来会返回这个对象
2.调用构造函数,并将构造函数的this指向新创建的对象newObj
3.把原型对象的方法给新创建的newObj,把newObj的__proto__指向构造函数的显式原型
4.判断构造函数的返回值,来决定new的返回值是(构造函数的返回值还是实例化对象)
<script>
//封装一个手写_new函数,当把构造函数传进去,就会模拟new 把它给实例化了
function _new (obj, ...rest){
// 创建一个新的对象并将这个对象的this指向obj的显示原型
const newObj = Object.create(obj.prototype);
// 将obj的this指向新创建的newObj对象并调用obj;并返回一个新的对象result;这个对象的参数为(rest)
const result = obj.apply(newObj,rest)
// 判断返回值:如果构造函数的返回值是一个对象则就返回这个对象,反之则返回这个新的对象(newObj)
return typeof result === 'object' ? result : newObj
}
//摸拟一下
// 声明一个构造函数
function fn(name,age){
this.name=name;
this.age=age
// 如果构造函数的返回值是对象则实例化之后就是这个对象
/* return my= {
data:'太难啦'
}*/
}
fn.prototype.sry=function(){
// 原型对象的this指向实列化对象
console.log(this.name)
}
// new实例化调用
const day1 = new fn('lily',22)
const day2 = _new(fn,'laowang',29)
console.log(day1)
console.log(day2)
// 通过实力化调用
day1.sry()
day2.sry()
</script>
手写insfanceof
理解instanceof:
A instanceof B: B 的原型对象(prototype)是否在A的原型链(proto)上;B是构造函数 A是实例对像
//声明一个函数将来调用这个函数的传入(A,B)就会返回true或者false
//true代表在原型链上 false代表不在
function instanceOf(A, B) {
//首先获取B的原型对象 然后再判断
var BPrototype = B.prototype;
while (A.__proto__) {
if (A.__proto__ === BPrototype) {
return true;
}
A = A.__proto__;
}
return false;
}
###手写promise
######promise的状态和值
1.promise不是异步代码,而是盛放异步代码的工具,promise是一个构造函数,需要实例化调用
2.Promise的两个参数是两个函数,resolve是成功函数,reject是失败函数,
3.当执行完成后,如果成功则调用执行resolve函数,如果失败则调用执行reject函数,这样Promise的返回值就会接到通知
4.如果Promise失败则返回的promise的状态就是rejected,如果Promise成功则返回的promise状态就是fulfilled/resolved
5.如果Promise正在执行,或者你没有调用成功失败函数,那么promise的状态将一直都是pending
then方法的返回值
1.then返回值是一个新的promise1对象, 他的状态看里面函数调用的返回值:
2.如果返回值是一个promise2对象,那么这个promise2的状态是什么,promise1的状态就是什么
3.如果返回值不是promise对象,或者没有返回值,那么promise1对象默认是成功状态
4.如果函数调用报错了,那么promise1对象就会是失败状态
######catch的返回值
1.本质就是借用then的第二个失败的回调函数,所以catch的返回值也是和then的返回值规则一致
function Mypromise(auto) {
// 这里得this指向Myprpmise的实列对象,所以要保存起来
const that = this
// 定义实列对象的默认状态为pending
that._status = 'pending'
// 定义实列对象的默认值为undefined
that._result = undefined
// 用来存储成功、失败回调函数的容器
that._vessel = {}
// 成功的函数resolve
function resolve(value) {
// 判断只能修改一次状态
if (that._status !== 'pending') return
//实列对象的状态和值
that._status = 'resolved'
that._result = value
// 异步调用that._vessel.onResolved函数,因为同步的话that._vessel容器里面还没用onResolved这个回调函数,
// 所以需要等待Mypromise调用成功函数resolve来触发显示原型上的then方法,then方法内会向that._vessel容器添加添加onResolved方法,
// 简而言之需要等待that._vessel容器内有onResolved方法
setTimeout(() => {
// 容器内有这个方法并且调用他
// that._vessel.onResolved && that._vessel.onResolved(value)
that._vessel.onResolved?.(value)
}, 0);
}
// 失败的函数reject
function reject(failure) {
if (that._status !== 'pending') return
that._status = 'reject'
that._result = failure
setTimeout(() => {
that._vessel.onRejected?.(failure)
}, 0);
}
// 下面的代表:
// 比如var p=new promise(function(resolve, reject){})
// 这个function(resolve, reject)就相当于auto(resolve, reject)
auto(resolve, reject)
}
// 在Mypromise的显示原型上添加then方法,该方法接收两个函数 onResolved, onRejected
Mypromise.prototype.then = function (onResolved, onRejected) {
// 原型上的this指向实例对象,先保存起来
const that = this;
/*由于catch 方法本质上是then方法的第二个回调函数,我们使用then的时候一般只写一个成功的回调函数,第二个回调函数由于不 写,所以下面就是解决不传then方法的第二个失败的的回调函数 */
onRejected = typeof onRejected === 'function' ? onRejected : (failure)=>{throw failure}
// // 由于catch 方法本质上是then方法的第二个回调函数,第一传递是unedfined 所以下面解决的是catch方法不传第一个参数
onResolved = typeof onResolved === 'function' ? onResolved : value =>value
// then方法返回的是一个新的promise对象,以此才能满足then方法的链式调用
// function(resolve, reject)这个就是构造函数Mypromise接收的参数
return new Mypromise(function (resolve, reject) {
/*下面是then方法的第一个回调函数:如果第一个回调函数中返回的是promise对象则then方法的第一个回调函数的成功与否就取决于这个promise对象*/
that._vessel.onResolved = function (value) {
try {
// 获取onResolved方法的返回值来判断是不是promise的实例化对象
const result = onResolved(value)
// 如果result是Mypromise的实例化对象则then的第一个回调函数的成功与否就看这个Mypromise实例化对象的状态
if (result instanceof Mypromise) {
//result就是Mypromise的实例化对象,实例化对象就会then方法,result调用then方法来返回当前的Mypromise的状态
result.then(resolve, reject)
} else {
// 如果then中的第一个回调函数的返回值不是prommise对象则默认成功
resolve(result)
}
} catch (e) {
// 如果then的第一个回调函数中出现错误啦则第一个回调函数的状态就失败
reject(e)
}
}
// 下面是then方法的第二个回调函数原理和第一个回调函数一样:
// 如果第二个回调函数中返回的是promise对象则then方法的第二个回调函数的成功与否就取决于这个promise对象
that._vessel.onRejected = function (failure) {
try {
const result = onRejected(failure)
if (result instanceof Mypromise) {
result.then(resolve, reject)
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
})
}
// 在Mypromise的显示原型上添加catch方法,该方法接收一个函数 onRejected:catch方法只考虑失败
Mypromise.prototype.catch = function (onRejected) {
//this指向实例化对象.实例化对象中肯定有then方法,借用then方法的第二个回调函数就可以啦
return this.then(undefined, onRejected);
}
节流
概念:给某个元素绑定啦事件的时候,比如进入某个元素中 点击 移动等 ,这个时候只要鼠标移动点击,绑定的事件会不断的被触发,这是非常耗费性能的,所以要给元素绑定的事件做限制,只能在规定的事件内触发一次事件(第一次),到啦规定的时间才会在触发一次
function move(e) {
console.log(Date.now());
console.log(this); //应该是指向oBox
console.log(e); //
}
const result = throttle(move, 5000);
//当box事件触发的时候 会走看门狗
oBox.onmousemove = result;
//高阶函数
//封装一个节流函数,参数:需要节流的功能函数 是每次功能函数调用所间隔的时间
function throttle(fn, time) {
//进来以后判断最新的时间和上一次访问的时间 间隔是否有200ms
//如果有 则执行move函数 否则直接return
//第一次进来没有上一次的时间,并且第一次一定是要执行的
//所以直接给第一个进来设置一个上一次的默认时间是0
let lastTime = 0;
return function () {
//获取当前进入的时间
let nowTime = Date.now();
//当前时间减去上一次时间要小于规定的时间的化就return
if (nowTime - lastTime < time) {
return;
}
//如果时间超出 则直接调用fn
//当前return的函数this指向是box 所以需要把fn的this指向当前的this
//当前的return的才是真正的事件函数,所以当前return的函数拥有event,但是fn没有
//需要把当前return的函数的参数 给到fn上, 要把事件对象给到 arguments[0] 上, 然后功能函数才会有event事件对象
fn.call(this, arguments[0]);
//把lastTime更新一下 方便下次判断
lastTime = nowTime;
};
}
防抖
概念:给某个元素绑定移动事件时,只要鼠标移动就不断触发,我们想要的是鼠标移动的最后一次才触发事件
function move(e) {
console.log(Date.now());
console.log(this); //应该是指向oBox
console.log(e); //
}
const result = debounce(move, 200);
//当我最后一次移动完成之后 200毫秒后再执行
oBox.onmousemove = result;
function debounce(fn, time) {
let timerID = null;
return function () {
//每次move一执行 就要重新触发 重新计时 所以需要清除计时器
clearTimeout(timerID);
const arg = arguments;
//重新设置时间
timerID = setTimeout(() => {
// 将事件对象给arg[0] 功能函数才会有event事件对象
fn.call(this, arg[0]);
}, time);
};
}
深拷贝
概念:复制对象中的对象
const obj = {
name: "xiaowang",
age: 18,
hobby: {
one: "喝酒",
two: "写代码",
},
score: [100, 90, 80],
do() {
console.log("eat");
},
}
// 封装一个检测数据类型的方法
function checkType(obj) {
return Object.prototype.toString.call(obj).slice(8, -1);
}
function deepClone(obj) {
let re;
//判断当前传进来的是对象的话则 re 就和传来的一样也是对象
if (checkType(obj) === "Object") {
re = {}
//如果是数组则 re 也是数组
} else if (checkType(obj) === "Array") {
re = [];
} else {
//如果都不是就return出去
return obj
}
//赋值给re
for (let i in obj) {
re[i] = deepClone(obj[i])
}
return re;
}
//测试
const newObj = deepClone(obj);
newObj.name = "zhang";
console.log(obj.name); //xiaowang
console.log(newObj.name);//zhang 说明拷贝的新的对象和旧的对象不是一个地址
快排
概念:数组快速小到大排序
function quickSort(arr) {
//确保数组的长度大于1
if (arr.length <= 1) {
return arr;
}
//根据数组的长度得出数字
const num = Math.floor(arr.length / 2);
//把原数组中的基准值删除掉,arr直接变成一个去除基准值的新数组,并且返回了一个删除元素的数组
const Value = arr.splice(num, 1)[0];
const left = [],
right = [];
//遍历原数组
arr.forEach((item, index) => {
if (item < Value) {
left.push(item);
} else {
right.push(item);
}
})
//递归并合并 ,concat方法将一维数字打散,将里面的值添加到数组尾部
return quickSort(left).concat(Value, quickSort(right))
}
//测试
const re = quickSort([1, 3, 5, 2, 11, 6, 4, 9, 7, 0]);
console.log(re);//[0, 1, 2, 3, 4, 5, 6, 7, 9, 11]