花了一天时间手写了前端必须要知道的api,供大家参考。
new、instenceof
<script>
function myNew(fn){
let obj={}
obj.__proto__=fn.prototype;
let result=fn.call(obj)
return result instanceof Object?result:obj;
}
function myinstenceof(obj1,obj2){
while(obj1.__proto__){
obj1=obj1.__proto__;
if(obj1===obj2.prototype){
return true;
}
}
return false;
}
function Person(){
this.name=2;
this.age=2;
// return {name:'hh'}
}
function Man(){
}
function Dog(){
}
Man.prototype=new Person()
let p=myNew(Person)
let result=myinstenceof(new Man(),Person)
let result2=myinstenceof(new Dog(),Person)
</script>
call、apply、bind
<script>
function add(){
console.log(arguments)
return 'aaa'
}
Function.prototype.myCall=function (obj,...args){
if(typeof this!=='function'){
throw new Error('not function!')
}
obj.fn=this
let result=obj.fn(...args);
delete obj.fn
return result
}
Function.prototype.myApply=function(obj){
if(typeof this!=='function'){
throw new Error('not function!')
}
let result;
if(arguments[1]){
result=this.call(obj,...arguments[1])
}else{
result=this.call(obj)
}
return result
}
Function.prototype.myBind=function(obj,...args){
if(typeof this!=='function'){
throw new Error('not function!')
}
let fn=this
let tempfn=function(){}
let rfn=function(){
//说明这个函数当做构造函数来new了
if(typeof this==='object' && this!==window){
fn.apply(this,args.concat(Array.from(arguments)))
}else{
fn.apply(obj,args.concat(Array.from(arguments)))
}
}
tempfn.prototype=fn.prototype;
rfn.prototype=new tempfn()
return rfn;
}
let obj={name:'小红'}
let fn2=function(...args){
console.log(this)
console.log(Object.prototype.toString.call(args))
}
fn2.prototype.sub=function(...args){
console.log(Object.prototype.toString.call(args))
}
add.myApply(obj,['a','b','c'])
// let testfn3=fn2.myBind(obj,'a','b')
// testfn3('e','f')
</script>
</body>
深拷贝(递归)
<script>
let obj={name:'aaa'}
let obj1={name:'a',o:{name:'xx',age:12},arr:[{name:'b'},2],d:obj}
//循环引用测试
// obj1.d.q=obj;
function isArrayorObject(ao){
return Object.prototype.toString.call(ao)==='[object Array]' || Object.prototype.toString.call(ao)==='[object Object]'
}
function DeepCopy(obj1,map=new Map()){
map.set(obj1)
let obj=isArrayorObject(obj1)?(Array.isArray[obj1]?[]:{}):''
for(let i in obj1){
if(isArrayorObject(obj1[i])){
if(map.has(obj1[i])){
throw new Error('the value of attribute cannot be a parent')
}
obj[i]=DeepCopy(obj1[i],map)
}else{
obj[i]=obj1[i]
}
}
//回溯之后,删除map中的obj1,不影响其它栈中的判断
map.delete(obj1)
return obj
}
let obj2=DeepCopy(obj1)
</script>
排序算法
<script>
let arr=[1,3,5,9,2,10,4];
//冒泡排序
//思想:每次排序把大的数往后排
function BubbleSort(arr){
//是否存在交换的值
let has=false;
let temp=0;
for(let i=0;i<arr.length-1;i++){
for(let j=0;j<arr.length-i-1;j++){
if(arr[j]>arr[j+1]){
temp=arr[j]
arr[j]=arr[j+1]
arr[j+1]=temp
has=true;
}
}
if(!has){
break;
}else{
has=false;
}
}
}
//选择排序
//思想:每次排序把后面最小的数放到前面
function SelectSort(arr){
let mindex;
let min;
for(let i=0;i<arr.length-1;i++){
mindex=i;
min=arr[i];
for(let j=i+1;j<arr.length;j++){
if(arr[j]<min){
mindex=j;
min=arr[j]
}
}
if(mindex!==i){
arr[mindex]=arr[i];
arr[i]=min;
}
}
}
//插入排序
//思想:每次排序,把前面当成有序的,后面当成无序的
function InsertSort(arr){
let insertIndex;
let Insert;
for(let i=0;i<arr.length;i++){
insertIndex=i;
Insert=arr[i]
while(insertIndex>=1&&arr[insertIndex-1]>Insert){
arr[insertIndex]=arr[insertIndex-1]
insertIndex--
}
if(insertIndex!==i){
arr[insertIndex]=Insert;
}
}
}
//希尔排序
//希尔排序,采用增量法,一开始就可以把后面较小的数往前排
function ShellSort(arr){
let insertIndex;
let Insert;
for(let gap=Math.floor(arr.length/2);gap>0;gap=Math.floor(gap/2)){
for(let i=gap;i<arr.length;i++){
insertIndex=i;
Insert=arr[i];
while((insertIndex-gap)>=0&&(arr[insertIndex-gap]>Insert))
{
arr[insertIndex]=arr[insertIndex-gap]
insertIndex-=gap;
}
if(insertIndex!==i){
arr[insertIndex]=Insert;
}
}
}
}
//快速排序
//把小的数放到基准数左边,把大的数放到基准数的右边
function QuickSort(arr,left,right){
if(left>right){
return;
}
let i=left;
let j=right;
let key=arr[left]
let temp=null;
while(i<j){
while(arr[j]>=key&&i<j){
j--;
}
while(arr[i]<=key&&i<j){
i++;
}
if(i<j){
temp=arr[i]
arr[i]=arr[j]
arr[j]=temp
}
}
arr[left]=arr[i]
arr[i]=key;
QuickSort(arr,left,i-1)
QuickSort(arr,i+1,right)
}
// BubbleSort(arr)
// SelectSort(arr)
// InsertSort(arr)
// ShellSort(arr)
QuickSort(arr,0,arr.length-1)
</script>
防抖和节流
<body>
<input type="text" name="" id="">
<script>
function debouce(fn,delay){
let timer;
return function(){
if(timer) clearTimeout(timer)
timer=setTimeout(()=>{
fn()
},delay)
}
}
function throwttle(fn,wait){
let date=(new Date()).getSeconds()
return function(){
if((new Date()).getSeconds()-date>wait){
fn()
date=(new Date()).getSeconds()
}
}
}
let fn=debouce(()=>{
console.log('防抖了。。。')
},3000)
let fn2=throwttle(()=>{
console.log('节流了。。。')
},3)
document.querySelector('input').addEventListener('input',function(){
// fn()
fn2()
})
</script>
</body>
打印杨辉三角
<!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>
<style>
*{
margin:0;
padding:0;
}
span{
width:77px;
text-align: center;
display: inline-block;
font-size: 30px;
}
</style>
</head>
<body>
<script>
function createTriangle(n){
let arr=[]
arr.length=n;
for(let i=0;i<n;i++){
arr[i]=[]
arr[i].length=i+1;
}
for(let i=0;i<n;i++){
arr[i][0]=arr[i][arr[i].length-1]=1
}
if(n>=3){
for(let i=2;i<n;i++){
for(let j=1;j<arr[i].length-1;j++){
arr[i][j]=arr[i-1][j]+arr[i-1][j-1]
}
}
}
//打印杨辉三角
for(let i=0;i<arr.length;i++){
for(let m=0;m<n-i;m++){
document.write(' ')
}
for(let j=0;j<=i;j++){
document.write(`<span>${arr[i][j]}</span>`)
}
document.write('<br/>')
}
}
createTriangle(15)
</script>
</body>
</html>
Promise、Promise.all、Promise.race
<script>
// function myPromise(fn){
// let sucess=[]
// let fail=[]
// let status='pedding'
// let result=null;
// resolve=function(value){
// status='fulfilled'
// result=value
// sucess.forEach(v=>v(value))
// }
// reject=function(value){
// status='rejected'
// result=value
// fail.forEach(v=>v(value))
// }
// try{
// fn(resolve,reject)
// }catch{
// reject('err')
// }
// this.then=function(fn1,fn2){
// if(status==='fulfilled'){
// fn1(result)
// }
// if(status==='rejected'){
// fn2(result)
// }
// if(status==='pedding'){
// sucess.push(v=>fn1(v))
// fail.push(v=>fn2(v))
// }
// }
// }
// new myPromise((resolve,reject)=>{
// setTimeout(()=>{
// // resolve('成功')
// reject('失败')
// },1000)
// }).then(res=>{
// console.log(res)
// },err=>{
// console.log(err)
// })
// function myPromiseAll(promises){
// let success=[]
// let count=0;
// return new Promise((resolve,reject)=>{
// function handle(res,index){
// success[index]=res;
// count=0;
// //因为数组中可能有empaty,单纯的用.length进行判断,会算上empty
// //使用forEach遍历,不会算上empty
// success.forEach(v=>count++)
// if(count===promises.length){
// resolve(success)
// }
// }
// promises.forEach((p,index)=>{
// //有可能promise有异步方法,为了保证最后结果数组和promises中的顺序保持一致,添加index
// p.then(res=>{
// handle(res,index)
// },err=>{
// reject(err)
// })
// })
// })
// }
// myPromiseAll([new Promise((resolve,reject)=>{setTimeout(()=>{
// resolve('第一个结果')
// },3000)}),Promise.resolve('第二个结果'),Promise.resolve('第三个结果'),Promise.reject('错误')]).then(res=>{
// console.log(res)
// },err=>{
// console.log(err)
// })
function myPromiseRace(promises){
return new Promise((resolve,reject)=>{
promises.forEach(p=>{
p.then(res=>{
resolve(res)
},err=>{
resolve(err)
})
})
})
}
let p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('第一个结果')
},6000)
})
let p2=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('第二个结果')
},3000)
})
myPromiseRace([p1,p2]).then(res=>{
console.log(res)
},err=>{
console.log(err)
})
</script>
vue2响应式和vue3响应式原理
<script>
/**
//使用Object.defineproperty实现vue2响应式原理
//vue2采用Object.defineproperty带来的问题是无法对数组进行劫持,而且对data中的属性都添加了属性劫持。其实没有必要
let data={name:'aaa',obj:{name:'bbb',number:1},arr:[1,2,3]}
class Oberser{
constructor(data){
this.oberser(data)
}
oberser(data){
if(!data || !Object.prototype.toString.call(data)==='[object Object]'){
return;
}
Object.keys(data).forEach(key=>{
if(Object.prototype.toString.call(data[key])==='[object Object]'){
//如果该属性值是对象,继续深度劫持
this.oberser(data[key])
}
this.defineReactive(data,key,data[key])
})
}
defineReactive(data,key,value){
//备份this
let _this=this;
Object.defineProperty(data,key,{
enumerable:true,
configurable:true,
get(){
//当解析模板时,会触发当前属性的get,此时收集依赖
//...
console.log(`获取了${key}属性值`)
//这里使用了闭包
return value
},
set(newValue){
if(value!==newValue){
value=newValue;
console.log(`${key}属性值更新了,我要去更新页面了`)
//判断如果设置的是对象的话,继续对该对象的属性进行劫持
if(Object.prototype.toString.call(value)==='[object Object]'){
_this.oberser(value);
}
//调用Dep中的Watcher中的update,从而更新页面中用到该属性的地方
//...
}
}
})
}
}
new Oberser(data)
**/
</script>
<script>
//vue3响应式原理采用的是proxy,可以对对象或数组进行数据劫持,而且默认只会代理一层。添加属性时也会触发set
//proxy对数组进行代理时,如果调用数组的push方法添加数值后,会导致第一次添加索引和值,第二次修改length,导致触发了两次更新
//这里使用WeakMap,而不使用Map的原因是WeackMap是弱引用,也就是如果WeackMap中的对象不被外部所引用了,会清空这个对象的
let reactiveMap=new WeakMap()
function reactive(data){
if(typeof data!=='object' ||!data){
return
}
if(reactiveMap.get(data)){
return reactiveMap.get(data)
}
let proxy=new Proxy(data,baseHandler)
reactiveMap.set(data,proxy)
return proxy
}
const baseHandler={
get(target,key,receiver){
let result=Reflect.get(target,key,receiver)
//依赖收集
console.log(`获取了${key}属性,值是${result}`)
track(target,'get',key)
if(typeof result==='object'){
//如果发现取得是对象继续递归代理
//这里就体现了使用proxy代理的优越性=>用得到的对象属性才会继续对它代理
return reactive(result)
}
return result
},
set(target,key,value,receiver){
const oldValue=target[key]
//对数组和对象新增属性也会走set
//如果是数组调用push方法添加值时,会触发两次更新:1、添加索引和值(长度在这里被修改了)2、修改长度
//为防止调用数组push方法,产生两次更新操作,采用下面的做法:
//调用数组的push方法,第一次是添加的索引和值,因为还没有调用Reflect.set(),所以这里的length还没被改变,此时的hadKey为false
//调用数组的push方法,第二次是修改的长度,会进入到hadOwn的判断,hasKey为true
//如果是arr[0]=1,修改索引和值时,hadKey为true
//如果set的是对象,就会进入到hasOwn的判断,有该属性hadKey为true,无该属性hadKey为false
let hadKey=Array.isArray(target)&&isIntegerKey(key)?parent(key)<target.length:hasOwn(target,key)
let result=Reflect.set(target,key,value,receiver);
if(!hadKey){
console.log(`添加了${key}属性,值是${value}`)
}else if(oldValue!==value){
console.log(`将${key}属性值更新为${value}`)
trigger(target,'set',key,value)
}
}
}
function effect(fn){
let effect=createReactiveEffect(fn)
effect()
}
//如果是effect函数嵌套使用的话,为了将依赖和effect一一对应起来,采用栈的结构
let effectStack=[]
let activeEffect;
function createReactiveEffect(fn){
let effect=function(){
try{
activeEffect=fn;
effectStack.push(activeEffect)
return fn()
}finally{
effectStack.pop()
activeEffect=effectStack[effectStack.length-1]
}
}
return effect
}
//将依赖和effect对应起来,并存放到weackmap中
const depsMap=new WeakMap()
function track(target,type,key){
//说明该属性不是在effect中使用的
if(!activeEffect){return}
let targetMap=depsMap.get(target)
if(!targetMap){
depsMap.set(target,targetMap=new Map())
}
let targetSet=targetMap.get(key)
if(!targetSet){
targetMap.set(key,targetSet=new Set())
}
//防止重复添加,对同一个effect中使用的相同属性,只能算一个
if(!targetSet.has(activeEffect)){
targetSet.add(activeEffect)
}
}
function trigger(target,type,key,value){
//表示对该属性没有收集
if(!depsMap.get(target)){return}
let targetMap=depsMap.get(target)
if(!targetMap){
return;
}
let targetSet=targetMap.get(key)
if(!targetSet){
return;
}
targetSet.forEach(e=>e())
}
//定义一些常用的方法
//判断是否是索引
function isIntegerKey(key){
return parseInt(key) ===key
}
function hasOwn(target,key){
return Object.prototype.hasOwnProperty.call(target,key)
}
let data={name:'aaa',obj:{name:'xxx'},age:12}
let proxy=reactive(data)
effect(()=>{
console.log(proxy.obj.name)
effect(()=>{
console.log(proxy.name)
})
console.log(proxy.age)
})
</script>