前端试题本(Javascript篇)

JS1. 下面这个JS程序的输出是什么:
JS2.下面的JS程序输出是什么:
JS3.页面有一个按钮button id为 button1,通过原生的js如何禁用?
JS4.页面有一个按钮button id为 button1,通过原生的js 设置背景色为红色?
JS5.处理a.html文件时,以下哪行伪代码可能导致内存越界或者抛出异常()
JS6.下面的代码结果是
JS7.下面的代码结果是
JS8.考察this(测试环境是浏览器,Node环境中全局对象有所不同)
JS9、var和函数的声明提前
JS10.给基本类型数据添加属性,不报错,但取值时是undefined
JS11、判断一个字符串中出现次数最多的字符,并统计次数
JS12.经典闭包-实现一段脚本,使得点击对应链接alert出相应的编号
JS13.事件处理程序中的this
JS14.请编写一个JavaScript函数 parseQueryString,它的用途是把URL参数解析为一个对象,如: var url =“http://witmax.cn/index.php?key0=0&key1=1&key2=2″
JS15、下面两个函数的返回值是一样的吗?为什么?
JS16、下面的代码会输出什么?为什么?
JS17、解释下面代码的输出
JS18、给你一个 DOM 元素,创建一个能访问该元素所有子元素的函数,并且要将每个子元素传递给指定的回调函数。
JS19下面表达式的结果是:
JS20下列表达式的值
JS21下列表达式的值
JS22.写出下面的输出结果
JS23.Javascript作用链域?
JS24.new操作符具体干了什么呢?
JS25.用js实现千位分隔符?(来源:前端农民工,提示:正则+replace)
JS26.编写一个JavaScript函数,输入指定类型的选择器(仅需支持id,class,tagName三种简单CSS选择器,无需兼容组合选择器)可以返回匹配的DOM节点,需考虑浏览器兼容性和性能。
JS27.实现一个函数clone,可以对JavaScript中的5种主要的数据类型(包括Number、String、Object、Array、Boolean)进行值复制。
JS28.请评价以下代码并给出改进意见
JS29.直接在对象的原型上添加方法是否安全?尤其是在Object对象上。
JS30..在Javascript中什么是伪数组?如何将伪数组转化为标准数组?
JS31.对作用域上下文和this的理解,看下列代码:
JS32.原生JS的window.onload与Jquery的$(document).ready(function(){})有什么不同?如何用原生JS实现Jq的ready方法?
JS32.(设计题)想实现一个对页面某个节点的拖曳?如何做?(使用原生JS)
JS33.说出以下函数的作用是?空白区域应该填写什么?
JS34.看下面的代码?问a怎么变,匿名函数里的this是什么?怎么改变里面的this?匿名函数不能传参怎么改变obj.a的值?
JS35.移除数组中的元素
JS36.进制转换
JS37.关于console.log的小题

JS1. 下面这个JS程序的输出是什么:

  1. function Foo() {
  2. var i = 0;
  3. return function() {
  4. console.log(i++);
  5. }
  6. }
  7. var f1 = Foo(),
  8. f2 = Foo();
  9. f1();
  10. f1();
  11. f2();

解析: 输出为:0,1,0 Foo函数返回一个匿名函数,这个匿名函数对Foo的内部变量 i 存在引用,所以只要指向匿名函数的变量不释放(如 f1),i 的值就一直存在。故每次执行 f1() 都会增加 i 的值。而 f1,f2 指向的是两个不同闭包,所以两者执行时互不影响。


JS2.下面的JS程序输出是什么:

  1. (function() {
  2. var a = b = 5;
  3. })();
  4. console.log(b);
  5. console.log(a);

上面的输出结果是:5,undefined 注意连续赋值,b实际上是全局变量,如果启用了严格模式,代码就会引发ReferenceError的错误:B没有定义(b is not defined)。请记住,严格模式,则需要明确指定,才能实现全局变量声明。比如,你应该写:

  1. (function() {
  2. 'use strict';
  3. var a = window.b = 5;
  4. })();
  5. console.log(b);

JS3.页面有一个按钮button id为 button1,通过原生的js如何禁用?

A. document.getElementById("button1").readolny= true;

B. document.getElementById("button1").setAttribute(“readolny”,”true”);

C. document.getElementById("button1").disabled = true;

D. document.getElementById("button1").setAttribute(“disabled”,”true”);

解析:这题的标准答案是C,毋庸置疑,答案D虽然也能达到预期的效果,但是实际上不管第二个参数为什么值都会值都会使按钮处于禁用状态,及时设置成布尔型的false.所以这个答案存在异议,不算是标准答案


JS4.页面有一个按钮button id为 button1,通过原生的js 设置背景色为红色?

A.document.getElementById('button1').style.backgroundColor="red";

B.document.getElementById('button1').style.backgroundcolor="red";

C.document.getElementById('button1').style.backGroundColor="red";

D.document.getElementById('button1').style.bgcolor="red";

解析:正确答案是A,js里样式设置直接把css写法的的“-”去掉,再改写为驼峰写法即可。


JS5.处理a.html文件时,以下哪行伪代码可能导致内存越界或者抛出异常()

  1. int totalBlank = 0;
  2. int blankNum = 0;
  3. int taglen = page.taglst.size();
  4. A for(int i = 1; i < taglen-1; ++i)
  5. {
  6. //check blank
  7. B while(page.taglst[i] == "<br>" && i < taglen)
  8. {
  9. C ++totalBlank;
  10. D ++i;
  11. }
  12. E if(totalBlank > 10)
  13. F blankNum += totalBlank;
  14. G totalBlank = 0;
  15. }

注意:以下代码中taglen是html文件中存在元素的个数,a.html中taglen的值是15,page.taglst[i]取的是a.html中的元素,例如page.taglst[1]的值是

a.html的文件如下:

  1. <!--这里有一个空行-->
  2. <html>
  3. <title>test</title>
  4. <body>
  5. <div>aaaaaaa</div>
  6. </body>
  7. </html>
  8. <br>
  9. <br>
  10. <br>
  11. <br>
  12. <br>

答案为B,因为while(page.taglst[i] == "<br>" && i < taglen)这个判断,先执行page.taglst[i] == "<br>"这个判断,如果这个判断返回值为true,再执行i < taglen这个判断。当i=taglen的时候,执行page.taglst[i] == "<br>"这个判断就会越界,所以B处,最先出现越界


JS6.下面的代码结果是

  1. var f = function g(){ return 23; };
  2. typeof g();

答案:会发生错误ReferenceError: g is not defined

因为function g(){ return 23; }是函数表达式,事实上只有事一个名字,不是一个函数声明,函数实际上是绑定到变量f,不是g。


JS7.下面的代码结果是

  1. var x = 1;
  2. if (function f(){} ){
  3. x += typeof f;
  4. }
  5. console.log(x);//1undefined

这里有个难点,if 中的 function f(){} 要如何处理?函数声明的实际规则如下:函数声明只能出现在程序或函数体内。从句法上讲,它们 不能出现在Block(块)({ … })中,例如不能出现在 if、while 或 for 语句中。因为 Block(块) 中只能包含Statement语句, 而不能包含函数声明这样的源元素。另一方面,仔细看一看规则也会发现,唯一可能让表达式出现在Block(块)中情形,就是让它作为表达式语句的一部分。但是,规范明确规定了表达式语句不能以关键字function开头。而这实际上就是说,函数表达式同样也不能出现在Statement语句或Block(块)中(因为Block(块)就是由Statement语句构成的)。关于这题的结果的内在原理并不是很懂,仿佛if语句不存在直接执行了里面的语句,又或者因为无法解释就把括号内的一坨直接当成了字符串为真,f还是没有定义。


JS8.考察this(测试环境是浏览器,Node环境中全局对象有所不同)

  1. var length = 10;
  2. function fn() {
  3. console.log(this.length);
  4. }
  5. var obj = {
  6. length: 5,
  7. method: function(fn) {
  8. fn();
  9. arguments[0]();
  10. }
  11. };
  12. obj.method(fn, 1);

正确结果是输出:10 2

解析:牢记->JavaScript中函数调用一共有四种方式:方法调用模式、函数调用模式、构造器调用模式、apply/call调用模式.第一次调用fn()时是普通函数调用模式this指向全局window。我们知道取对象属于除了点操作符还可以用中括号,所以第二次执行时相当于arguments调用方法,this指向arguments,而这里传了两个参数,故输出arguments长度为2。


JS9、var和函数的声明提前

  1. function fn(a) {
  2. console.log(a);
  3. ①var a = 2;
  4. ②function a() {}
  5. console.log(a);
  6. }
  7. fn(1);
  8. 输出:function a() {} 2 //chrome测试

我们知道var和function是会提前声明的,而且函数声明是优先于var声明的(如果同时存在的话),这里的优于可以理解为晚于变量声明后(先提升变量再紧跟着函数声明,函数声明提升相当于整个函数块包括代码一起移动到前面了),如果函数名和变量名相同,函数声明就能覆盖变量声明,所以提前声明后输出的a是个function,然后代码往下执行a进行重新赋值了,故第二次输出是2。即使把第一句和第二句交换一下输出结果不变


JS10.给基本类型数据添加属性,不报错,但取值时是undefined

  1. var a = 10;
  2. a.pro = 10;
  3. console.log(a.pro + a);
  4. var s = 'hello';
  5. s.pro = 'world';
  6. console.log(s.pro + s);

答案:NaN undefinedhello

解析:给基本类型数据加属性不报错,但是引用的话返undefined,undefined+10返回NaN,这里运用的规则是:对于加性操作符,如果一个操作数是树值,另一个操作数是undefined、null、布尔值时,则先调用Number()将非树值转换成数值,undefined会转化成NaN,null转换成0,布尔值转化成1或者0.而undefined和字符串相加时转变成了字符串,所以结果是undefiendhello


JS11、判断一个字符串中出现次数最多的字符,并统计次数

  • hash table方式:
  1. var s = 'aaabbbcccaaabbbaaa';
  2. var obj = {};
  3. var maxn = -1;
  4. var letter;
  5. for(var i = 0; i < s.length; i++) {
  6. if(obj[s[i]]) {
  7. obj[s[i]]++;
  8. if(obj[s[i]] > maxn) {
  9. maxn = obj[s[i]];
  10. letter = s[i];
  11. }
  12. } else {
  13. obj[s[i]] = 1;
  14. if(obj[s[i]] > maxn) {
  15. maxn = obj[s[i]];
  16. letter = s[i];
  17. }
  18. }
  19. }
  20. alert(letter + ': ' + maxn);
  • 正则方式:
  1. var s = 'aaabbbcccaaabbbaaabbbbbbbbbb';
  2. var a = s.split('');
  3. a.sort();
  4. s = a.join('');
  5. var pattern = /(\w)\1*/g;
  6. var ans = s.match(pattern);
  7. ans.sort(function(a, b) {
  8. return a.length < b.length;});;
  9. console.log(ans[0][0] + ': ' + ans[0].length);

JS12.经典闭包-实现一段脚本,使得点击对应链接alert出相应的编号

  • dom污染法:

    <!-- 实现一段脚本,使得点击对应链接alert出相应的编号 -->
  1. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  2. <body>
  3. <a href='#'> 第一个链接 </a> </br>
  4. <a href='#'> 第二个链接 </a> </br>
  5. <a href='#'> 第三个链接 </a> </br>
  6. <a href='#'> 第四个链接 </a> </br>
  7. <script type="text/javascript">
  8. var lis = document.links;
  9. for(var i = 0, length = lis.length; i < length; i++) {
  10. lis[i].index = i+1;
  11. lis[i].onclick = function() {
  12. alert(this.index);
  13. };
  14. }
  15. </script>
  16. </body>
  • 闭包:

    <!-- 实现一段脚本,使得点击对应链接alert出相应的编号 -->
  1. meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  2. <body>
  3. <a href='#'> 第一个链接 </a> </br>
  4. <a href='#'> 第二个链接 </a> </br>
  5. <a href='#'> 第三个链接 </a> </br>
  6. <a href='#'> 第四个链接 </a> </br>
  7. <script type="text/javascript">
  8. var lis = document.links;
  9. for(var i = 0, length = lis.length; i < length; i++) {
  10. (function(i) {
  11. lis[i].onclick = function() {
  12. alert(i + 1);
  13. };
  14. })(i);
  15. }
  16. </script>
  17. </body>

JS13.事件处理程序中的this

  1. function JSClass() {
  2. this.m_Text = 'division element';
  3. this.m_Element = document.createElement('div');
  4. this.m_Element.innerHTML = this.m_Text;
  5. this.m_Element.addEventListener('click', this.func);
  6. // this.m_Element.onclick = this.func;
  7. }
  8. JSClass.prototype.Render = function() {
  9. document.body.appendChild(this.m_Element);
  10. }
  11. JSClass.prototype.func = function() {
  12. alert(this.m_Text);
  13. };
  14. var jc = new JSClass();
  15. jc.Render(); // add div
  16. jc.func(); // 输出 division element

click添加的div元素division element会输出underfined,为什么?

答案:division element undefined

解析:第一次输出很好理解,func()作为对象的方法调用,所以输出division element,点击添加的元素时,this其实已经指向this.m_Element,也就是事件的目标元素(事件对象的currentTarget属性值-或者说是注册事件处理程序的元素),因为是this.m_Element调用的addEventListener函数,所以内部的this全指向它了,而这个元素并没有m_Text属性,所以输出undefined。


JS14.请编写一个JavaScript函数 parseQueryString,它的用途是把URL参数解析为一个对象,如: var url =“http://witmax.cn/index.php?key0=0&key1=1&key2=2

  1. function parseQueryString(url) {
  2. var obj = {};
  3. var a = url.split('?');
  4. if(a.length === 1) return obj;
  5. var b = a[1].split('&');
  6. for(var i = 0, length = b.length; i < length; i++) {
  7. var c = b[i].split('=');
  8. obj[c[0]] = c[1];
  9. }
  10. return obj;
  11. }
  12. ```
  13. 下面给出一个实际常用的更严谨的写法(来自JavaScript高级程序设计):
  14. ```javascript
  15. function getQueryStringArgs() {
  16. //取得查询字符串并去掉开头的问号(这里使用了BOM中的location对象)
  17. var qs=(location.search.length>0?location.search.substring(1):""),
  18. //保存数据的对象
  19. args={},
  20. //取得每一项
  21. items=qs.length?qs.split('&'):[],
  22. item=null,
  23. name=null,
  24. value=null,
  25. i=0,
  26. len=items.length;
  27. for (i = 0; i <len; i++) {
  28. item=items[i].split("=");
  29. name=decodeURIComponent(item[0]);
  30. value=decodeURIComponent(item[1]);
  31. if (name.length) {
  32. args[name]=value;
  33. }
  34. }
  35. return args;
  36. }

还可以借助正则表达式

  1. function getQueryObject(url) {
  2. url = url == null ? window.location.href : url;
  3. var search = url.substring(url.lastIndexOf("?") + 1);
  4. var obj = {};
  5. var reg = /([^?&=]+)=([^?&=]*)/g;
  6. search.replace(reg, function (rs, $1, $2) {
  7. var name = decodeURIComponent($1);
  8. var val = decodeURIComponent($2);
  9. val = String(val);
  10. obj[name] = val;
  11. return rs;
  12. });
  13. return obj;
  14. }

JS15、下面两个函数的返回值是一样的吗?为什么?

  1. function foo1()
  2. {
  3. return {
  4. bar: "hello"
  5. };
  6. }
  7. function foo2()
  8. {
  9. return
  10. {
  11. bar: "hello"
  12. };
  13. }

在编程语言中,基本都是使用分号(;)将语句分隔开,这可以增加代码的可读性和整洁性。而在JS中,如若语句各占独立一行,通常可以省略语句间的分号(;),JS 解析器会根据能否正常编译来决定是否自动填充分号:

  1. var test = 1 +
  2. 2
  3. console.log(test); //3

在上述情况下,为了正确解析代码,就不会自动填充分号了,但是对于 returnbreakcontinue 等语句,如果后面紧跟换行,解析器一定会自动在后面填充分号(;),所以上面的第二个函数就变成了这样:

  1. function foo2()
  2. {
  3. return;
  4. {
  5. bar: "hello"
  6. };
  7. }

所以第二个函数是返回 undefined


JS16、下面的代码会输出什么?为什么?

  1. var arr1 = "john".split('');
  2. var arr2 = arr1.reverse();
  3. var arr3 = "jones".split('');
  4. arr2.push(arr3);
  5. console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1));
  6. console.log("array 2: length=" + arr2.length + " last=" + arr2.slice(-1));

答案是array 1: length=5 last=j,o,n,e,s

array 2: length=5 last=j,o,n,e,s

解析:

  • 数组的reverse方法会将原数组反转后返回,arr1指向反转后的数组
  • JS中当从一个变量向另一个变量复制引用类型的值时,会将存储在变量对象中的值复制一份放到为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响另一个变量,如下图所示:前端试题本(Javascript篇)

    所以arr2和arr1实际上引用的是同一个对象。

JS17、解释下面代码的输出

  1. var a={},
  2. b={key:'b'},
  3. c={key:'c'};
  4. a[b]=123;
  5. a[c]=456;
  6. console.log(a[b]);

输出是 456,参考原文的解释:

javascript中给对象添加和访问属性时会隐式的把属性名转换成字符串,因为b和c都是都是对象,转换成字符串都是[object Object],因此两次实际上是设置了同一属性a[[object Object]]


JS18、给你一个 DOM 元素,创建一个能访问该元素所有子元素的函数,并且要将每个子元素传递给指定的回调函数。

函数接受两个参数:

  • DOM
  • 指定的回调函数

原文利用 深度优先搜索(Depth-First-Search) 给了一个实现:

  1. function Traverse(p_element,p_callback) {
  2. p_callback(p_element);
  3. var list = p_element.children;
  4. for (var i = 0; i < list.length; i++) {
  5. Traverse(list[i],p_callback); // recursive call
  6. }
  7. }

JS19下面表达式的结果是:

[1 < 2 < 3, 3 < 2 < 1]

这个题会让人误以为是 2 > 1 && 2 < 3 其实不是的.

这个解题思路是

  1. 1 < 2 => true;
  2. true < 3 => 1 < 3 => true;
  3. 3 < 2 => false;
  4. false < 1 => 0 < 1 => true;

答案是 [true, true]


JS20下列表达式的值

  1. var lowerCaseOnly = /^[a-z]+$/;
  2. console.log([lowerCaseOnly.test(null), lowerCaseOnly.test()])

执行时会使用toString方法将参数转化成字符串“null”,"undefined",因此都返回true


JS21下列表达式的值

  1. function captureOne(re, str) {
  2. var match = re.exec(str);
  3. return match && match[1];
  4. }
  5. var numRe = /num=(\d+)/ig,
  6. wordRe = /word=(\w+)/i,
  7. a1 = captureOne(numRe, "num=1"),
  8. a2 = captureOne(wordRe, "word=1"),
  9. a3 = captureOne(numRe, "NUM=2"),
  10. a4 = captureOne(wordRe, "WORD=2");
  11. [a1 === a2, a3 === a4]

因为第一个正则有一个 g 选项 它会‘记忆’他所匹配的内容, 等匹配后他会从上次匹配的索引继续, 而第二个正则不会,所以 a1 = '1'; a2 = '1'; a3 = null; a4 = '2'


JS22.写出下面的输出结果

  1. function Foo() {
  2. getName = function () { alert (1); };
  3. return this;
  4. }
  5. Foo.getName = function () { alert (2);};
  6. Foo.prototype.getName = function () { alert (3);};
  7. var getName = function () { alert (4);};//声明提升,但是赋值却留在原地
  8. function getName() { alert (5);}//会整体提升
  9. //请写出以下输出结果:
  10. Foo.getName();//2
  11. getName();//4
  12. Foo().getName();//1
  13. getName();//1
  14. new Foo.getName();//2
  15. new Foo().getName();//3
  16. new new Foo().getName();//3

答案参考:一道常被人轻视的前端JS面试题


JS23.Javascript作用链域?

外部函数无法查看内部函数的内部细节,但内部函数可以查看其外层的函数细节,直至全局细节。当需要从局部函数查找某一属性或方法时,如果当前作用域没有找到,就会上溯到上层作用域查找,直至全局函数,这种组织形式就是作用域链。


JS24.new操作符具体干了什么呢?

1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。

2、属性和方法被加入到 this 引用的对象中。

3、新创建的对象由 this 所引用,并且最后隐式的返回 this 。

  1. var obj = {};
  2. obj.__proto__ = Base.prototype;
  3. Base.call(obj);

JS25.用js实现千位分隔符?(来源:前端农民工,提示:正则+replace)

  1. function commafy(num) {
  2. num = num + '';
  3. var reg = /(-?d+)(d{3})/;
  4. if(reg.test(num)){
  5. num = num.replace(reg, '$1,$2');
  6. }
  7. return num;
  8. }

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

  1. var $= function(selector) {
  2. var reg = /^([#\.])?(\w+)$/ig;
  3. var match = reg.exec(selector);
  4. var result = [];
  5. if (!match) {//如果没有匹配
  6. return;
  7. }else if (match[1]==="#") {//如果是ID
  8. return document.getElementById(match[2]);
  9. }else{//如果是类或者标签选择器
  10. if (typeof document.querySelectorAll==='function') {//如果这个函数存在可以直接处理类和标签选择器
  11. return document.querySelectorAll(match[0]);
  12. }else{//如果上面牛逼的函数没有,则要对类和标签选择器分别处理
  13. if (match[1]) {//如果是类选择器
  14. return getElementsByClassName(match[2]);//自定义的类选择函数
  15. }else{
  16. return getElementsByTagName(match[0]);
  17. }
  18. }
  19. }
  20. };
  21. /*自定义一个通过类名获取元素的函数*/
  22. var getElementsByClassName=document.getElementsByClassName||function(className){
  23. var tags=document.getElementsByTagName("*");
  24. var result=[];
  25. for (var i = 0,len=tags.length; i < len; i++) {
  26. if (tags[i].nodeType===1) {
  27. if (tags[i].className.split(/\s+/).indexOf(className)!==-1) {//因为可能存在多个类名以空格分隔,所以这样处理一下比较符合要求
  28. result.push(tags[i]);
  29. }
  30. }
  31. }
  32. return result;
  33. };

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

  1. /**
  2. * 对象克隆
  3. * 支持基本数据类型及对象
  4. * 递归方法
  5. */
  6. function clone(obj) {
  7. var o;
  8. switch (typeof obj) {
  9. case "undefined":
  10. break;
  11. case "string":
  12. o = obj + "";
  13. break;
  14. case "number":
  15. o = obj - 0;
  16. break;
  17. case "boolean":
  18. o = obj;
  19. break;
  20. case "object": // object 分为两种情况 对象(Object)或数组(Array)
  21. if (obj === null) {
  22. o = null;
  23. } else {
  24. if (Object.prototype.toString.call(obj).indexOf("Array")! == -1) {
  25. o = [];
  26. for (var i = 0; i < obj.length; i++) {
  27. o.push(clone(obj[i]));
  28. }
  29. } else {
  30. o = {};
  31. for (var k in obj) {
  32. o[k] = clone(obj[k]);
  33. }
  34. }
  35. }
  36. break;
  37. default:
  38. o = obj;
  39. break;
  40. }
  41. return o;
  42. }

JS28.请评价以下代码并给出改进意见

  1. if(window.addEventListener){
  2. var addListener = function(el,type,listener,useCapture){
  3. el.addEventListener(type,listener,useCapture);
  4. };
  5. }
  6. else if(document.all){
  7. addListener = function(el,type,listener){
  8. el.attachEvent("on"+type,function(){
  9. listener.apply(el);
  10. });
  11. }
  12. }

上述代码的本意是定义一个跨浏览器兼容的事件监听程序,主要有三个问题:一是addListener函数不应该在条件语句里声明和赋值,这样做封装性和移植性很不好;二是没有考虑DOM0级的兼容性;三是使用的能力检测不是很规范。改进如下:

  1. var addListener=function(el,type,listener,useCapture){
  2. if (el.addEventListener) {
  3. el.addEventListener(type,handle,useCapture);
  4. }else if(el.attachEvent){
  5. el.attachEvent("on"+type,listener);
  6. }else{
  7. el["on"+type]=listener;
  8. }
  9. }

JS29.直接在对象的原型上添加方法是否安全?尤其是在Object对象上。

既然都这样问了,肯定是不安全的

  • 容易造成全局污染,和其他库冲突
  • 出了Bug不太好定位问题
  • 有可能出现代码向上不兼容的情况,比如定义了一个Object.prototype.clone。万一ES7、ES8也定义了这个函数,那旧代码不就会出现问题了嘛!
  • 在原型上添加方法会被所有实例共享
  • for…in..会列举出添加在原型的方法

    应该根据实际情况确定是否可以实施,或者在添加之前确定是否该方法是否存在


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

伪数组(类数组):无法直接调用数组方法或期望length属性有什么特殊的行为,但仍可以对真正数组遍历方法来遍历它们。典型的是函数的argument参数,还有像调用getElementsByTagName,document.childNodes之类的,它们都返回NodeList对象都属于伪数组。可以使用Array.prototype.slice.call(fakeArray)将数组转化为真正的Array对象。要注意的是在IE8及以前,NodeList实现为一个COM对象,要想将其转换层数组,必须手动枚举所有成员。下面的实现兼容性较强。

  1. function converToArray(fakeArray){
  2. var array = null;
  3. try{
  4. array = Array.prototype.slice.call(fakeArray,0);
  5. }catch(ex){
  6. array = new Array();
  7. for( var i = 0 ,len = nodes.length; i < len ; i++ ) {
  8. array.push(fakeArray[i])
  9. }
  10. }
  11. return array;
  12. }

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

  1. var User = {
  2. count: 1,
  3. getCount: function() {
  4. return this.count;
  5. }
  6. };
  7. console.log(User.getCount()); // what?
  8. var func = User.getCount;
  9. console.log(func()); // what?

问两处 console 输出什么?为什么?

答案是 1 和 undefined。

func 是在 winodw 的上下文中被执行的,所以会访问不到 count 属性。那么问题来了,如何确保Uesr总是能访问到func的上下文,即正确返回1。答案:正确的方法是使用Function.prototype.bind。兼容各个浏览器完整代码如下:

  1. Function.prototype.bind = Function.prototype.bind || function(context) {
  2. var self = this;
  3. return function() {
  4. return self.apply(context, arguments);
  5. };
  6. }
  7. var func = User.getCount.bind(User);
  8. console.log(func())

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

  • window.onload()方法是必须等到页面内包括图片、CSS文件、JS文件等所有资源加载完毕后才能执行。
  • (document).ready()是DOM结构绘制完毕后就执行,不必等到资源加载完毕。另外还有一点不同的是,window.onload只能绑定一个事件处理程序," role="presentation" style="position: relative;">(document).ready()是DOM结构绘制完毕后就执行,不必等到资源加载完毕。另外还有一点不同的是,window.onload只能绑定一个事件处理程序,(document).ready()是DOM结构绘制完毕后就执行,不必等到资源加载完毕。另外还有一点不同的是,window.onload只能绑定一个事件处理程序,document.ready()可以多次调用,添加多个事件处理程序
  1. document.ready=(function (){
  2. var fns = []; //为了支持多个事件处理函数,将添加的事件处理函数缓存在数组中
  3. //执行所有缓存的事件处理函数
  4. var run = function(){
  5. for (var i = 0; i < fns.length; i++){
  6. fns[i]();
  7. }
  8. } ;
  9. if (document.addEventListener) {//标准浏览器
  10. document.addEventListener("DOMContentLoaded",function(){
  11. // 注销事件,避免反复触发
  12. document.removeEventListener("DOMContentLoaded",arguments.callee,false);
  13. run();//执行函数
  14. },false);
  15. }else if (document.attachEvent) {//IE浏览器
  16. document.attachEvent('onreadystatechange',function(){
  17. if (document.readyState=="interactive" || document.readyState=="complete") {
  18. document.detachEvent("onreadystatechange",arguments.callee);
  19. run();
  20. }
  21. });
  22. }
  23. //返回添加事件处理程序到缓存数组的函数,利用了闭包的特性
  24. return function(fn){
  25. fns.push(fn);
  26. }
  27. })();
  28. //下面是测试代码
  29. document.ready(function(){
  30. alert("1");
  31. });
  32. document.ready(function(){
  33. alert("2");
  34. });

参考:

Running code when the document is ready

《JavaScript高级程序设计-第三版》P390页


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

万能的红宝书《JavaScript高级程序设计》在第16章介绍了HTML5原生支持的拖放,在第22章也实现了不需要HTML5特性支持的拖放,我们可以直接参考。

如果浏览器支持HTML5,为了使页面元素具有拖放功能,只需要设置元素的draggable="true"即可。下面主要介绍在不支持HTML5情况下的原生拖放。

  • 设置需要拖拽的节点绝对定位
  • 给需要拖拽的节点绑定mousedown, mousemove, mouseup事件
  • mousedown事件触发后,开始拖拽
  • mousemove时,需要通过event.clientX和clientY获取拖拽位置,并实时更新位置
  • mouseup时,拖拽结束
  1. var DragDrop=function () {
  2. var dragging=null,
  3. diffx=0,
  4. diffy=0;
  5. function handleEvent(event){
  6. event=event||window.event;
  7. var target=event.target||event.srcElement;
  8. switch(event.type){
  9. case "mousedown":
  10. //如果该元素添加了draggable类,就认为其可以拖拽
  11. if (target.className.indexOf("draggable")>-1) {
  12. dragging=target;
  13. diffx=event.clientX-target.offsetLeft;
  14. diffy=event.clientY-target.offsetTop;
  15. }
  16. break;
  17. case "mousemove":
  18. if (dragging!==null) {
  19. dragging.style.left=(event.clientX-diffx)+"px";
  20. dragging.style.top=(event.clientY-diffy)+"px";
  21. }
  22. break;
  23. case "mouseup":
  24. dragging=null;
  25. break;
  26. }
  27. }
  28. return{
  29. enable:function(){//这里偷懒未考虑事件监听的兼容性
  30. document.addEventListener("mousedown",handleEvent,false);
  31. document.addEventListener("mousemove",handleEvent,false);
  32. document.addEventListener("mouseup",handleEvent,false);
  33. },
  34. disable:function(){
  35. document.removeEventListener("mousedown",handleEvent,false);
  36. document.removeEventListener("mousemove",handleEvent,false);
  37. document.removeEventListener("mouseup",handleEvent,false);
  38. }
  39. }
  40. }();

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

  1. //define
  2. (function(window) {
  3. function fn(str) {
  4. this.str = str;
  5. }
  6. fn.prototype.format = function() {
  7. var arg = ______;
  8. return this.str.replace(_____, function(a, b) {
  9. return arg[b] || "";
  10. });
  11. }
  12. window.fn = fn;
  13. })(window);
  14. //use
  15. (function() {
  16. var t = new fn('<p><a href="{0}">{1}</a><span>{2}</span></p>');
  17. console.log(t.format('http://www.alibaba.com', 'Alibaba', 'Welcome'));
  18. })();

答案:访函数的作用是使用format函数将函数的参数替换掉{0}这样的内容,返回一个格式化后的结果:

第一个空是:arguments

第二个空是:/\{(\d+)\}/ig


JS34.看下面的代码?问a怎么变,匿名函数里的this是什么?怎么改变里面的this?匿名函数不能传参怎么改变obj.a的值?

  1. var obj = {
  2. a : 1,
  3. func : function () {
  4. (function () { this.a = 2; }).call(this);
  5. }
  6. };
  7. obj.func();

obj.a不变,匿名函数里的this指向全局对象(window),相当于给window加了一个名为a的属性。

解决方案

  • 使用call或者apply方法
  1. var obj = {
  2. a : 1,
  3. func : function () {
  4. (function () { this.a = 2; }).call(this);
  5. }
  6. };
  7. obj.func();
  8. console.log(obj.a);
  • 保存外部环境的上下文在内部使用
  1. var obj = {
  2. a : 1,
  3. func : function () {
  4. var self=this;
  5. (function () { self.a = 2; })();
  6. }
  7. };
  8. obj.func();
  9. console.log(obj.a);

JS35.移除数组中的元素

  • 返回新数组
  1. function remove(arr, item) {
  2. return arr.filter(function(x) {
  3. return x !== item;
  4. });
  5. }
  1. function remove(arr, item) {
  2. var newArr = [];
  3. arr.forEach(function(e) {
  4. if(e !== item) {
  5. newArr.push(e);
  6. }
  7. })
  8. return newArr;
  9. }
  • 直接在原数组上操作
  1. function removeWithoutCopy(arr, item) {
  2. for(var i = 0; i < arr.length; i++) {
  3. if(item === arr[i]) {
  4. arr.splice(i, 1);
  5. i--; // for循环逆序的话,i--可以省略
  6. }
  7. }
  8. return arr;
  9. }
  1. function removeWithoutCopy(arr, item) {
  2. var pos = arr.indexOf(item);
  3. while(pos !== -1) {
  4. arr.splice(pos, 1);
  5. pos = arr.indexOf(item);
  6. }
  7. return arr;
  8. }

JS36.进制转换

十进制转换成其他进制

  1. /**
  2. * [convert description]
  3. * @param {[number]} num [description]
  4. * @param {[number]} base [转换后的基数]
  5. * @return {[type]} [description]
  6. */
  7. function convert(num, base) {
  8. return num.toString(base);
  9. }
  10. console.log(convert(16,2))//10000

其他进制转化成十进制

  1. function convertFromD(num,base) {
  2. return parseInt(num,base);
  3. }
  4. console.log(convertFromD(11000,2))//24

JS37.关于console.log的小题

1.下面的表达式在浏览器中的结果是console还是window

  1. console.log.apply(console,this);

解析:笔试时居然脑子短路判成了console,首先这里传入参数this时,这个this指向Window无疑,这里的this是实参,在console.log函数中打印的就是这个Window,并没有因为执行环境上下文是console而覆盖参数。本来单独执行console.log(),log方法就是作为console对象的方法来调用,现在传进去的环境还是console.log,上面的语句实际上就是console.log(this)

2.输出的字符串前统一加上(app) 这样的字符串(考察arguments->args;apply)

  1. function log(){
  2. var args = Array.prototype.slice.call(arguments);
  3. args.unshift('(app)');
  4. console.log.apply(console, args);
  5. };

参考:

(由于很多题都是临时收集,很多没注意出处,请谅解)

你有必要知道的 25 个 JavaScript 面试题

javascript-puzzlers

BAT及各大互联网公司2014前端笔试面试题–JavaScript篇

上一篇:PAT甲级题解-1100. Mars Numbers (20)-字符串处理


下一篇:PHP自定义函数使用外部变量