面经三

前言

2018年6月1日儿童节,真是个喜庆的节日。对我来说可能是很重要的一天。

问题一

window.onload与document.ready()的区别?

==Jquery==中$(document).ready()的作用类似于传统JavaScript中的window.onload方法,不过与window.onload方法还是有区别的。

1:执行时间不同

$(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕,document.ready()加载的速度较快,只需要等待dom树的加载,无需等待图片以及媒体资源。 而window.onload不仅要等DOM结构加载完,还要加载图片,视频,音频在内的所有文件都加载完毕,如果在加载图片和媒体资源上花费了大量时间的话,用户就会明显感觉到网速明显的卡顿。

2:编写个数不同

window.onload同时编写多个,在执行程序时只会执行最后一个window.onload,而document.ready()则会执行多个。

例如:

window.onload=function(){  
    <span style="white-space:pre;"> </span>alert(3)  
    }  
    window.onload=function(){  
        alert(5)  
    }  

两个window.onload,则只会弹出5

  $(document).ready(function(){  
<span style="white-space:pre;"> </span>alert("1")  
})  
   $(document).ready(function(){  
    alert("2")  
})  
   $(document).ready(function(){  
    alert("3")  
})  

同时多个$(document).ready(),则会全部弹出1,2,3。

问题2

###有没有遇见javascript的float计算不准确的问题?

电脑做着正确的二进制浮点运算,但问题是你输入的是十进制的数,电脑以二进制运算,这两者并不是总是转化那么好的,有时候会得到正确的结果,但有时候就不那么幸运了。

alert(0.7+0.1);//输出0.7999999999999999
alert(0.6+0.2);//输出0.8

输入两个十进制数,转化为二进制运算过后再转化回来,在转化过程中自然会有损失。但一般的损失往往在乘除运算中比较多,而JS在简单的加减法里也会出现这类问题,你也看到了,这个误差也是非常小的,但是却是不该出现的。

一种方法。比如0.7+0.1,先把0.1和0.7都乘10,加完之后再除10。

另外可以自己写点函数来解决这个问题,自己百度谷歌一下应该有很多,但是最好还是不要用JS做一些复杂的浮点运算,毕竟JS更多的作用不在于此。

<script type="text/javascript">  
  
    // 两个浮点数求和  
    function accAdd(num1,num2){  
       var r1,r2,m;  
       try{  
           r1 = num1.toString().split('.')[1].length;  
       }catch(e){  
           r1 = 0;  
       }  
       try{  
           r2=num2.toString().split(".")[1].length;  
       }catch(e){  
           r2=0;  
       }  
       m=Math.pow(10,Math.max(r1,r2));  
       // return (num1*m+num2*m)/m;  
       return Math.round(num1*m+num2*m)/m;  
    }  
      
    // 两个浮点数相减  
    function accSub(num1,num2){  
       var r1,r2,m;  
       try{  
           r1 = num1.toString().split('.')[1].length;  
       }catch(e){  
           r1 = 0;  
       }  
       try{  
           r2=num2.toString().split(".")[1].length;  
       }catch(e){  
           r2=0;  
       }  
       m=Math.pow(10,Math.max(r1,r2));  
       n=(r1>=r2)?r1:r2;  
       return (Math.round(num1*m-num2*m)/m).toFixed(n);  
    }  
    // 两数相除  
    function accDiv(num1,num2){  
       var t1,t2,r1,r2;  
       try{  
           t1 = num1.toString().split('.')[1].length;  
       }catch(e){  
           t1 = 0;  
       }  
       try{  
           t2=num2.toString().split(".")[1].length;  
       }catch(e){  
           t2=0;  
       }  
       r1=Number(num1.toString().replace(".",""));  
       r2=Number(num2.toString().replace(".",""));  
       return (r1/r2)*Math.pow(10,t2-t1);  
    }  
      
    function accMul(num1,num2){  
       var m=0,s1=num1.toString(),s2=num2.toString();   
    try{m+=s1.split(".")[1].length}catch(e){};  
    try{m+=s2.split(".")[1].length}catch(e){};  
    return Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,m);  
    }  
      
  </script>  
    
    <script>  
    document.write("使用js原生态方法");  
    document.write("<br/> 1.01 + 1.02 ="+(1.01 + 1.02));  
    document.write("<br/> 1.01 - 1.02 ="+(1.01 - 1.02));  
    document.write("<br/> 0.000001 / 0.0001 ="+(0.000001 / 0.0001));  
    document.write("<br/> 0.012345 * 0.000001 ="+(0.012345 * 0.000001));  
    document.write("<br/><hr/>");  
    document.write("<br/>使用自定义方法");  
    document.write("<br/> 1.01 + 1.02 ="+accAdd(1.01,1.02));  
    document.write("<br/> 1.01 - 1.02 ="+accSub(1.01,1.02));  
    document.write("<br/> 0.000001 / 0.0001 ="+accDiv(0.000001,0.0001));  
    document.write("<br/> 0.012345 * 0.000001 ="+accMul(0.012345,0.000001));  
    </script>  

问题3

JS中数字如何四舍五入?

方法一:

在JS中四舍五入的函数 toFixed(n) , n为要保留的小数位数。n为0~20,当n超过20的时候,JS会出错。

var d=10.005;

​ var f=d.toFixed(2);

​ alert(f);

bug:

​ 如果小数点前和要截取的前一位都是0时,不会按常理截取。

​ var h=0.07

​ h.toFixed(1)的值为0.0

​ 如果要修改这个缺陷,可以把js中的Number类型的toFixed方法重写。

例如:

Number.prototype.toFixed = function(d)  
  {  
      var s=this+"";if(!d)d=0;  
      if(s.indexOf(".")==-1)s+=".";s+=new Array(d+1).join("0");  
      if (new RegExp("^(-|\\+)?(\\d+(\\.\\d{0,"+ (d+1) +"})?)\\d*$").test(s))  
      {  
          var s="0"+ RegExp.$2, pm=RegExp.$1, a=RegExp.$3.length, b=true;  
          if (a==d+2){a=s.match(/\d/g); if (parseInt(a[a.length-1])>4)  
          {  
              for(var i=a.length-2; i>=0; i--) {a[i] = parseInt(a[i])+1;  
              if(a[i]==10){a[i]=0; b=i!=1;} else break;}  
          }  
          s=a.join("").replace(new RegExp("(\\d+)(\\d{"+d+"})\\d$"),"$1.$2");  
      }if(b)s=s.substr(1);return (pm+s).replace(/\.$/, "");} return this+"";  
};  

方法二:

通过巧妙的使用Math.round函数,完全可以解决数值很小时的精度问题。Math.round(number) 返回与给出的数值表达式最接近的整数。如果 number 的小数部分大于等于 0.5,返回值是大于 number 的最小整数。否则,round 返回小于等于 number 的最大整数。

function round2(number,fractionDigits){     
        return  Math.round(number*Math.pow(10,fractionDigits))/Math.pow(10,fractionDigits);     
    }        
  
var money=0.00542;//0.006;     
alert(round2(money,2));//0.01   

问题4

struts是怎么工作的?

(1)客户端提交一个HttpServletRequest请求(.actionJSP页面)
(2)请求被提交到一系列Filter过滤器,ActionCleanUpFilterDispatcher
(3)FilterDispatcherStruts2控制器的核心,它通常是过滤器链中的最后一个过滤器
(4)请求发到FilterDispatcher,FilterDispatcher询问ActionMapper是否需要调用某个Action来处理这个Request(一般根据URL后缀是否为.action来判断)
(5)如果ActionMapper决定需要调用某个Action,FilterDispatcher则把请求交到ActioProxy,由其进行处理.
(6)ActionProxy通过Configuration Manager(它会访问struts.xml)询问框架的配置文件,找到需要调用的Action.
(7)ActionProxy创建一个ActionInvocation实例,ActionInvocation通过代理模式调用Action,(在调用之前会根据配置文件加载相关的所有Interceptor拦截器)
(8)Action执行完毕后,返回一个result字符串,此时再按相反的顺序通过Interceptor拦截器.
(9) 最后ActionInvocation负责根据struts.xml中配置的result元素,找到与返回值对应的result,决定进行下一步输出.

问题5

懒加载是怎么实现的?

懒加载简述:当我们查询一个对象的时候,在默认情况下,返回的只是该对象的代理对象,当用户去使用该对象的属性时,才会向数据库再一次发出查询语句。

例如:有一个对象是Employee,还有一个对象是Department。显然,对于Employee相对Department来说,是多对一的关系;而对于Department相对Employee来说,是一对多的关系。当我们查询Employee对象的时候,如果希望通过employee对象的属性department查询到所对应的Department,那么是会抛出异常的。这是因为懒加载的存在,在session关闭之后,hibernate又向数据库发出一次请求,结果就抛出异常了。

下面总结的是解决这个问题的四种方式:

1:显式初始化(在查询方法内部)

要查询某员工属于哪个部门的时候,需要对Department进行预先查询。

Hibernate.initialize(Department.class);

2:修改对象关系文件,将lazy 改写lazy = false,即关闭懒加载。

以上两种方法,确实可以解决问题,但是缺点是无论后面是否使用该对象,hibernate都会向数据库发出SQL语句请求数据,造成不必要的性能浪费。

3:使用过滤器(web项目)

  • 获取session的方式必须使用getCurrentSession。
  • 特殊的关闭session方式。
public void doFilter(ServletRequest request, ServletResponse response,  
            FilterChain arg2) throws IOException, ServletException {  
        // TODO Auto-generated method stub  
        Session session = null;  
        Transaction tx = null;  
        try {  
            session = HibernateUtil.getCurrentSession();  
            tx = session.beginTransaction();  
            arg2.doFilter(request, response);//请求一直在走  
            tx.commit();  
        } catch (Exception e) {  
            // TODO: handle exception  
            if(tx != null){  
                tx.rollback();  
            }  
        }finally{  
            //特殊的关闭方式  
            HibernateUtil.closeCurrentSession();  
        }  
    }  

4:在SSH框架中,使用spring提供的openSessionView。

其原理和第三种方法中使用Filter类似,只不过这个filter是spring提供的。使用时只需要在web.xml文件配置如下:

<!-- 使用spring解决懒加载问题 -->  
    <filter>  
        <filter-name>OpenSessionInViewFilter</filter-name>  
        <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>  
    </filter>  
    <filter-mapping>  
        <filter-name>OpenSessionInViewFilter</filter-name>  
        <url-pattern>/*</url-pattern>  
    </filter-mapping>  

第3和第4中方法也能解决懒加载的问题,其中第4种方法也是目前使用较多的。但是这两种方法也是有缺点的,缺点就是延长了session关闭的时间,session的生命周期变长。没有使用该方法之前,session是在查询完数据之后,就被关闭了;而现在,session的关闭是在一次web请求的最后才关闭。

坚持原创技术分享,您的支持将鼓励我继续创作!