js(二)

  • call,apply,bind

主要用于改变this指向的 1.call格式:函数名.call(上下文环境,参数)

  说明:
  
     上下文环境:就是应用函数的作用域范围
     参数:是以逗号分隔的参数列表
     
     例如:getMsg.call(obj,'微信小程序','Angular');
 
 特点:1.改变this指向,2.函数被执行了
 
 2. apply格式:函数名.apply(上下文环境,参数)
 
    说明:
     上下文环境:就是应用函数的作用域范围
     参数:是用数组形式传递的参数列表
     
     例如:getMsg.apply(obj,['微信小程序111','Angular222']);
     
 特点:同call的特点
 
 3. bind格式:函数名.bind(上下文环境,参数)
 
   bind传参调用:
   
   getMsg.bind(obj,'微信小程序','Angular')()
   getMsg.bind(obj)('微信小程序','Angular')
 
 
 特点:1.改变this指向,2.返回调用函数本身,函数没有被执行
 
   回顾一下React中的事件绑定:<div onClick={this.play.bind(this)}>
   
  
 
 二、说一下call和apply的实现原理?
 
 > 记住:函数由Function构造器实例化
 
 > 平时创建一个函数:
 >
 > 1.函数声明
 >
 > function Fn() {
 >
 > }
 >
 > 2. 函数表达式
 >
 >     var Fn=function() {
 >
 >
 >     }
 >
 > 3. 用new的方式创建一个函数
 >
 >     var Fn=new Function('函数参数','函数执行体'')
 
 

 eval:可以将内部的字符串解析成代码或表达式去执行
 例如:
 eval('1+2')
 3
 eval('alert("1906A")')
 
 参考官方文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/eval

 call实现原理,模拟call的实现:
 
 //用JS原生模拟call
 
 Function.prototype.call2=function(context) {
 
    var ctx=context || window;
 
    //将函数临时添加到对象上 
    ctx.fn=this;  //其中this代码要调用的函数
 
 
   //处理参数接收问题,arguments
   var args=[];
   for(var i=1,len=arguments.length;i<len;i++) {
       //将获取的每个参数压入数组中
       args.push('arguments['+i+']');  
   }
 
   //最后得到: args[arguments[0],arguments[1],arguments[2]]
 
 
    //执行对象上的函数,eval,用于执行字符串代码的
    var result=eval('ctx.fn('+args+')')  //ctx.fn(args)
    
    //最后删除掉这个函数
    delete ctx.fn
 
   return result;
    
 }

 
 apply实现原理,模拟apply实现:
 
 Function.prototype.apply2=function(context,arr) {
 
    var ctx=context || window;
 
    //将函数临时添加到对象上 
    ctx.fn=this;  //其中this代码要调用的函数
 
   //对是否有arr进行处理
   if(!arr) {
       ctx.fn();
   }else {
 
     //处理参数接收问题,arguments
     var args=[];
     for(var i=1,len=arr.length;i<len;i++) {
         //将获取的每个参数压入数组中
         args.push('arguments['+i+']');  
     }
 
     //最后得到: args[arguments[0],arguments[1],arguments[2]]
 
     //执行对象上的函数,eval,用于执行字符串代码的
     var result=eval('ctx.fn('+args+')')  //ctx.fn(args)
 
   }
    //最后删除掉这个函数
    delete ctx.fn
 
   return result;
    
 }

 
 三、防抖和节流实现原理?

    1. 防抖和节流解决什么问题?

       解决页面高频触发或向后端连续请求的优化逻辑问题 例如:搜索,滚动加载
       
       键盘:onkeydown(键盘按下),onkeyup(键盘抬起)
       鼠标:onmousemove(鼠标移动)
       浏览器事件:onresize(改变窗口尺寸),onscroll(滚动事件)

 
 2. 防抖:

     生活比喻:比如电脑屏保,电梯的开关
     防抖:即在固定n秒间隔内,不会执行代码逻辑,除非n秒内有事件触发,则会再延长n秒,直到n秒没有触发的事件,则在n秒后执行代码逻辑
     
     代码实现思路:
     
     1.主要利用定时器实现
     2.考虑this指向问题
     3.考虑事件对象是否使用 事件对象属于事件处理函数的参数
     
     //防抖函数具体实现
       /*
       * { function }func:代表对哪个高频函数进行防抖
       * { number } wait:代表防抖的时间间隔
       */
      function debounce(func,wait) {
           var timeout;
           return function() {
               var _this=this;
               var args=arguments;
               clearTimeout(timeout)
               timeout=setTimeout(function() {
                   func.apply(_this,args)
               },wait)
     
           }
       }

 
 3. 节流
 
     生活比喻:每天有规律的时间学习,吃饭,跑步,不会因为杂事儿所打乱,类似闹钟是有规律的
     节流:固定的时间节点,执行固定的代码逻辑
 
     > **时间戳实现节流**
 
     
      //节流函数
       /*
       * { function }func:代表对哪个高频函数进行节流
       * { number } wait:代表节流的时间间隔
       * 节流有两种实现方式:时间戳方式,定时器方式
       */
       function throttle(func,wait) {
           var _this;
           var args;
           var previous=0; //记录历史时间戳
     
           return function() {
               var now=+new Date();  //生成一个当前的时间戳
               args=arguments;
               _this=this;
     
              //用当前时间now减去历史时间previous大于wait,就会执行事件处理函数,并且更新历史时间
               if(now-previous>wait) {
                   //执行事件处理函数
                   func.apply(_this,args)
                   //更新历史时间戳
                   previous=now;
               }
           }
       }

 
     > **定时器实现节流**
     
      //节流函数
       /*
       * { function }func:代表对哪个高频函数进行节流
       * { number } wait:代表节流的时间间隔
       * 节流有两种实现方式:时间戳方式,定时器方式
       */
       function throttle(func,wait) {
          var timeout;
          var args;
          var _this;
          
           return function() {
               _this=this;
               args=arguments;
             if(!timeout) {
                 timeout=setTimeout(function() {
                     func.apply(_this,args)
                     timeout=null;
     
                 },wait)
                 
             }
     
             
           }
       }

 
     语言组织:
 
        功能:就是代表能用!
 
        性能:就是代表好用!
 

 四、new的实例原理:
 
 面试官可能会这样问:
  1.new一个构造函数时,中间发生了什么?
  2.或者new一个构造函数,中间执行了哪些步骤?
  
  答:
  第一步:创建一个临时对象obj
  第二步:获取构造函数赋值给Constructor
  第三步:将obj的原型指向Constructor的原型,目的可以让实例化对象找到构造器原型上的方法
  第四步:让Constructor属性作用于obj上,从而可以操作this.xxx的实例属性
  第五步:返回临时对象obj
  
  
  封装的new代码如下:
  
 function new1() {
    // 创建一个临时对象
    var obj={};
   //获取构造函数赋值给Constructor
   var Constructor=[].shift.apply(arguments)
   //然后arguments中的值只能除第一个值之外的参数了
   //将obj的原型指向Constructor的原型,目的可以让实例化对象找到构造器原型上的方法
   obj.__proto__=Constructor.prototype
   
   //让Constructor属性作用于obj上,从而可以操作this.xxx的实例属性
   Constructor.apply(obj,arguments)
 
   //返回obj
   return obj;
 }
原文地址:https://www.cnblogs.com/akby/p/13033231.html