zoukankan      html  css  js  c++  java
  • JS常用面试题



    一、闭包:
     
    1、函数嵌套函数、内部函数可以引用外部函数的参数和变量。
    参数和变量不会被垃圾回收机制所收回
     
    function aaa(a){
    var b = 5;
    function bbb(){
    alert(a); //内部函数引用外部函数的参数
    alert(b);//内部函数引用外部函数的变量
    }
     
    }
     
    2、好处:
     
    1)希望一个变量长期驻扎在内存当中。
    2)避免全局变量的污染
    3)私有成员的存在
     
     
    function aaa(){
    var a = 1; //局部,避免在函数外面是全局,影响其他变量
    return function (){
    a++;
    alert(a);
    }
     
    }
    var b = aaa();
    b(); //2
    b(); //3
     
    ```
    function fn1() {
    var a = 1;
     
    function fn2() {
    //我们是可以访问到fn1中定义a的值的
    //alert(a);
    alert(a++);
    }
     
    fn2();
    }
     
    fn1(); // 1
    fn1();// 1 //当一个函数被执行的时候,和函数有关的一些变量会被申明(出现在内存中),当这个函数执行完成以后,如果函数中申明的变量没有再被其他地方所调用,则和这个函数有关的数据自动会被销毁
     
     
     
    function fn1() {
    var a = 1;
     
    function fn2() {
    alert(a++); // alert(a); a=a+1;
    //alert(++a) // a=a+1; alert(a);
    }
     
    return fn2; //返回出去的是一个引用类型的值
    }
     
    var f = fn1();// f 和 fn2 指向的同一内存地址,所以fn1中的变量和函数不会被销毁
     
    f(); // 1
    f(); // 2
    f = 1; // 断开,f 与 fn2 不指向同一内存,fn1销毁其中的变量和函数
    f = fn1(); // f 重新和fn2建立关系
    f(); // 1
     
     
    3、用法:
     
    1)代码模块化:
     
    函数声明 转成 函数表达式:(代码模块化,减少全局变量的污染)
     
    第一种用法:函数自执行:
     
    (function (){
     
    alert(1);
     
    })();
     
    第二种用法:return写法:
     
    var aaa = (function(){
    var a = 1;
    return function(){
    a++;
    alert(a);
     
    }
     
    })();
     
    aaa(); //2
    aaa(); //3
     
    私有成员的存在:
     
    var aaa = (function(){
    var a = 1;
    function bbb(){
    a++;
    alert(a);
    }
     
    function ccc(){
    a++;
    alert(a);
    }
     
    return {
     
    b:bbb,
    c:ccc
     
    }
     
    })();
     
    aaa.b(); //2
    aaa.c(); //3
     
    在外面调用不到a,bbb,ccc
     
     
    2)在循环中直接找到对应元素的索引:
     
     
    var li = document.getElementsByTagName('li');
     
    for(var i = 0;i<li.length;i++){
     
    li[i].onclick = function(){
    alert(i); // 3,点击哪一个li都是3,当点击的时候for循环已经结束了为3
    }
    }
     
     
    闭包写法
     
    var li = document.getElementsByTagName('li');
     
    for(var i = 0;i<li.length;i++){
     
    (function(){
     
    li[i].onclick = function(){
    alert(i); // 点击第一个弹出1,第二个弹出2,第三个弹出3
    }
     
    })(i);// i 驻扎在内存中。
     
     
    }
     
     
    return写法
     
    for(var i = 0;i<li.length;i++){
     
    li[i].onclick = (function(){
    return function(){
    alert(i);
    }
    })(i);// i 驻扎在内存中。
    }
     
    ````
    模拟选项卡:
     
    for (var i=0; i<aInput.length; i++) {
    //show(i);
     
    (function(i) { //这里面的包括形式参数在内的所有 i 都和外面的i无关,仅仅是个形参,可以换成任何字母
    aInput[i].onclick = function() {
    for (var j=0; j<aInput.length; j++) {
    aInput[j].style.background = '';
    aP[j].style.display = 'none';
    }
     
    aInput[i].style.background = 'yellow';
    aP[i].style.display = 'block';
    }
    })(i);//这个i代表for循环中的i
     
    }
     
    以上写法就相当于,定义一个带参函数show(a),然后调用,并传参:show(i);
     
    4、IE下会引发内存泄漏,内存一直增加占用cpu。
     
    事件中的函数中引用外部对象时:
     
    window.onload = function(){
     
    var div = document.getElementById('div');
     
    div.onclick = function(){
     
    alert(div.id); // div 是onclock函数外部的div,引发内存泄漏
    }
     
    //解决方法1:
     
    window.onunload = function(){
     
    div.onclick = null;//内存释放
     
    }
     
    }
     
     
     
    window.onload = function(){
     
    var div = document.getElementById('div');
     
    //解决方法2代码:
    var id = div.id;
     
    div.onclick = function(){
     
    alert(id); // div 是o'clock函数外部的div,引发内存泄漏
    }
    //解决方法2代码:
    div = null;//让对象为空
     
    }
     
     
     
    二、函数声明与函数表达式:
     
     
    函数声明: function 函数名(){}
     
    函数表达式: var a = function 函数名(可写可不写)() { }
     
    写函数名:命名函数表达式
    不写: 匿名函数表达式
     
    function aaa(){} //函数声明
     
    前面有 =是表达式:
     
    var a = function aaa(){} //命名函数表达式
     
    var a = function () {} // 命名函数表达式
     
    括号中的都是表达式:
     
    (function aaa(){}) //表达式
     
    位运算符都是表达式:
     
    ~function aaa(){}
    +function aaa(){}
    -function aaa(){}
     
     
    区别:
     
    1.函数表达式可以直接后面加括号执行。而函数声明是不可以的。
    2.函数声明可以被提前解析出来的。js的预解析。
     
    错误:function aaa(){}();
     
    正确:var a = function aaa(){}(); //直接执行
     
    ~function aaa(){}();
     
    (function aaa(){})();
     
     
    兼容性:
     
    函数表达式写法不要在外部调用函数名
     
    var a = function aaa(){
    alert(2);
    }
     
    a(); 所有浏览器都兼容
     
    //aaa();//外面找不到,不推荐使用
     
     
     
     
    函数表达式:
     
    alert(fn1); //undefined
     
    //申明一个变量fn1,赋值了一个函数
     
    var fn1 = function() {
    alert(1);
    }
     
    函数声明:
     
    alert(fn2); //function
     
    function fn2() {
    alert(2);
    }
     
     
     
    三、对象和函数都是引用的关系
     

    var a = 5;
    var b = a;
    b += 3;
    alert(b);//8
    alert(a);//5
     
     
    对象引用:
     
    var a = [1,2,3];
    var b = a;//a把内存地址也给了b,a和b共用一个地址
    b.push(4);
    alert(b);//1,2,3,4
    alert(a);//1,2,3,4
     
     
     
     
    var a = [1,2,3];
    var b = a;//a把内存地址也给了b,a和b共用一个地址
    b = [1,2,3,4];//b在内存当中又重新占了一个地址,与a分离,与之前的b不同,修改b也不会影响到a了。
     
    alert(b);//1,2,3,4
    alert(a);//1,2,3
     
     
     
    var obj = {
    a:10
     
    };
     
    var obj2 = obj;
    obj2.a = 20;
    alert(obj.a);//20

     
    四、事件委托
     
    事件委托(事件代理):利用冒泡原理
     
    event对象的事件源:不管在哪个事件中,只要你操作的那个元素就是事件源
     
    ie:window.event.srcElement
     
    标准下:event.target
     
    nodeName:找到当前元素的标签名
     
    好处:
     
    1)提高性能
     
     
    例子:移到li使标签变颜色。移出颜色消失。移到ul身上没反应。
     
    window.onload = function(){
     
    var oUl = document.getElementById('ul');
    var oLi = oUl.getElementsTageName('li');
     
    /*for(var i = 0; i < oLi.length;i++){
    oLi[i].onclick = function (){
    alert(123);
    }
    }*/
     
    //事件委托:
    oUl.onmouseover = function(ev){
     
    var ev = ev || window.event;
     
    var target = ev.target || ev.srcElement;
     
    if(target.nodeName.toLowerCase()=='li'){
     
    target.style.background = 'red';
     
    }
    }
    oUl.onmouseout = function(ev){
     
    var ev = ev || window.event;
     
    var target = ev.target || ev.srcElement;
     
    target.style.background = ' ';
     
    }
    }
     
     
    2)新添加的元素还会有之前的事件
     
     
    window.onload = function(){
     
    var oUl = document.getElementById('ul');
    var oLi = oUl.getElementsTageName('li');
    var iNow = 4;
     
    oUl.onmouseover = function(ev){
     
    var ev = ev || window.event;
     
    var target = ev.target || ev.srcElement;
     
    if(target.nodeName.toLowerCase()=='li'){
     
    target.style.background = 'red';
     
    }
     
    }
    oUl.onmouseout = function(ev){
     
    var ev = ev || window.event;
     
    var target = ev.target || ev.srcElement;
     
    target.style.background = ' ';
     
    }
     
    //点击一次按钮后会在ul中新添加一个li,当不使用事件委托,直接在li上添加鼠标 移入移出事件,那么新添加的li元素就不会 存在之前li的鼠标移入移出事件。如果使 用事件委托,之前的li事件效果也会添加到新添加的li元素身上,原理就是利用了冒 泡。
    button.onclick = function(){
     
    iNow++;
    var oLi = document.createElement('li');
    oLi.innerHTML = 1111* iNow;
    oUl.appendChild(oLi);
     
    }
    }
     
     
     
     
     
    五:排序:
     
     
    快速排序:
     
     
    1、找一个处于中间位置的基准点
    2、建立两个数组,分别存储左边和右边的数组。比基准小的放前面,比基准大的放后面。
    3、利用递归进行下次比较
     
    递归:一个函数进入下一个这个函数又进入,遇到条件返回上一个,一层一层返回到第一个。
     
    1)函数调用函数自身,执行递的动作
    2)最后一次判断一个终止条件,可以执行归的动作。
     
     
    递归求阶乘:
     
    function test(n){
     
    if(n==1){//当到1时,结束递的过程,开始倒着进行归过程
    return 1;//一层层返回,最后一次返回到上一次的函数...
    }
     
    return n*test(n-1);// 4x3x2
    }
    alert(test(4));
     
     
    快排:
     
    function quickSort(arr){
     
    if(arr.length<=1){ //数组为空或者有一个数
     
    return arr;
     
    }
    var num = Math.floor(arr.length/2);//获取中间位置的索引
     
    var numValue = arr.splice(num,1);//把中间位置的基准点分离出来
     
    var left = [];
     
    var right = [];
     
    for(var i =0;i<arr.length;i++){
     
    if(arr[i]<numValue){//如果小于基准点,放在left[]中
     
    left.push(arr[i]);
     
    }else{
     
    right.push(arr[i]);
     
    }
     
    }
     
    //递归第二次是根据第一次基准点分成的左右,分别在左边和右边各找个中间的基准点,再次划分left和right...
    return quickSort(left).concat([numValue],quickSort(right));//合并,递归
     
    }
     
    alert(quickSort([12,5,37,6,22,40]);)//5,6,12,22,37,40
     
     
     
    冒泡排序:
     
     
    for (var i=0; i<arr.length-1; i++) {
     
    var f = false;
     
    for (var j=0; j<arr.length-1-i; j++) {
    var a = arr[j];
    var b = arr[j+1];
     
    if (a < b) {
    f = true;
    arr[j] = b;
    arr[j+1] = a;
    }
    }
     
    if (!f) {
    break;
    }
     
    alert(arr);
     
    }
     
     
    六、枚举算法
     
    枚举算法:
     
    从众多的候选答案中用for来做筛选,通过if找出正确的解
     
     
    七、JS的跨域:
     
    同一个域名下的js文件不存在跨域问题
     
    ajax不能跨域
     
    解决跨域问题:
     
    1)子域和主域都设置document.domain = 'a.com';
     
    2)服务器代理:XMLHttpRequest代理文件
     
    3)script标签:jsonp的形式(单域操作)
     
    jsonp: json + padding(内填充)
     
    在a网站:在a网站调用b网站
    <script>
    function box(json){
     
    alert(son.name); //leo
     
    }
    </script>
    //jsonp在调用函数js的下面调用:
    <script src="jsonp.js"></script>
     
     
    在b网站:jsonp.js:
    <script>
    box({name = 'leo'});
    </script>
     
     
    4)location.hash
     
    5)window.name
     
    6)flash
     
    7)html5 postMessage
     
     
     
     
     
     
     
    八、iframe:
     
    1)在主页面操作iframe页面中元素方法:
     
    var oIframe = .....
     
    所有浏览器都支持:
     
    oIframe.contentWindow.document.getElementById('div')...
     
    或者:
     
    ie 6 7不支持:
     
    oIframe.contentDocument.getElementById('div')...
     
     
    2)在iframe页面中操作父级页面元素方法:
     
    window.parent.document.getElementById('div')...//父层页面
     
    window.top.document.getElementById('div')...//最顶层页面
     
     
     
    ie下iframe的unload事件只能用绑定的形式
     
     
     
    九、console:
     
     
    console.log()
     
    console.warn();//警告提示
     
    console.error();//错误提示
     
    console.group()//分组开始
     
    console.groupEnd();//分组结束
     
    分组开始和结束之间一般添加console.log()进行调试
     
    console.dir();输出对象所有信息
     
    console.dirxml();显示当前元素包含的代码结构
     
    console.assert();如果是假的断言失败,如果是真的断言不提示信息
     
    console.trace();当前函数的执行过程
     
    console.time('计时器');代码执行时间。括号中写个标题,表示开始。
     
    ...代码
     
    console.timeEnd('计时器');表示时间结束。括号中写的内容与开始时一样
     
    console.profile();测试代码性能,括号中可以为空。
     
    ...代码
     
    console.profileEnd();
     
     
     
     
     
     
    十、DOM 优化:
     
    1、dom与JavaScript:
     
    尽量减少js操作dom 的次数
     
    innerHTML与dom方法:
     
    如果要创建多个元素li,在for循环前面声明一个变量:var str =" "; 然后在for循环里用str += '<li></li>';后在for循环后,添加到ul.innerHTML = str;
     
    var ul = document.get ...
     
    var str = " ";
     
    for(var i =0;i<3000;i++){
     
    str += '<li></li>';
     
    }
     
    ul.innerHTML = str;
     
     
    2、减少dom操作
     
    1)节点克隆 - cloneNode
     
    var ul = doc...
    var li = document.createElement('li');
    ul.innerHTML = 'li';
    for(var i = 0;i<3000;i++){
     
    var new = li.cloneNode(true);
    ul.appendChild(new);
     
    }
     
     
    2)访问元素集合 - 尽量用局部变量
     
    var lei = li.length;//优化
     
    for(var i = 0;i<len;i++){
     
    li[i]....
    }
     
     
    3)元素节点 - 尽量用只获取元素的节点方法
     
    childNodes:能获取元素节点、文本节点
    children:只能获取元素节点,推荐使用
     
    firstChild:能获取元素节点、文本节点
    firstElementChild:只能获取元素节点,推荐使用
     
     
    4)选择器API
     
    querySelector、querySelectorAll
     
     
     
    3、dom与浏览器:
     
     
    重排:改变页面的内容(改变元素的宽高和位置)
    重绘:浏览器显示内容(重排后的显示就是重绘)
     
    改变元素的背景颜色是重绘
     
    1)添加顺序 - 尽量在appendChild前添加操作
     
    2)合并dom操作 - 利用cssText
     
    3)缓存布局信息 - 把操作用变量先保存起来
     
    4)文档碎片 - createDocumentFragment();创建少的话提高不大
     
    var ul = document.get...
     
    var frag = document.createDocumentFragment();
     
    for(var i = 0 ; i <3000; i++){
     
    var li = document.createElement('li');
     
    frag.appendChild(li);
     
    }
     
    ul.appendChild(frag);
     
     
    4、dom与事件的关系:
     
    可以用事件委托
     
    5、dom与前端模板:
     
    能更好地对逻辑和视图分离,MVC架构的基础
     
     
     
     
    十一、变量预解析
     
     
     
    alert(a);
    //在很多的其他语言中,变量需要先申明再使用,但是在js中我们可以先使用再申明(虽然这里的结果不是10,是undefined)
    var a = 10;
    //在js中,js解析器会对我们的代码做一些初始化分析的工作,其他包含了这么一项内容,解析器会把程序中变量的申明在程序代码执行之前做一个初始化, 解析器会把变量的申明提前处理,上面的先调用,后申明其实也可以是下面这样
     
    变量预解析:
     
    var a; //申明会提前,赋值不会提前
     
    alert(a);
     
    a = 10;//赋值
     
    作用域:
     
    var a = 10;
     
    function fn() { //函数申明
    //var a;
    alert(a); //函数内部的a
    var a = 100; //申明被提前到了当前作用域的最开始
    alert(a);
    }
     
    fn(); //undefined 100
     
     
     
     
    十二、callee 和 caller
     
     
    callee 返回正在执行的函数本身的引用,它是arguments的一个属性
     
    1 这个属性只有在函数执行时才有效
    2 它有一个length属性,可以用来获得形参的个数,因此可以用来比较形参和实参个数是否一致,即比较arguments.length是否等于arguments. callee.length
    3 它可以用来递归匿名函数。
     
    function fn() {
    //arguments.callee => 当前函数
    alert(arguments.callee);// function fn(){}
    }
     
    eg:让setTimeout来模拟setInterval
     
    函数递归方法:
     
    var a = 1;
     
    function interval() {
    setTimeout(function() {
    document.title = a++;
    interval();
    }, 100);
    }
     
    interval();
     
    利用闭包自执行和callee来递归:
     
    var a = 1;
     
    (function() {
    var _arguments = arguments;
    setTimeout(function() {
    document.title = a++;
    _arguments.callee(); //_arguments代表之前定义的父级函数的
    }, 100);
    })();
     
     
     
    caller:返回一个函数的引用,这个函数调用了当前的函数。即调用该函数的函数。
     
    使用这个属性要注意:
     
    1 这个属性只有当函数在执行时才有用
    2 如果在javascript程序中,函数是由顶层调用的,则返回null
    functionName.caller: functionName是当前正在执行的函数。
     
    function fn1() {
    console.log(1);
    console.log(fn1.caller);//fn2
    }
     
    function fn2() {
    console.log(2);
    console.log(fn2.caller); //null
    fn1();
    }
     
    fn2(); //2 -> null -> 1 -> fn2
     
     
     
    十三、call 和 apply
     
     
    call和apply都是能改变函数内部this的指向
     
    function fn1(a, b) {
    console.log(this);
    console.log(a + b);
    }
     
    fn1.call(document, 1, 2);// 第一个参数是this的指向,可以为null,其后的都是该函数的参数
     
    apply 和call基本一致,不同的是参数的传递上
     
    fn1.apply(document, [1, 2]); //第二个参数就是fn1中的arguments
     
    求最大值:
     
    var arr = [1,6,4,8,3,10,2];
     
    //console.log( Math.max(1,6,4,8,3,10,2) ); //arguments => [1,6,4,8,3,10,2]
     
    console.log( Math.max.apply(null, arr) ); //arguments => arr
     
     
    十四、this
     
    this
    * 函数外 : window
    * 函数内 :
    * 当一个函数被对象调用,则该函数内的this指向调用该函数的对象
    * 当一个函数被事件调用,则该函数内的this指向触发该事件的对象
    * 通过call、apply去调用一个函数,那么这个函数指向call、apply的第一个参数(如果call、apply的第一个参数是undefined/null,则this指向调用该函数的对象)
     
    十五、优化
     
    减少全局变量:作用域链
    只加载可视区内容
    减少dom操作:事件委托 、文档碎片
    减少请求和质量:合并JS 、压缩JS
    能使用正则尽量使用正则
     
     
     
    十六、变量的作用域:
     
    1、外层的变量,内层可以找到(全局);内层的变量,外层找不到(局部)。
     
    var a = 10;
    function aaa(){
     
    alert(a);
     
    }
     
    function bbb(){
     
    var a = 20;
    aaa();
     
    }
     
    bbb(); // 10
     
     
    2、当var 不加的时候,会自动生成全局的变量(不建议这样写。最好把所有要定义的变量加上var)。
     
    function aaa(){
     
    var a = b = 10; // 拆分为b = 10; var a = 10;
     
    }
    aaa();
    alert(a); // undefined
    alert(b); // 10;
     
    3、变量的查找是在代码逐行解读下就近原则去寻找var定义的变量或者function的参数 ,当就近没找到就到父级去找。
     
    var a = 0;
     
    function aaa(){
     
    alert(a); // undefined;代码逐行解读
    var a = 20;
     
    }
    aaa();
     
    ```
    var a = 10;
     
    function aaa(){
     
    a = 20;
    alert(a); //20
     
    }
    aaa();
     
    ```
     
    var a = 0;
     
    function aaa(){
     
    alert(a); //10
    a = 20;
     
    }
    aaa();
     
    ```
     
    var a = 10;
    function aaa(){
     
    bbb();
    alert(a);//10
    function bbb(){
    var a = 20;
    }
     
     
    }
     
     
    4、当参数和局部变量重名的时候,优先级是等同的。
     
    var a = 10;
     
    function aaa(a){
     
    alert(a); //10
    var a = 20;
     
    }
    aaa(a);
     
     
    5、基本类型的赋值,只是值的改变;对象之间是引用的关系,在内存中地址是相同的。
     
    var a = 20;
     
    function aaa(a){
     
    a+=3;
     
    }
    aaa(a);
    alert(a);//10 函数中的a和外部的a不同
     
     
    ···
     
    var a = [1,2,3];
     
    function aaa(a){
     
    a.push(4); // 修改了外部a
     
    }
    aaa(a);
    alert(a); // 1,2,3,4
     
    ···
     
    var a = [1,2,3];
     
    function aaa(a){
     
    a = [1,2,3,4]; //在内存中重新生成了一个a,与外部的a不同
     
    }
    aaa(a);
    alert(a); // 1,2,3

     

  • 相关阅读:
    Java:线程的六种状态及转化
    Java:多线程概述与创建方式
    小白学Java:RandomAccessFile
    如何用IDEA开启断言
    如何通过IDEA添加serialVersionUID
    小白学Java:I/O流
    更改IDEA相对路径
    小白学Java:File类
    小白学Java:内部类
    Leetcode数组题*3
  • 原文地址:https://www.cnblogs.com/hello-web/p/7221472.html
Copyright © 2011-2022 走看看