很多面试题是我自己面试BAT亲身经历碰到的。整理分享出来希望更多的前端er共同进步吧。更多的是我一路以来收集的,也有往年的,答案不确保一定正确,如有错误或有更好的解法,还请斧正。
Javascript初级:
1.JavaScript是一门什么样的语言,它有哪些特点?
没有标准答案。
2.JavaScript的数据类型都有什么?
基本数据类型:String, boolean, Number, Undefined, Null
引用数据类型:Object(Array,Date,RegExp,Function)
那么问题来了,如何判断某变量是否为数组数据类型?
方法一.判断其是否具有“数组性质”,如slice()方法。可自己给该变量定义slice方法,故有时会失效
方法二.obj instanceof Array 在某些IE版本中不正确
方法三.方法一二皆有漏洞,在ECMA Script5中定义了新方法Array.isArray(), 保证其兼容性,最好的方法如下:
if(typeof Array.isArray === "undefined") { Array.isArray = function(arg){ return Object.prototype.toString.call(arg)==="[object Array]" };}
3. 已知ID的Input输入框,希望获取这个输入框的输入值,怎么做?(不使用第三方框架)
document.getElementById(“ID”).value
4.希望获取到页面中所有的checkbox怎么做?(不使用第三方框架)
var domList = document.getElementsByTagName(‘input’)var checkBoxList = [];var len = domList.length; //缓存到局部变量while (len--) { //使用while的效率会比for循环更高 if (domList[len].type == ‘checkbox’) { checkBoxList.push(domList[len]); }}
5.设置一个已知ID的DIV的html内容为xxxx,字体颜色设置为黑色(不使用第三方框架)
var dom = document.getElementById(“ID”);dom.innerHTML = “xxxx”dom.style.color = “#000”
6.当一个DOM节点被点击时候,我们希望能够执行一个函数,应该怎么做?
直接在DOM里绑定事件:
在JS里通过onclick绑定:xxx.onclick = test
通过事件添加进行绑定:addEventListener(xxx, ‘click’, test)
那么问题来了,Javascript的事件流模型都有什么?
“事件冒泡”:事件开始由最具体的元素接受,然后逐级向上传播
“事件捕捉”:事件由最不具体的节点先接收,然后逐级向下,一直到最具体的
“DOM事件流”:三个阶段:事件捕捉,目标阶段,事件冒泡
7.什么是Ajax和JSON,它们的优缺点。
Ajax是 异步JavaScript和XML,用于在Web页面中实现异步数据交互。
优点:
可以使得页面不重载全部内容的情况下加载局部内容,降低数据传输量
避免用户不断刷新或者跳转页面,提高用户体验
缺点:
对搜索引擎不友好(
要实现ajax下的前后退功能成本较大
可能造成请求数的增加
跨域问题限制
JSON是 一种轻量级的数据交换格式,ECMA的一个子集
优点:轻量级、易于人的阅读和编写,便于机器(JavaScript)解析,支持复合数据类型(数组、对象、字符串、数字)
8.看下列代码输出为何?解释原因。
var a;alert(typeof a); // undefinedalert(b); // 报错
解释:Undefined是一个只有一个值的数据类型,这个值就是“undefined”,在使用var声明变量但并未对其赋值进行初始化时,这个变量的值就是undefined。而b由于未声明将报错。注意未申明的变量和声明了未赋值的是不一样的。
9.看下列代码,输出什么?解释原因。
var a = null;alert(typeof a); //object
解释:null是一个只有一个值的数据类型,这个值就是null。表示一个空指针对象,所以用typeof检测会返回”object”。
10.看下列代码,输出什么?解释原因。
var undefined;undefined == null; // true1 == true; // true2 == true; // false0 == false; // true0 == ''; // trueNaN == NaN; // false[] == false; // true[] == ![]; // true
undefined与null相等,但不恒等(===)
一个是number一个是string时,会尝试将string转换为number
尝试将boolean转换为number,0或1
尝试将Object转换成number或string,取决于另外一个对比量的类型
所以,对于0、空字符串的判断,建议使用 “===” 。“===”会先判断两边的值类型,类型不匹配时为false。
那么问题来了,看下面的代码,输出什么,foo的值为什么?
var foo = "11"+2-"1";console.log(foo);console.log(typeof foo);
执行完后foo的值为111,foo的类型为String。
11.看代码给答案。
var a = new Object();a.value = 1;b = a;b.value = 2;alert(a.value);
答案:2( 考察引用数据类型细节 )
12.已知数组var stringArray = [“This”, “is”, “Baidu”, “Campus”],Alert出”This is Baidu Campus”。
答案:alert(stringArray.join(""))
已知有字符串foo="get-element-by-id",写一个function将其转化成驼峰表示法"getElementById"。
function combo(msg){ var arr=msg.split("-"); for(var i=1;i arr[i]=arr[i].charAt(0).toUpperCase()+arr[i].substr(1,arr[i].length-1); } msg=arr.join(""); return msg;}
( 考察基础API )
13. var numberArray = [3,6,2,4,1,5]; ( 考察基础API )
1) 实现对该数组的倒排,输出[5,1,4,2,6,3]
2) 实现对该数组的降序排列,输出[6,5,4,3,2,1]
var numberArray = [3,6,2,4,1,5];numberArray.reverse(); // 5,1,4,2,6,34 numberArray.sort(function(a,b){ //6,5,4,3,2,16 return b-a;})
14.输出今天的日期,以YYYY-MM-DD的方式,比如今天是2014年9月26日,则输出
var d = new Date(); // 获取年,getFullYear()返回4位的数字 var year = d.getFullYear(); // 获取月,月份比较特殊,0是1月,11是12月 var month = d.getMonth() + 1; // 变成两位 month = month < 10 ? '0' + month : month; // 获取日 var day = d.getDate(); day = day < 10 ? '0' + day : day; alert(year + '-' + month + '-' + day);
15.将字符串”{$id}{$name}”中的{$id}替换成10,{$name}替换成Tony (使用正则表达式)
"{$id}{$id}_{$name}".replace(/{\$id}/g, '10').replace(/{\$name}/g, ‘Tony’);
16.为了保证页面输出安全,我们经常需要对一些特殊的字符进行转义,请写一个函数escapeHtml,将, &, “进行转义
function escapeHtml(str) { return str.replace(/[<>”&]/g, function(match) { switch (match) { case “ return “>”; case “>”: return “ case “&”: return “&”; case “\””: return “"”; } });}
17.foo = foo||bar ,这行代码是什么意思?为什么要这样写?
if(!foo) foo = bar; //如果 foo 存在,值不变,否则把 bar 的值赋给 foo。
短路表达式:作为"&&"和"||"操作符的操作数表达式,这些表达式在进行求值时,只要最终的结果已经可以确定是真或假,求值过程便告终止,这称之为短路求值。
18.看下列代码,将会输出什么?( 变量声明提升 )
var foo = 1;function() { console.log(foo); var foo = 2; console.log(foo);}
答案:输出undefined 和 2。上面代码相当于:
var foo = 1;function(){ var foo; console.log(foo); //undefined foo = 2; console.log(foo); // 2; }
函数声明与变量声明会被 JavaScript 引擎隐式地提升到当前作用域的顶部,但是只提升名称不会提升赋值部分。
19. 用js实现随机选取10--100之间的10个数字,存入一个数组,并排序。
var iArray = []; funtion getRandom(istart, iend){ var iChoice = istart - iend +1; return Math.floor(Math.random() * iChoice + istart;}for(var i=0; i<10; i++){ iArray.push(getRandom(10,100));}iArray.sort();
20.把两个数组合并,并删除第二个元素。
var array1 = ['a','b','c'];var bArray = ['d','e','f'];var cArray = array1.concat(bArray);cArray.splice(1,1);
21.怎样添加、移除、移动、复制、创建和查找节点(原生JS)
1 )创建新节点
createDocumentFragment() // 创建一个 DOM 片段createElement() // 创建一个具体的元素createTextNode() // 创建一个文本节点
2 )添加、移除、替换、插入
appendChild() // 添加removeChild() // 移除replaceChild() // 替换insertBefore() // 插入
3 )查找
getElementsByTagName() // 通过标签名称getElementsByName() // 通过元素的 Name 属性的值getElementById() // 通过元素 Id ,唯一性
22.有这样一个URL:http://item.taobao.com/item.htm?a=1&b=2&c=&d=xxx&e,请写一段JS程序提取URL中的各个GET参数(参数名和参数个数不确定),将其按key-value形式返回到一个json结构中,如{a:'1', b:'2', c:'', d:'xxx', e:undefined}。
答案:
function serilizeUrl(url) { var result = {}; url = url.split("?")[1]; var map = url.split("&"); for(var i = 0, len = map.length; i < len; i++) { result[map[i].split("=")[0]] = map[i].split("=")[1]; } return result;}
中级Javascript:
1.实现一个函数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 { //普通变量直接赋值32 return Obj; }}
2.如何消除一个数组里面重复的元素?
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 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
3.小贤是一条可爱的小狗 (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 testvar dog = new Dog();dog.yelp();var madDog = new MadDog();madDog.yelp();
4.下面这个 ul ,如何点击每一列的时候 alert 其 index?( 闭包 )
1
- 2
- 这是第一条3
- 这是第二条4
- 这是第三条5
答案:
// 方法一: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].onclick=(function(a){ return function() { alert(a); })(i);}
5.编写一个 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);3 } } //如果不支持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]); } } } } } //如果是标签选择器37 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;}
6.请评价以下代码并给出改进意见。
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; }}
7.给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中,解析器在向执行环境中加载数据时,对函数声明和函数表达式并非是一视同仁的,解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问),至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解析执行。
8.定义一个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]) 。
9.在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);};
10.对作用域上下文和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());
放松一下?
欢迎关注前端开发攻城狮获取更多内容