跳到正文
文章封面

【前端面试题】JS高级

JS高级知识

1、 JQuery一个对象可以同时绑定多个事件,这是如何实现的?

jQuery可以给一个对象同时绑定多个事件,低层实现方式是使用addEventListner或attachEvent兼容不同的浏览器实现事件的绑定,这样可以给同一个对象注册多个事件。

2、 知道什么是webkit么? 知道怎么用浏览器的各种工具来调试和debug代码么?

Webkit是浏览器引擎,包括html渲染和js解析功能,手机浏览器的主流内核,与之相对应的引擎有Gecko(Mozilla Firefox 等使用)和Trident(也称MSHTML,IE 使用)。 对于浏览器的调试工具要熟练使用,主要是页面结构分析,后台请求信息查看,js调试工具使用,熟练使用这些工具可以快速提高解决问题的效率

3、 如何测试前端代码? 知道BDD, TDD, Unit Test么? 知道怎么测试你的前端工程么(mocha, sinon, jasmin, qUnit..)?

了解BDD行为驱动开发与TDD测试驱动开发已经单元测试相关概念,

4、 前端templating(Mustache, underscore, handlebars)是干嘛的, 怎么用?

Web 模板引擎是为了使用户界面与业务数据(内容)分离而产生的, Mustache 是一个 logic-less (轻逻辑)模板解析引擎,它的优势在于可以应用在 Javascript、PHP、Python、Perl 等多种编程语言中。 Underscore封装了常用的JavaScript对象操作方法,用于提高开发效率。 Handlebars 是 JavaScript 一个语义模板库,通过对view和data的分离来快速构建Web模板。

5、 简述一下 Handlebars 的基本用法?

没有用过的话说出它是干什么的即可

6、 简述一下 Handlerbars 的对模板的基本处理流程, 如何编译的?如何缓存的?

学习技术不仅要会用,还有熟悉它的实现机制,这样在开发中遇到问题时才能更好的解决

7、 用js实现千位分隔符?

原生js的熟练度,实践经验,实现思路

8、 检测浏览器版本版本有哪些方式?

IE与标准浏览器判断,IE不同版本的判断,userAgent var ie = /@cc_on !@/false;

9、 我们给一个dom同时绑定两个点击事件,一个用捕获,一个用冒泡,你来说下会执行几次事件,然后会先执行冒泡还是捕获

对两种事件模型的理解

10、实现一个函数clone,可以对JavaScript中的5种主要的数据类型(包括Number、String、Object、Array、Boolean)进行值复制

• 考察点1:对于基本数据类型和引用数据类型在内存中存放的是值还是指针这一区别是否清楚 • 考察点2:是否知道如何判断一个变量是什么类型的 • 考察点3:递归算法的设计

    // 方法一:
Object.prototype.clone = function(){
   var o = this.constructor === Array ? [] : {};
   for(var e in this){
      o[e] = typeof this[e] === "object" ? this[e].clone() : this[e];
   }
   return o;
}
//方法二:
     /**
     * 克隆一个对象
     * @param Obj
     * @returns
     */
    function clone(Obj) {   
        var buf;   
        if (Obj instanceof Array) {   
            buf = [];//创建一个空的数组 
            var i = Obj.length;   
            while (i--) {   
                buf[i] = clone(Obj[i]);   
            }   
            return buf;    
        }else if (Obj instanceof Object){   
            buf = {};//创建一个空对象 
            for (var k in Obj) { //为这个对象添加新的属性 
                buf[k] = clone(Obj[k]);   
            }   
            return buf;   
        }else{ //普通变量直接赋值
            return Obj;   
        }   
    }

11、如何消除一个数组里面重复的元素?

    var arr=[1,2,3,3,4,4,5,5,6,1,9,3,25,4];
        function deRepeat(){
            var newArr=[];
            var obj={};
            var index=0;
            var l=arr.length;
            for(var i=0;i<l;i++){
                if(obj[arr[i]]==undefined)
                  {
                    obj[arr[i]]=1;
                    newArr[index++]=arr[i];
                  }
                else if(obj[arr[i]]==1)
                  continue;
            }
            return newArr;
        }
        var newArr2=deRepeat(arr);
        alert(newArr2); //输出1,2,3,4,5,6,9,25

12、

小贤是一条可爱的小狗(Dog),它的叫声很好听(wow),每次看到主人的时候就会乖乖叫一声(yelp)。从这段描述可以得到以下对象:

    function Dog() {
      this.wow = function() {
               alert(’Wow’);
      }
      this.yelp = function() {
              this.wow();
      }
}

小芒和小贤一样,原来也是一条可爱的小狗,可是突然有一天疯了(MadDog),一看到人就会每隔半秒叫一声(wow)地不停叫唤(yelp)。请根据描述,按示例的形式用代码来实。(继承,原型,setInterval)

    function MadDog() {
    this.yelp = function() {
          var self = this;          
          setInterval(function() {
                self.wow();      
          }, 500);
      }
}
MadDog.prototype = new Dog();         
//for test
var dog = new Dog();
dog.yelp();
var madDog = new MadDog();
madDog.yelp();

13、下面这个ul,如何点击每一列的时候alert其index?(闭包)

<ul id=”test”>
<li>这是第一条</li>
<li>这是第二条</li>
<li>这是第三条</li>
</ul>

<script>
    // 方法一:
var lis=document.getElementById('2223').getElementsByTagName('li');
for(var i=0;i<3;i++)
{
    lis[i].index=i;
    lis[i].onclick=function(){
        alert(this.index);
    };
}
//方法二:
var lis=document.getElementById('2223').getElementsByTagName('li');
for(var i=0;i<3;i++){
    lis[i].index=i;
    lis[i].onclick=(function(a){
        return function() {
            alert(a);
        }
    })(i);
}
</script>

14、编写一个JavaScript函数,输入指定类型的选择器(仅需支持id,class,tagName三种简单CSS选择器,无需兼容组合选择器)可以返回匹配的DOM节点,需考虑浏览器兼容性和性能。

/***
 @param selector {String} 传入的CSS选择器。*
 @return {Array}*/
var query = function(selector) {
var reg = /^(#)?(\.)?(\w+)$/img;
var regResult = reg.exec(selector);
var result = [];
//如果是id选择器
if(regResult[1]) {
if(regResult[3]) {
if(typeof document.querySelector === "function") {
result.push(document.querySelector(regResult[3]));
    }else {
      result.push(document.getElementById(regResult[3]));
    }
   }
   }
   //如果是class选择器
   else if(regResult[2]) {
      if(regResult[3]) {
       if(typeof document.getElementsByClassName === 'function') {
         var doms = document.getElementsByClassName(regResult[3]);
         if(doms) {
            result = converToArray(doms);
         }
       }
     //如果不支持getElementsByClassName函数
     else {
        var allDoms = document.getElementsByTagName("*") ;
       for(var i = 0, len = allDoms.length; i < len; i++) {
         if(allDoms[i].className.search(new RegExp(regResult[2])) > -1) {
           result.push(allDoms[i]);
          }
       }
      }
      }
}
  //如果是标签选择器
  else if(regResult[3]) {
    var doms = document.getElementsByTagName(regResult[3].toLowerCase());
    if(doms) {
      result = converToArray(doms);
    }
  }
  return result;
  }
  function converToArray(nodes){
    var array = null;         
    try{        
      array = Array.prototype.slice.call(nodes,0);//针对非IE浏览器         
     }catch(ex){
       array = new Array();         
     for( var i = 0 ,len = nodes.length; i < len ; i++ ) { 
      array.push(nodes[i])         
     }
  }      
  return array;
}

15、请评价以下代码并给出改进意见。

    if(window.addEventListener){
    var addListener = function(el,type,listener,useCapture){
        el.addEventListener(type,listener,useCapture);
  };
}
else if(document.all){
    addListener = function(el,type,listener){
        el.attachEvent("on"+type,function(){
          listener.apply(el);
      });
   }  
}

•  不应该在if和else语句中声明addListener函数,应该先声明; •  不需要使用window.addEventListener或document.all来进行检测浏览器,应该使用能力检测; •  由于attachEvent在IE中有this指向问题,所以调用它时需要处理一下 改进如下:

function addEvent(elem, type, handler){
  if(elem.addEventListener){
    elem.addEventListener(type, handler, false);
  }else if(elem.attachEvent){
    elem['temp' + type + handler] = handler;
    elem[type + handler] = function(){
    elem['temp' + type + handler].apply(elem);
  };
  elem.attachEvent('on' + type, elem[type + handler]); 
  }else{
  elem['on' + type] = handler;
  }
}

16、给String对象添加一个方法,传入一个string类型的参数,然后将string的每个字符间价格空格返回,例如:

addSpace(“hello world”) // -> ‘h e l l o  w o r l d’
    String.prototype.spacify = function(){
      return this.split('').join(' ');
    };

接着上述问题答案提问, 1)直接在对象的原型上添加方法是否安全?尤其是在Object对象上。(这个我没能答出?希望知道的说一下。)  2)函数声明与函数表达式的区别? 答案:在js中,解析器在向执行环境中加载数据时,对函数声明和函数表达式并非是一视同仁的,解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问),至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解析执行。

17、定义一个log方法,让它可以代理console.log的方法。

可行的方法一:

function log(msg) {
    console.log(msg);
}
log("hello world!") // hello world!

如果要传入多个参数呢?显然上面的方法不能满足要求,所以更好的方法是:

function log(){
    console.log.apply(console, arguments);
};

到此,追问apply和call方法的异同。 对于apply和call两者在作用上是相同的,即是调用一个对象的一个方法,以另一个对象替换当前对象。将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。 但两者在参数上有区别的。对于第一个参数意义都一样,但对第二个参数: apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。 如 func.call(func1,var1,var2,var3)对应的apply写法为:func.apply(func1,[var1,var2,var3]) 。

18、在Javascript中什么是伪数组?如何将伪数组转化为标准数组?

伪数组(类数组):无法直接调用数组方法或期望length属性有什么特殊的行为,但仍可以对真正数组遍历方法来遍历它们。典型的是函数的argument参数,还有像调用getElementsByTagName,document.childNodes之类的,它们都返回NodeList对象都属于伪数组。可以使用Array.prototype.slice.call(fakeArray)将数组转化为真正的Array对象。 假设接第八题题干,我们要给每个log方法添加一个”(app)”前缀,比如’hello world!’ ->’(app)hello world!’。方法如下:

function log(){
      var args = Array.prototype.slice.call(arguments);  //为了使用unshift数组方法,将argument转化为真正的数组
      args.unshift('(app)');
      console.log.apply(console, args);
    };

19、对作用域上下文和this的理解,看下列代码:

var User = {
  count: 1,
  getCount: function() {
    return this.count;
  }
};
console.log(User.getCount());  // what?
var func = User.getCount;
console.log(func());  // what?

问两处console输出什么?为什么? 答案是1和undefined。 func是在winodw的上下文中被执行的,所以会访问不到count属性。 继续追问,那么如何确保Uesr总是能访问到func的上下文,即正确返回1。正确的方法是使用Function.prototype.bind。兼容各个浏览器完整代码如下:

Function.prototype.bind = Function.prototype.bind || function(context){
   var self = this;
   return function(){
      return self.apply(context, arguments);
   };
}
var func = User.getCount.bind(User);
console.log(func());

20、原生JS的window.onload与Jquery的$(document).ready(function(){})有什么不同?如何用原生JS实现Jq的ready方法?

window.onload()方法是必须等到页面内包括图片的所有元素加载完毕后才能执行。 $(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕。

/*
 * 传递函数给whenReady()
 * 当文档解析完毕且为操作准备就绪时,函数作为document的方法调用
 */
var whenReady = (function() {               //这个函数返回whenReady()函数
    var funcs = [];             //当获得事件时,要运行的函数
    var ready = false;          //当触发事件处理程序时,切换为true
    //当文档就绪时,调用事件处理程序
    function handler(e) {
        if(ready) return;       //确保事件处理程序只完整运行一次
        //如果发生onreadystatechange事件,但其状态不是complete的话,那么文档尚未准备好
        if(e.type === 'onreadystatechange' && document.readyState !== 'complete') {
            return;
        }
        //运行所有注册函数
        //注意每次都要计算funcs.length
        //以防这些函数的调用可能会导致注册更多的函数
        for(var i=0; i<funcs.length; i++) {
            funcs[i].call(document);
        }
        //事件处理函数完整执行,切换ready状态, 并移除所有函数
        ready = true;
        funcs = null;
    }
    //为接收到的任何事件注册处理程序
    if(document.addEventListener) {
        document.addEventListener('DOMContentLoaded', handler, false);
        document.addEventListener('readystatechange', handler, false);            //IE9+
        window.addEventListener('load', handler, false);
    }else if(document.attachEvent) {
        document.attachEvent('onreadystatechange', handler);
        window.attachEvent('onload', handler);
    }
    //返回whenReady()函数
    return function whenReady(fn) {
        if(ready) { fn.call(document); }
        else { funcs.push(fn); }
    }
})();

如果上述代码十分难懂,下面这个简化版:

function ready(fn){
    if(document.addEventListener) {//标准浏览器
        document.addEventListener('DOMContentLoaded', function() {
            //注销事件, 避免反复触发
            document.removeEventListener('DOMContentLoaded',arguments.callee, false);
            fn();//执行函数
        }, false);
    }else if(document.attachEvent) {//IE
        document.attachEvent('onreadystatechange', function() {
            if(document.readyState == 'complete') {
                document.detachEvent('onreadystatechange', arguments.callee);
                fn();//函数执行
            }
        });
    }
};

21、(设计题)想实现一个对页面某个节点的拖曳?如何做?(使用原生JS)

回答出概念即可,下面是几个要点

  1. 给需要拖拽的节点绑定mousedown, mousemove, mouseup事件

  2. mousedown事件触发后,开始拖拽

  3. mousemove时,需要通过event.clientX和clientY获取拖拽位置,并实时更新位置

  4. mouseup时,拖拽结束

  5. 需要注意浏览器边界的情况

    22、请实现如下功能

    function setcookie(name,value,days){  //给cookie增加一个时间变量
      var exp = new Date(); 
      exp.setTime(exp.getTime() + days*24*60*60*1000); //设置过期时间为days天
      document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString(); 
    } 
    function getCookie(name){
      var result = "";
      var myCookie = ""+document.cookie+";"; 
      var searchName = "+name+"=";
      var startOfCookie = myCookie.indexOf(searchName);
      var endOfCookie;
      if(satrtOfCookie != -1){
        startOfcookie += searchName.length;
        endOfCookie = myCookie.indexOf(";",startOfCookie);
        result = (myCookie.substring(startOfCookie,endOfCookie));
      }
      return result;
    }
    (function(){
      var oTips = document.getElementById('tips');//假设tips的id为tips
      var page = {
      check: function(){//检查tips的cookie是否存在并且允许显示
        var tips = getCookie('tips');
        if(!tips || tips == 'show') return true;//tips的cookie不存在
        if(tips == "never_show_again") return false;
      },
      hideTip: function(bNever){
        if(bNever) setcookie('tips', 'never_show_again', 365);
        oTips.style.display = "none";//隐藏
      },
      showTip: function(){
      oTips.style.display = "inline";//显示,假设tips为行级元素
      },
      init: function(){
        var _this = this;
        if(this.check()){
        _this.showTip();
        setcookie('tips', 'show', 1);
      }
      oTips.onclick = function(){
        _this.hideTip(true);
      };
      }
      };
    page.init();
    })();

    23、说出以下函数的作用是?空白区域应该填写什么?

    //define 
    (function(window){
    function fn(str){
      this.str=str;
    }
    
    fn.prototype.format = function(){
      var arg = ______;
      return this.str.replace(_____,function(a,b){
           return arg[b]||"";
    });
    }
    window.fn = fn;
    })(window);
    

//use (function(){ var t = new fn('

{1}{2}

'); console.log(t.format('http://www.alibaba.com','Alibaba','Welcome')); })();

答案:访函数的作用是使用format函数将函数的参数替换掉{0}这样的内容,返回一个格式化后的结果:
第一个空是:arguments
第二个空是:/\{(\d+)\}/ig
## 24、    Javascript作用链域?
理解变量和函数的访问范围和生命周期,全局作用域与局部作用域的区别,JavaScript中没有块作用域,函数的嵌套形成不同层次的作用域,嵌套的层次形成链式形式,通过作用域链查找属性的规则需要深入理解。
## 25、    谈谈this对象的理解。
理解不同形式的函数调用方式下的this指向,理解事件函数、定时函数中的this指向,函数的调用形式决定了this的指向。
## 26、    eval是做什么的?
它的功能是把对应的字符串解析成JS代码并运行;应该避免使用eval,不安全,非常耗性能(2个步骤,一次解析成js语句,一次执行)

## 27、    关于事件,IE与火狐的事件机制有什么区别? 如何阻止冒泡?
[1].在IE中,事件对象是作为一个全局变量来保存和维护的.所有的浏览器事件,不管是用户触发的,还是其他事件,都会更新window.event对象.所以在代码中,只要调用window.event就可以获取事件对象, 再event.srcElement就可以取得触发事件的元素进行进一步处理. 
[2].在FireFox中,事件对象却不是全局对象,一般情况下,是现场发生,现场使用,FireFox把事件对象自动传给事件处理程序.
关于事件的兼容性处理要熟练掌握,事件对象具体哪些属性存在兼容性问题,IE与标准事件模型事件冒泡与事件捕获的支持要理解
## 28、    什么是闭包(closure),为什么要用它?
简单的理解是函数的嵌套形成闭包,闭包包括函数本身已经它的外部作用域
使用闭包可以形成独立的空间,延长变量的生命周期,报存中间状态值
## 29、javascript 代码中的"use strict";是什么意思 ? 使用它区别是什么?
意思是使用严格模式,使用严格模式,一些不规范的语法将不再支持
## 30、如何判断一个对象是否属于某个类?
Instanceof   constructor
## 31、new操作符具体干了什么呢?
1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
2、属性和方法被加入到 this 引用的对象中。
3、新创建的对象由 this 所引用,并且最后隐式的返回 this 。
## 32、用原生JavaScript的实现过什么功能吗?
主要考察原生js的实践经验
## 33、Javascript中,有一个函数,执行时对象查找时,永远不会去查找原型,这个函数是?
HasOwnProperty
## 34、对JSON的了解?
轻量级数据交互格式,可以形成复杂的嵌套格式,解析非常方便
## 35、js延迟加载的方式有哪些?
方案一:<script>标签的async="async"属性(详细参见:script标签的async属性)
方案二:<script>标签的defer="defer"属性
方案三:动态创建<script>标签
方案四:AJAX eval(使用AJAX得到脚本内容,然后通过eval_r(xmlhttp.responseText)来运行脚本)
方案五:iframe方式
## 36、模块化开发怎么做?
理解模块化开发模式:浏览器端requirejs,seajs;服务器端nodejs;ES6模块化;fis、webpack等前端整体模块化解决方案;grunt、gulp等前端工作流的使用
## 37、AMD(Modules/Asynchronous-Definition)、CMD(Common Module Definition)规范区别?
理解这两种规范的差异,主要通过requirejs与seajs的对比,理解模块的定义与引用方式的差异以及这两种规范的设计原则
## 38、requireJS的核心原理是什么?(如何动态加载的?如何避免多次加载的?如何 缓存的?)
核心是js的加载模块,通过正则匹配模块以及模块的依赖关系,保证文件加载的先后顺序,根据文件的路径对加载过的文件做了缓存
## 39、让你自己设计实现一个requireJS,你会怎么做?
核心是实现js的加载模块,维护js的依赖关系,控制好文件加载的先后顺序
## 40、谈一谈你对ECMAScript6的了解?
ES6新的语法糖,类,模块化等新特性
## 41、ECMAScript6 怎么写class么,为什么会出现class这种东西?
```js
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  toString() {
     return '('+this.x+', '+this.y+')';
  }
}

42、异步加载的方式有哪些?

方案一: