• js原理及手写系列


    一、JS基础

    new()方法
    (1)__proto__ 指向 构造函数的原型
    (2)通过apply改变this指向获取构造函数的属性和方法
    (3) 如果构造函数执行的结果返回的是一个对象,那么返回这个对象
    (4)不存在就返回对象
    function _new(fun ){
            var obj  ={}
            obj.__proto__ =fun.prototype
            //var obj = Object.create(fun.prototype) 

            //合并前面两步
            var result =  fun.aplly(obj)
            if(obj && (typeof(obj)=='object' || typeof(obj) =='funtion') ){
                    return result
            }
            return obj
    }

     Object.create() 
    思路:将传入的对象作为原型

    function create(obj){

           function F(){ }
            F.proptotype= obj
            return new F()
    }

    function create(fun){

            var obj  ={}  // new Object()
             obj.__proto__ =fun.prototype // Object.prototype = fun.prototype

            retrun obj
    }
    考虑下是否一样

     intanceof
    用于判断构造函数的proptotype是否出现在原型链的任何位置

    (1)首先获取类型的原型

    (2)然后获得对象的原型

    (2)然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null

    function instanceof( left,right){
           var proto = Object.getProrotypeOf(left),
                 prototype = right.prototype;
            while(true){
                   if(!proto)  return false;
                   if(proto == prototype ) return true;
                    proto = Object.getPrototypeOf(prototype);

            }
    }

     Promise

    new Promise(function(resolve,reejct){
            setTimeout(funciton(){
                    console.log('1')
                    resolve("随便什么数据")

            })
    })

    const PENDING = 'pending' 
    const RESOLVED = 'resolved'
    const REJECTED = 'rejected'
    function MyPromise(fn){
            //保存初始化状态

            var self = this

            //初始化状态
            this.state = PENDING

            // 用于保存resolve或者 rejected传入的值

            this.value = null

            //保存resolve的回调函数

            this.resolvedCallbacks = []

           //保存reject 的回调函数

            this.rejectedCallbacks = []

            //状态转变为 resolved方法
            function resolve(value){

                     // 判断传入元素是否为 Promise 值,如果是,则状态改变必须等到前一个状态改变后再进行改变
                    if(value instanceof MyPromise){
                            return value.then(resolve,reject)
                     }

                    //保证代码的执行属性为本轮事件循环的的末尾

                    setTimeout(()=>{
                            // 只有状态为 PENDING
                           if(self.state === PENDING){
                                   //修改状态
                                    self.state = RESOLVED
                                    //设置传入的值
                                    self.value =value
                                    self.resolvedCallbacks.forEach(callback =>{
                                            callback(value)

                                    })

                            }

                    },0)
            }
            // 状态转变为 rejected 方法

            function reject(value){

                  //保证代码的执行属性为本轮事件循环的的末尾

                    setTimeout(()=>{
                            // 只有状态为 PENDING
                           if(self.state === PENDING){
                                   //修改状态
                                    self.state = REJECTED
                                    //设置传入的值
                                    self.value =value
                                    self.rejectedCallbacks.forEach(callback =>{
                                            callback(value)

                                    })

                            }

                    },0)
            }

           // 将两个方式出入函数参数

            try{
                    fn(resolve,reject)
            }catch(e){
                //遇到错误时,补货错误,执行reject 函数    
              reject(e)
            }

    }

    MyPromise.prototype.then = function(onResolved,onRejected){
            //首先判断两个参数是否函数类型,因为这两个参数是可选参数
           onResolved = typeof onResolved === function? onResolved:
                                    funciont(value){  return value }
           onRejected= typeof onRejected=== function? onRejected:
                                    funciont(error){ throw error }
            //如果是等待状态,则将函数加入对应的列表中
            if(this.state === PENDING){
                   this.resolvedCallbacks.push (onResolved)
                   this.rejectedCallbacks.push(onRejected)
            }
            //如果状已经凝固 则直接执行对应状态的函数
            if(this.state === RESOLVED){
                    onResolve(this.value)
            }

            if(this.state === REJECTED){
                    onRejected(this.value)
            }


    }

    Promise.then
     

    • 连续多个 then 里的回调方法是同步注册的,但注册到了不同的 callbacks 数组中,因为每次 then 都返回新的 promise 实例(参考上面的例子和图)
    • 注册完成后开始执行构造函数中的异步事件,异步完成之后依次调用 callbacks 数组中提前注册的回调

    then(onFulfilled, onReject){
        // 保存前一个promise的this
        const self = this; 
        return new MyPromise((resolve, reject) => {
          // 封装前一个promise成功时执行的函数
          let fulfilled = () => {
            try{
              const result = onFulfilled(self.value); // 承前
              return result instanceof MyPromise? result.then(resolve, reject) : resolve(result); //启后
            }catch(err){
              reject(err)
            }
          }
          // 封装前一个promise失败时执行的函数
          let rejected = () => {
            try{
              const result = onReject(self.value);
              return result instanceof MyPromise? result.then(resolve, reject) : reject(result);
            }catch(err){
              reject(err)
            }
          }
          switch(self.status){
            case PENDING: 
              self.onFulfilledCallbacks.push(fulfilled);
              self.onRejectedCallbacks.push(rejected);

              break;
            case FULFILLED:
              fulfilled();
              break;
            case REJECT:
              rejected();
              break;
          }
        })
       }

    Promise.all  返回一个数组
    其中一个接口失败了,多个也就失败了
    function promiseAll(promises){
       return new Promise(funciont(resolve,reject){
            if(!Array.isArray(promises)){
                    throw new TypeError("arguments must be array ")

            }
            var resolvedCounter =0
            var promiseNum = promise.length

            var resolvedResult = []
            for(let i =0; i                 Promise.resolve(promises[i]).then(value=>{

                             resolvedCounter  ++     

                             resolvedResult[i] = value

                            if(resolvedCounter == promiseNum){
                                    return resolve(resolvedResult)

                            }

                    },error=>{
                            return reject(error)

                    }

                    }
            })
    }

    let p1 = new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve(1)
        }, 1000)
    })
    let p2 = new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve(2)
        }, 2000)
    })
    let p3 = new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve(3)
        }, 3000)
    })
    promiseAll([p3, p1, p2]).then(res => {
        console.log(res) // [3, 1, 2]
    })

    Promise.race 谁先成功就用谁的状态
    Promise.race = funciton (args){
            return new Promise((resolve,reject)=>{
                    for(let i = 0 ; i< args.length;i++){
                            args[i].then(resolve,reject)

                    }

            })

    }

    防抖
    函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。

    function debounce(fn, wait) {
          var time = null

           return function(){
                    let context = this,

                    args = arguments
                    if(time){

                            clearTimeout(time)

                            time = null
                    }

                 time= setTimeout(()=>{
                   fun.apply(context,args)

                   },wait)
            }
    }

    function debounce(fn,wait=50,immediate){
            val timer= null;
            retrun function(){

                   if (timer) clearTimeout(timer)
                   const self = this

                   if (immediate && !timer) {

                          fn.apply(this, arguments)

                        }

                   time = setTimeout(()=>{
                          fn.apply(self ,arguments)
                            timer=null
                    },wait)
          }
    }

    节流
    指定时间内执行,在指定时间内多次点击也只会执行一次,来降低事件频率。

    function throttle(fn,delay){
            let curTime = Date.now();

            return function(){
                    let context = this,

                    args = arguments,
                    nowTime = Date.now();
                    if(nowTime -curTime>=dalay){
                            curTime = Date.now()
                            return  fn.apply(context,args)
                    }
            }
    }

    判断函数
    function getType(value){
            if(value === null){
                    return value + " "
            }
            //Object Array
            if(typeof value === "object"){
                    let valueClass = Object.prototype.toString.call(value),
                         type = valueClass.split(" ")[1].split("");//转成数组 第一个O 最后一个]        
                         type.pop() //删除 ]
                         type.join("").toLowerCase() //将首字母小写
                  return type
            }else{
                    // 判断数据是基本数据类型的情况和函数的情况
                    // string boolean number symbol  undefined  function
                    return typeof value;
            }
    }

    call函数 数组

    1. 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
    2. 判断传入上下文对象是否存在,如果不存在,则设置为 window 。
    3. 处理传入的参数,截取第一个参数后的所有参数。
    4. 将函数作为上下文对象的一个属性。
    5. 使用上下文对象来调用这个方法,并保存返回结果。
    6. 删除刚才新增的属性。
    7. 返回结果。


    Function.prototype.myCall = function(context){
            // 判断调用对象
            if(typeof this !=="function"){
                    console.error("type error")
            }
            //获取参数
            let args = [...arguments].slice(1),//去掉第一个参数 数组
                 result = null 
            //判断 context是否传入 ,未传入则传入window
            context = context||window
            // 将调用函数设为方法
            context.fn = this

            // 调用函数
            result = context.fn(...args)
            // 将属性删除
            delete context.fn


            return result  //返回的执行结果
    }

    apply函数

    1. 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
    2. 判断传入上下文对象是否存在,如果不存在,则设置为 window 。
    3. 将函数作为上下文对象的一个属性。
    4. 判断参数值是否传入
    5. 使用上下文对象来调用这个方法,并保存返回结果。
    6. 删除刚才新增的属性
    7. 返回结果

    Function.prototype.myApply = function(context){
            //判断调用对象是否为函数
            if(type this !== "function"){

                    throw new TypeError("Error")
            }
            let context = context || window,

            result = null 
            context.fn = this 

            if(arguments[1]){
                    result = context.fn(...arguments[1])

            }else{
               result = context.fn()

            }
            delete context.fn
            return result 
    }

    bind
    Function.prototype.bind = function(){
            let args = Arrary.prototype.slice(arguments) //转化成可操作数组
            let t = args.shift() // 分割 args 并返回第一个参数 
            const self = this :
            return function(){
                    return self.apply(t, args)

            }
    }
    function fun1(a,b,c){
            console.log(this)  // {name:"obj"}
            console.log(a,b,c) //10,20,30
    }
    var obj = {
            name:"obj"        
    }
    var fun = fun1.bind(obj,10,20,30)
    fun() 
     

    promise封装Post请求-缺

    深拷贝

    浅拷贝-没有深入拷贝,或者只拷贝了属性
     

    funciton deppClone(object){
            if( !object ||  typeof object !==''object"){
                    return  object

            }

            let newObject = Array.isArray(object) ? [] :{}
            for(let key in object){

                   if(object.hasOwnProperty(key)){ //对象自己的属性,过滤掉原型链上的属性

                          newObject[key] = typeof object[key]  =='object' ?deepClone( object[key]) : object[key]
                          }
               }

            return newObject
    }

    二、常用方法

    时间的格式化
    dataFormat('2020-10-11','yyyy-MM-dd')

    dataFormat('2020-10-11','yyyy/MM/dd')
    function dataFormat(dateInpue,format){

            const currentDate = new Date(dateInpue)
            const day = currentDate.getDate()

            const month = currentDate.getMonth() +1
            const year = currentDate.getFullYear()
            format.replace(/yyyy/,year)
            format.replace(/MM/,month )
            format.replace(/dd/,day )
            return format
    }

    两个数字变量不通过临时变量实现值互换
    a  b
    a = a + b
    b= a - b  //原来的a
    a= a -b  //原来的b

    两个数字求和
    var a = [1,2,3,4,5,6,,7,8,9]
    (1)方法一for循环
    (2)reduce
    var num = a.reduce((sum,current,index)=>{

         return   sum = sum + current

    },0)

    (3)递归
    function add (a){
          if(a.length==0){

            return 0

            }
           if(a.length == 1) return a[0]
            return a[0] + add(a.slice(1))
    }

    数组扁平化

    let arr = [1, [2, [3, 4]]];

    (1)array.tostring().split(',')
    (2)flat(层数)  
            arry.flat(3)
    (3)递归遍历 
    (4)reduce 

            funciont flatArray(arry)
                   arry.reduce(function(prev,next){

                            return prev.concat(Array.isArray(next)? flatArray(next):next)

                    },[])   
           }

    (5) while some 

            function flatten(arr) {
                while (arr.some(item => Array.isArray(item))) {
                    arr = [].concat(...arr);
                }
                    return arr;
            }
    (6正则
            function flatten(arr) {
              let str = JSON.stringify(arr);
              str = str.replace(/(

    |" role="presentation" style="text-align: center; position: relative;">|
    )/g, '');
              str = '[' + str + ']';
              return JSON.parse(str); 
            }

    数组去重
    const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
    (1)遍历
    (2)Array.from(new Set(array ))
    (3)属性 使用 Object.hasOwnPropety() 
           {1:1,2:1,3:1,9:1} 
          这种方式

    实现数组的flat方法

    function _flat(arry,depth){
            if(Array.isArray(arry) || depth<=0 ){
                    return arry

            }
            arry.reduce((prev,next)=>{
                    if(Array.isArray(next)){
                            return prev.concat(_flat(next))
                    }else{
                            prev.push(next)
                    }
            },[])
    }

    实现数组的push方法
    let arr = []
    Array.prototype.push = function(){
            for(let i , i                 this[this.length] = arguments[i]
            }
           return this.length
    }

    实现数组的filter
    fn返回是true false

    Array.prototype.filter = funciton(fn){
            if(typeof this !== 'function'){
                   return console.log(‘必须为函数’)

            }
           const res = []

            for(let i=0,len = this.length; i                 fn(this[i]) && res.push(this[i])
            }

           return res 

    }

    实现数组的map函数
    Array.prototype.map = function(fn){

            if(typeof this !== 'function'){
                   return console.log(‘必须为函数’)

            }

            const res = []
            for(let i=0,len = this.length; i             res.push(fn(this[i]))
            }

           return res 
    }

    字符串添加翻转

    Array.prototype._reverse= function(){
          return  this.split('').reverse.join('')
    }

    中间还有很多

    。。。。。。

    实现Jsonp
    function addScript(url){
            const script= document.createElement('script')

            script.src = url 

            script.type = 'text/javascirpt'
            document.body.appendChild(script)
    }
    addScript("http://xxx.xxx.com/xxx.js?callback=handleRes");
    // 设置一个全局的callback函数来接收回调结果
    function handleRes(res) {
      console.log(res);
    }
    // 接口返回的数据格式
    handleRes({a: 1, b: 2});

    实现发布式订阅

    class EventCenter{
      // 1. 定义事件容器,用来装事件数组
        let handlers = {}

      // 2. 添加事件方法,参数:事件名 事件方法
      addEventListener(type, handler) {
        // 创建新数组容器
        if (!this.handlers[type]) {
          this.handlers[type] = []
        }
        // 存入事件
        this.handlers[type].push(handler)
      }

      // 3. 触发事件,参数:事件名 事件参数
      dispatchEvent(type, params) {
        // 若没有注册该事件则抛出错误
        if (!this.handlers[type]) {
          return new Error('该事件未注册')
        }
        // 触发事件
        this.handlers[type].forEach(handler => {
          handler(...params)
        })
      }

      // 4. 事件移除,参数:事件名 要删除事件,若无第二个参数则删除该事件的订阅和发布
      removeEventListener(type, handler) {
        if (!this.handlers[type]) {
          return new Error('事件无效')
        }
        if (!handler) {
          // 移除事件
          delete this.handlers[type]
        } else {
          const index = this.handlers[type].findIndex(el => el === handler)
          if (index === -1) {
            return new Error('无该绑定事件')
          }
          // 移除事件
          this.handlers[type].splice(index, 1)
          if (this.handlers[type].length === 0) {
            delete this.handlers[type]
          }
        }
      }
    }

  • 相关阅读:
    java基础巩固15
    网格化下的服务熔断
    (Pytorch)判断一个tensor中是否存在nan
    docker清理缓存
    clickhouse安装部署
    67、数据源配置 及 配置多个数据源--C3P0 数据源 和 Hikari 数据源
    OpenGL多线程多视图的实现 编程
    微信小程序--数字化会议OA系统之首页搭建
    ai智能电话语音机器人,ai外呼机器人怎么用
    虚拟网络编辑器三种模式工作原理详细介绍(桥接-网络地址转换-主机模式)
  • 原文地址:https://blog.csdn.net/weixin_41127362/article/details/126869569