Mr. Panda
Tech For Fun

前端面试题集锦(6)

temp-or8

41. 解释下 JavaScript 中 this 是如何工作的。

 

this 永远指向函数运行时所在的对象(所处的运行环境[对象]),而不是函数被创建时所在的对象(运行时而非创建时)。匿名函数或不处于任何对象中的函数指向 window 。

理解:this指向函数运行时所属的对象,通俗的说就是谁点出来的。(window.fuc()或者说Obj.func())。

1. 如果是 call,apply,with,指定的 this 是谁,就是谁。 (把this传给了谁?)

2. 普通的函数调用,函数被谁调用,this 就是谁。

参阅:JavaScript 的 this 原理--阮一峰

实例:

var obj = {
  foo: function () { console.log(this.bar) },
  bar: 1
};

var foo = obj.foo;
var bar = 2;

obj.foo() // 1
foo() // 2

42. 解释下原型继承的原理。

 

以下代码展示了 JS 引擎如何查找属性:

1 function getProperty(obj,prop) {
2     if (obj.hasOwnProperty(prop)) {
3         return obj[prop];
4     } else if (obj.__proto__!==null) {
5         return getProperty(obj.__proto__,prop);
6     } else {
7         return undefined;
8     }
9 }

 

 

 

下图展示的原(prototype)的关联:

实例:

//Animal构造函数
function Animal(name){
    this.name = name;
}
//Animal原型对象
Animal.prototype = {
    id:"Animal",
    sleep:function(){
        alert("sleep");
    }
}

function Human(name,age){
    Animal.call(this,name);
    this.age = age;
}

Human.prototype = new Animal();
Human.prototype.id = "Human";

Human.prototype.say = function(){
    alert("hello everyone,My name is "+this.name +",I'm "+this.age+" and I'm a "+this.id);
}

//Human相关调用
var jxl = new Human('笨蛋',25);
alert(jxl.name);//笨蛋
alert(jxl.id);//Human
jxl.say();//hello everyone,My name is 笨蛋,I'm 25 and I'm a Human

alert(Animal.prototype.isPrototypeOf(jxl));//true
alert(Object.prototype.isPrototypeOf(jxl));//true

Human.prototype.__proto__ = Animal.constructor

规律总结:

  1. 实例只有__proto__,类有__proto__和prototype,原型有__proto__和constructor。
  2. 实例的__proto__指向Class.prototype。
  3. 类的__proto__永远指向Function.prototype,类的prototype指向Class.prototype(类是Function的实例)。
  4. 原型的__proto__永远指向Object.prototype,原型的constructor永远指向对应的类(原型是Object的实例)。
  5. Object.prototype.__proto__永远指向null。

temp-or8

prototype

 


43. 你是如何测试 JavaScript 代码的?

 

结合自己的项目经验进行讲述。(chrome 使用技巧


44.AMD vs.CommonJS?

 

请看文章 JavaScript
之模块化编程


45. 什么是哈希表?

 

散列表(也叫哈希表),是根据关键码值直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。


46. 解释下为什么接下来这段代码不是 IIFE(立即调用的函数表达式):function foo(){}();.

 

要做哪些改动使它变成 IIFE?

因为在解析器解析全局的 function 或者 function 内部 function 关键字的时候,默认是认为 function 声明,而不是
function 表达式,如果你不显示告诉编译器,它默认会声明成一个缺少名字的 function,并且抛出一个语法错误信息,因为 function
声明需要一个名字。

1 var foo = function() {
2     // doSomeThing.
3 };
4 
5 foo();

47. 描述以下变量的区别:null,undefined 或 undeclared?

 

JavaScript 的最初版本是这样区分的:null 是一个表示 "无" 的对象,转为数值时为 0;undefined 是一个表示 "无"
的原始值,转为数值时为 NaN。

但是,上面这样的区分,在实践中很快就被证明不可行。目前,null 和 undefined 基本是同义的,只有一些细微的差别。

null 表示 "没有对象",即该处不应该有值。典型用法是:

 

  • 用来初始化一个变量,这个变量可能被赋值为一个对象。
  • 用来和一个已经初始化的变量比较,这个变量可以是也可以不是一个对象。
  • 当函数的参数期望是对象时,被用作参数传入。
  • 当函数的返回值期望是对象时,被用作返回值传出。
  • 作为对象原型链的终点。
  • undefined 表示 "缺少值",就是此处应该有一个值,但是还没有定义。典型用法是:
  • 变量被声明了,但没有赋值时,就等于 undefined。
  • 调用函数时,应该提供的参数没有提供,该参数等于 undefined。
  • 对象没有赋值的属性,该属性的值为 undefined。
  • 函数没有返回值时,默认返回 undefined。
  • 该如何检测它们?

    null:表示无值;undefined:表示一个未声明的变量,或已声明但没有赋值的变量,或一个并不存在的对象属性。

    == 运算符将两者看作相等。如果要区分两者,要使用 === 或 typeof 运算符。

    以下是不正确的用法:

    1 var exp = undefined;
    2 
    3 if (exp == undefined) {
    4     alert("undefined");
    5 }
    

    exp 为 null 时,也会得到与 undefined 相同的结果,虽然 null 和 undefined 不一样。注意:要同时判断 undefined
    和 null 时可使用本法。

    typeof
    返回的是字符串,有六种可能:"number"、"string"、"boolean"、"object"、"function"、"undefined"。

    以下是正确的用法:

    1 var exp = undefined;
    2 
    3 if(typeof(exp) == undefined) {
    4     alert("undefined");
    5 }
    

    JS 中如何判断 null?

    以下是不正确的用法:

    1 var exp = null;
    2 
    3 if(exp == null) {
    4     alert("is null");
    5 }
    

    exp 为 undefined 时,也会得到与 null 相同的结果,虽然 null 和 undefined 不一样。注意:要同时判断 null 和
    undefined 时可使用本法。

    1 var exp=null;
    2 
    3 if(!exp) {
    4     alert("is null");
    5 }
    

    如果 exp 为 undefined 或者数字零,也会得到与 null 相同的结果,虽然 null 和二者不一样。注意:要同时判断
    null、undefined 和数字零时可使用本法。

    1 var exp = null;
    2 
    3 if(typeof(exp) == "null") {
    4     alert("is null");
    5 }
    

    为了向下兼容,exp 为 null 时,typeof 总返回 object。这种方式也不太好。

    以下是正确的用法:

    1 var exp = null;
    2 
    3 if(!exp&&typeof(exp) != "undefined" && exp != 0) {
    4     alert("is null");
    5 }
    

    48. 什么是闭包,如何使用它,为什么要使用它?

     

    包就是能够读取其他函数内部变量的函数。由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成
    “定义在一个函数内部的函数”。

    所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

    使用闭包的注意点:

  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在 IE
    中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

 

 

  • 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public
    Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

 

(关于闭包,详细了解请看 JavaScript 之作用域与闭包详解


49. 请举出一个匿名函数的典型用例?

 

自执行函数IIFE(立即调用的函数表达式),用闭包模拟私有变量、特权函数等。


50. 解释 “JavaScript 模块模式” 以及你在何时使用它。

如果有提到无污染的命名空间,可以考虑加分。

如果你的模块没有自己的命名空间会怎么样?

 

请看文章 -JavaScript
之模块化编程
JavaScript
之命名空间模式 浅析


51. 你是如何组织自己的代码?是使用模块模式,还是使用经典继承的方法?

 

请看文章 -JavaScript
之模块化编程
Javascript
之对象的继承


52. 请指出 JavaScript 宿主对象和原生对象的区别?

 
原生对象

ECMA-262 把本地对象(native object)定义为 “独立于宿主环境的 ECMAScript 实现提供的对象”。

“本地对象”
包含哪些内容:Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError。

由此可以看出,简单来说,本地对象就是 ECMA-262 定义的类(引用类型)。

内置对象

ECMA-262 把内置对象(built-in object)定义为 “由 ECMAScript 实现提供的、独立于宿主环境的所有对象,在
ECMAScript 程序开始执行时出现”。这意味着开发者不必明确实例化内置对象,它已被实例化了。

同样是 “独立于宿主环境”。根据定义我们似乎很难分清“内置对象” 与“本地对象”的区别。而 ECMA-262 只定义了两个内置对象,即 Global 和
Math (它们也是本地对象,根据定义,每个内置对象都是本地对象)。如此就可以理解了。内置对象是本地对象的一种。

宿主对象

何为 “宿主对象”?主要在这个“宿主” 的概念上,ECMAScript 中的 “宿主” 当然就是我们网页的运行环境,即 “操作系统” 和“浏览器”。

所有非本地对象都是宿主对象(host object),即由 ECMAScript 实现的宿主环境提供的对象。所有的 BOM 和 DOM
都是宿主对象。
因为其对于不同的 “宿主” 环境所展示的内容不同。其实说白了就是,ECMAScript
官方未定义的对象都属于宿主对象,因为其未定义的对象大多数是自己通过 ECMAScript 程序创建的对象。


53.call 和. apply 的区别是什么?

 

call 方法:

语法:call(thisObj,Object)
定义:调用一个对象的一个方法,以另一个对象替换当前对象。
说明:call
方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。 如果没有提供
thisObj 参数,那么 Global 对象被用作 thisObj。
apply 方法

语法:apply(thisObj,[argArray])
定义:应用某一对象的一个方法,用另一个对象替换当前对象。
说明:如果
argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。如果没有提供 argArray 和 thisObj
任何一个参数,那么 Global 对象将被用作 thisObj, 并且无法被传递任何参数。

对于 apply 和 call
两者在作用上是相同的,但两者在参数上有以下区别
对于第一个参数意义都一样,但对第二个参数:apply
传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而 call 则作为 call 的参数传入(从第二个参数开始)。如
func.call(func1,var1,var2,var3) 对应的 apply 写法为:func.apply(func1,[var1,var2,var3])
同时使用 apply 的好处是可以直接将当前函数的 arguments 对象作为 apply 的第二个参数传入。


54. 请解释 Function.prototype.bind 的作用?


55. 你何时优化自己的代码?

 

请看文章 JavaScript
之高效编程
JavaScript
编码风格指南


56. 你能解释一下 JavaScript 中的继承是如何工作的吗?

 

原型链等。


57. 在什么时候你会使用 document.write()?

 

大多数生成的广告代码依旧使用 document.write(),虽然这种用法会让人很不爽。


58. 请指出浏览器特性检测,特性推断和浏览器 UA 字符串嗅探的区别?

 

特性检测:为特定浏览器的特性进行测试,并仅当特性存在时即可应用特性。

User-Agent 检测:最早的浏览器嗅探即用户代理检测,服务端(以及后来的客户端)根据 UA 字符串屏蔽某些特定的浏览器查看网站内容。

特性推断:尝试使用多个特性但仅验证了其中之一。根据一个特性的存在推断另一个特性是否存在。问题是,推断是假设并非事实,而且可能导致可维护性的问题。


59. 请尽可能详尽的解释 AJAX 的工作原理。

 

请参考文章 AJAX
工作原理


60. 请解释 JSONP 的工作原理,以及它为什么不是真正的 AJAX。

 

JSONP (JSON with Padding) 是一个简单高效的跨域方式,HTML 中的 script 标签可以加载并执行其他域的
javascript,于是我们可以通过 script 标记来动态加载其他域的资源。例如我要从域 A 的页面 pageA 加载域 B 的数据,那么在域 B 的页面
pageB 中我以 JavaScript 的形式声明 pageA 需要的数据,然后在 pageA 中用 script 标签把 pageB 加载进来,那么
pageB 中的脚本就会得以执行。JSONP 在此基础上加入了回调函数,pageB 加载完之后会执行 pageA
中定义的函数,所需要的数据会以参数的形式传递给该函数。JSONP
易于实现,但是也会存在一些安全隐患,如果第三方的脚本随意地执行,那么它就可以篡改页面内容,截获敏感数据。但是在受信任的双方传递数据,JSONP
是非常合适的选择。

AJAX 是不跨域的,而 JSONP 是一个是跨域的,还有就是二者接收参数形式不一样!


61. 你使用过 JavaScript 模板系统吗?

 

如有使用过,请谈谈你都使用过哪些库,比如 Mustache.js,Handlebars 等等。


62. 请解释变量声明提升。

 

在 JS 里定义的变量,存在于作用域链里,而在函数执行时会先把变量的声明进行提升,仅仅是把声明进行了提升,而其值的定义还在原来位置。示例如下:

 

1 var test = function() {
2     console.log(name); // 输出:undefined
3     var name = "jeri";
4     console.log(name); // 输出:jeri
5 }
6 
7 test();

 

 

 

上述代码与下述代码等价。

 

1 var test = function() {
2     var name;
3     console.log(name); // 输出:undefined
4     name = "jeri";
5     console.log(name); // 输出:jeri
6 }
7 
8 test();

 

 

 

由以上代码可知,在函数执行时,把变量的声明提升到了函数顶部,而其值定义依然在原来位置。


63. 请描述下事件冒泡机制。

 

冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标 (document 对象) 的顺序触发。

捕获型事件:事件从最不精确的对象 (document 对象) 开始触发,然后到最精确(也可以在窗口级别捕获事件,不过必须由开发人员特别指定)。

支持 W3C 标准的浏览器在添加事件时用 addEventListener(event,fn,useCapture)方法,基中第 3 个参数
useCapture 是一个 Boolean 值,用来设置事件是在事件捕获时执行,还是事件冒泡时执行。而不兼容 W3C 的浏览器 (IE) 用
attachEvent()方法,此方法没有相关设置,不过 IE 的事件模型默认是在事件冒泡时执行的,也就是在 useCapture 等于 false
的时候执行,所以把在处理事件时把 useCapture 设置为 false 是比较安全,也实现兼容浏览器的效果。


64."attribute" 和 "property" 的区别是什么?

1. 定义

Property:属性,所有的 HTML 元素都由 HTMLElement 类型表示,HTMLElement
类型直接继承自 Element 并添加了一些属性,添加的这些属性分别对应于每个 HTML 元素都有下面的这 5 个标准特性:
id,title,lang,dir,className。DOM 节点是一个对象,因此,他可以和其他的 JavaScript
对象一样添加自定义的属性以及方法。property 的值可以是任何的数据类型,对大小写敏感,自定义的 property 不会出现在 html 代码中,只存在
js 中。

Attribute:特性,区别于 property,attribute 只能是字符串,大小写不敏感,出现在
innerHTML 中,通过类数组 attributes 可以罗列所有的 attribute。

2. 相同之处

标准的 DOM properties 与 attributes 是同步的。公认的(非自定义的)特性会被以属性的形式添加到 DOM
对象中。如,id,align,style 等,这时候操作 property 或者使用操作特性的 DOM 方法如 getAttribute()
都可以操作属性。不过传递给 getAttribute() 的特性名与实际的特性名相同。因此对于 class 的特性值获取的时候要传入 “class”。

3. 不同之处

1). 对于有些标准的特性的操作,getAttribute 与点号 (.) 获取的值存在差异性。如
href,src,value,style,onclick 等事件处理程序。
2).href:getAttribute 获取的是 href
的实际值,而点号获取的是完整的 url,存在浏览器差异。


65. 为什么扩展 JavaScript 内置对象不是好的做法?


66. 为什么扩展 JavaScript 内置对象是好的做法?

 

 


67. 请指出 document.onload 和 document.ready 两个事件的区别。

 

页面加载完成有两种事件,一是 ready,表示文档结构已经加载完成(不包含图片等非文字媒体文件),二是
onload,指示页面包含图片等文件在内的所有元素都加载完成。


68.== 和 === 有什么不同?

 

首先,== equality 等同,=== identity 恒等。 ==, 两边值类型不同的时候,要先进行类型转换,再比较。
===,不做类型转换,类型不同的一定不等。

先说 ===,这个比较简单。下面的规则用来判断两个值是否 === 相等:

 

  • 如果类型不同,就 [不相等]
  • 如果两个都是数值,并且是同一个值,那么 [相等];(!例外) 的是,如果其中至少一个是 NaN,那么 [不相等]。(判断一个值是否是 NaN,只能用
    isNaN() 来判断)

 

 

  • 如果两个都是字符串,每个位置的字符都一样,那么 [相等];否则 [不相等]。
  • 如果两个值都是 true,或者都是 false,那么 [相等]。
  • 如果两个值都引用同一个对象或函数,那么 [相等];否则 [不相等]。
  • 如果两个值都是 null,或者都是 undefined,那么 [相等]。
  • 再说 ==,根据以下规则:
  • 如果两个值类型相同,进行 === 比较。
  • 如果两个值类型不同,他们可能相等。根据下面规则进行类型转换再比较:
  • 如果一个是 null、一个是 undefined,那么 [相等]。
  • 如果一个是字符串,一个是数值,把字符串转换成数值再进行比较。
  • 如果任一值是 true,把它转换成 1 再比较;如果任一值是 false,把它转换成 0 再比较。
  • 如果一个是对象,另一个是数值或字符串,把对象转换成基础类型的值再比较。对象转换成基础类型,利用它的 toString 或者 valueOf 方法。js
    核心内置类,会尝试 valueOf 先于 toString;例外的是 Date,Date 利用的是 toString 转换。非 js
    核心的对象,令说(比较麻烦,我也不大懂)

 

 

  • 任何其他组合,都 [不相等]。

 


69. 你如何从浏览器的 URL 中获取查询字符串参数。

 

以下函数把获取一个 key 的参数。

 

 1 function parseQueryString ( name ){
 2     name = name.replace(/[\[]/,"\\\[");
 3     var regexS = "[\\?&]"+name+"=([^&#]*)";
 4     var regex = new RegExp( regexS );
 5     var results = regex.exec( window.location.href );
 6 
 7     if(results == null) {
 8         return "";
 9     } else {
10     return results[1];
11     }
12 }

 

 

 


70. 请解释一下 JavaScript 的同源策略。

 

在客户端编程语言中,如 javascript 和
ActionScript,同源策略是一个很重要的安全理念,它在保证数据的安全性方面有着重要的意义。同源策略规定跨域之间的脚本是隔离的,一个域的脚本不能访问和操作另外一个域的绝大部分属性和方法。那么什么叫相同域,什么叫不同的域呢?当两个域具有相同的协议,
相同的端口,相同的 host,那么我们就可以认为它们是相同的域。同源策略还应该对一些特殊情况做处理,比如限制 file 协议下脚本的访问权限。本地的 HTML
文件在浏览器中是通过 file 协议打开的,如果脚本能通过 file 协议访问到硬盘上其它任意文件,就会出现安全隐患,目前 IE8 还有这样的隐患。


71. 请描述一下 JavaScript 的继承模式。

 

关于继承请看文章 JavaScript
之对象的继承


72. 如何实现下列代码:[1,2,3,4,5].duplicator();//[1,2,3,4,5,1,2,3,4,5]。


73. 描述一种 JavaScript 中实现 memoization(避免重复运算) 的策略。


74. 什么是三元表达式?“三元” 表示什么意思?

三元表达式:? :三元 -- 三个操作对象

在表达式 boolean-exp ? value0 : value1 中,如果 “布尔表达式” 的结果为 true,就计算
“value0”,而且这个计算结果也就是操作符最终产生的值。如果“布尔表达式” 的结果为
false,就计算“value1”,同样,它的结果也就成为了操作符最终产生的值。


75.JavaScript 里函数参数 arguments 是数组吗?

 

在函数代码中,使用特殊对象 arguments,开发者无需明确指出参数名,通过使用下标就可以访问相应的参数。

arguments 虽然有一些数组的性质,但其并非真正的数组,只是一个类数组对象。其并没有数组的很多方法,不能像真正的数组那样调用.
jion(),.concat(),.pop() 等方法。


76. 什么是 "use strict";? 使用它的好处和坏处分别是什么?

 

在代码中出现表达式 -"use strict"; 意味着代码按照严格模式解析,这种模式使得 Javascript
在更严格的条件下运行。

好处:

 

  • 消除 Javascript 语法的一些不合理、不严谨之处,减少一些怪异行为;
  • 消除代码运行的一些不安全之处,保证代码运行的安全;
  • 提高编译器效率,增加运行速度;
  • 为未来新版本的 Javascript 做好铺垫。
  • 坏处:

    同样的代码,在 "严格模式" 中,可能会有不一样的运行结果;一些在 "正常模式" 下可以运行的语句,在 "严格模式" 下将不能运行。


    77. 解释 "chaining"。

     

    jQuery 方法链接。直到现在,我们都是一次写一条 jQuery
    语句(一条接着另一条)。不过,有一种名为链接(chaining)的技术,允许我们在相同的元素上运行多条 jQuery 命令,一条接着另一条。

    提示:这样的话,浏览器就不必多次查找相同的元素。

    如需链接一个动作,您只需简单地把该动作追加到之前的动作上。


    78. 解释 "deferreds"。

     

    开发网站的过程中,我们经常遇到某些耗时很长的 javascript 操作。其中,既有异步的操作(比如 ajax
    读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们都不是立即能得到结果的。

    通常的做法是,为它们指定回调函数(callback)。即事先规定,一旦它们运行结束,应该调用哪些函数。

    但是,在回调函数方面,jQuery 的功能非常弱。为了改变这一点,jQuery 开发团队就设计了 deferred 对象。

    简单说,deferred 对象就是 jQuery 的回调函数解决方案。在英语中,defer 的意思是 "延迟",所以 deferred 对象的含义就是
    "延迟" 到未来某个点再执行。


    79. 你知道哪些针对 jQuery 的优化方法?

  • 1. 总是从 ID 选择器开始继承在 jQuery 中最快的选择器是 ID 选择器,因为它直接来自于 JavaScript 的
    getElementById() 方法。
  • 例如有一段 HTML 代码:

     

     1 <div>
     2     <form method="post"action="#">
     3         <h2>交通信号灯</h2>
     4         <ul>
     5             <li>
     6                 <input type="radio"class="on"name="light"value="red"/>红色
     7             </li>
     8             <li>
     9                 <input type="radio"class="off"name="light"value="yellow"/>黄色
    10             </li>
    11             <li>
    12                 <input type="radio"class="off"name="light"value="green"/>绿色
    13             </li>
    14         </ul>
    15         <inputid="traffic_button"type="submit"value="Go"/>
    16     </form>
    17 </div>
    

     

     

     

    比如需要选择红绿单选框,那么可以使用一个 tag name 来限制 (修饰)class,如下所示:var
    active_light=$(“input.on”); 当然也可以结合就近的 ID,如下所示:var
    active_light=$(“#traffic_light input.on”);  如果采用下面的选择器,那么效率是低效的。var
    traffic_button=$(“#content.button”); 因为 button 已经有 ID 了,我们可以直接使用 ID 选择器。如下所示:var
    traffic_button=$(“#traffic_button”); 当然这只是对于单一的元素来讲。如果你需要选择多个元素,这必然会涉及到 DOM
    遍历和循环,为了提高性能,建议从最近的 ID 开始继承。如下所示:var traffic_lights=$(“#traffic_light input”);

     

  • 2. 在 class 前使用 tag(标签名) 在 jQuery 中第二快的选择器是 tag(标签) 选择器
    (比如:$(“head”))。
  • 跟 ID 选择器累时,因为它来自原生的 getElementsByTagName() 方法。继续看刚才那段 HTML 代码:

    在使用 tag 来修饰 class 的时候,我们需要注意以下几点:(1)不要使用 tag 来修饰 ID,如下所示:var
    content=$(“div#content”); 这样一来,选择器会先遍历所有的 div 元素,然后匹配 #content。(好像 jQuery 从
    1.3.1 开始改变了选择器核心后,不存在这个问题了。暂时无法考证。)(2)不要画蛇添足的使用 ID 来修饰 ID,如下所示:var
    traffic_light=$(“#content#traffic_light”);

     

  • 3. 将 jQuery 对象缓存起来把 jQuery 对象缓存起来就是要告诉我们要养成将 jQuery 对象缓存进变量的习惯。
  • 下面是一个 jQuery 新手写的一段代码:
    1 $("#traffic_light input.on").bind("click",function(){});
    2 $("#traffic_light input.on").css("border","1px dashed yellow");
    3 $("#traffic_light input.on").css("background-color","orange");
    4 $("#traffic_light input.on").fadeIn("slow");
    

    但切记不要这么做。我们应该先将对象缓存进一个变量然后再操作,如下所示:

    记住,永远不要让相同的选择器在你的代码里出现多次. 注:(1)为了区分普通的 JavaScript 对象和 jQuery 对象,可以在变量首字母前加上 $
    符号。(2)上面代码可以使用 jQuery 的链式操作加以改善。如下所示:

     

    1 var $active_light = $("#traffic_light input.on");
    2 
    3 $active_light.bind("click",function(){})
    4 .css("border","1px dashed yellow")
    5 .css("background-color","orange")
    6 .fadeIn("slow");
    

     

     

     

     

  • 4. 如果你打算在其他函数中使用 jQuery 对象,那么你必须把它们缓存到全局环境中。
  • 如下代码所示:

     

     1 // 在全局范围定义一个对象(例如:window对象)
     2 window.$my = {
     3     head:$("head"),
     4     trafficLight:$("#trafficLight"),
     5     trafficButton:$("#trafficButton")
     6 };
     7 
     8 function doSomething(){
     9     // 现在你可以引用存储的结果并操作它们
    10     var script=document.createElement("script");
    11     $my.head.append(script);
    12 
    13     // 当你在函数内部操作是,可以继续将查询存入全局对象中去.
    14     $my.coolResults=$("#some_ul li");
    15     $my.otherResults=$("#some_table td");
    16 
    17     // 将全局函数作为一个普通的jquery对象去使用.
    18     $my.otherResults.css("border-color","red");
    19     $my.trafficLight.css("border-color","green");
    20 }
    21 // 你也可以在其他函数中使用它.
    

     

     

     

    这里的基本思想是在内存中建立你确实想要的东西,然后更新 DOM。这并不是一个 jQuery 最佳实践,但必须进行有效的 JavaScript 操作。直接的
    DOM 操作速度很慢。例如,你想动态的创建一组列表元素,千万不要这样做, 如下所示:对直接的 DOM 操作进行限制。

     

    1 var top_100_list = [];// 假设这里是100个独一无二的字符串
    2 var $mylist = $("#mylist");// jQuery选择到<ul>元素
    3 
    4 for(var i = 0,l = top_100_list.length;i<l;i++) {
    5     $mylist.append("<li>" + top_100_list[i] + "</li>");
    6 }
    

     

     

     

    我们应该将整套元素字符串在插入进 dom 中之前先全部创建好,如下所示:

    1 $("#entryform input").bind("focus",function() {
    2     $(this).addClass("selected");
    3 }).bind("blur",function(){
    4     $(this).removeClass("selected");
    5 });
    

     

  • 5. 冒泡除非在特殊情况下, 否则每一个 js 事件 (例如: click,mouseover 等.) 都会冒泡到父级节点。
  • 当我们需要给多个元素调用同个函数时这点会很有用。代替这种效率很差的多元素事件监听的方法就是, 你只需向它们的父节点绑定一次。比如,
    我们要为一个拥有很多输入框的表单绑定这样的行为: 当输入框被选中时为它添加一个 class 传统的做法是,直接选中 input,然后绑定 focus
    等,如下所示:

     

    1 $("#entryform").bind("focus",function(e) {
    2     var $cell = $(e.target); // e.target捕捉到触发的目标元素
    3     $cell.addClass("selected");
    4 }).bind("blur",function(e) {
    5     var $cell = $(e.target);
    6     $cell.removeClass("selected");
    7 });
    

     

     

     

    当然上面代码能帮我们完成相应的任务,但如果你要寻求更高效的方法,请使用如下代码:

    通过在父级监听获取焦点和失去焦点的事件,对目标元素进行操作。在上面代码中,父级元素扮演了一个调度员的角色,
    它可以基于目标元素绑定事件。如果你发现你给很多元素绑定了同一个事件监听, 那么现在的你肯定知道哪里做错了。

     

  • 6. 推迟到 $(window).load。
  • jQuery 对于开发者来说有一个很诱人的东西, 可以把任何东西挂到 $(document).ready 下。尽管 $(document).rady
    确实很有用,它可以在页面渲染时,其它元素还没下载完成就执行。如果你发现你的页面一直是载入中的状态,很有可能就是 $(document).ready
    函数引起的。你可以通过将 jQuery 函数绑定到 $(window).load 事件的方法来减少页面载入时的 cpu 使用率。它会在所有的 html(包括
    iframe) 被下载完成后执行。一些特效的功能,例如拖放, 视觉特效和动画, 预载入隐藏图像等等,都是适合这种技术的场合。

     

  • 7. 压缩 JavaScript。
  • 在线压缩地址: http://dean.edwards.name/packer / 压缩之前,请保证你的代码的规范性,否则可能失败,导致 Js 错误。

     

  • 8. 尽量使用 ID 代替 Class。
  • 前面性能优化已经说过,ID 选择器的速度是最快的。所以在 HTML 代码中,能使用 ID 的尽量使用 ID 来代替 class。看下面的一个例子:

     

     1 // 创建一个list
     2 var $myList = $('#myList');
     3 var myListItems = '<ul>';
     4 
     5 for(var i = 0; i < 1000; i ++) {
     6     myListItems += '<li class = "listItem'+i+'">This is a list item</li>';
     7     // 这里使用的是class
     8 }
     9 
    10 myListItems += '</ul>';
    11 $myList.html(myListItems);
    12 // 选择每一个li
    13 
    14 for(var i = 0; i<1000; i++) {
    15     var selectedItem = $('.listItem' + i);
    16 }
    

     

     

     

    在上段代码中,选择每个 li 总共只用了 61 毫秒,相比 class 的方式,将近快了 100
    倍。       在代码最后,选择每个 li 的过程中,总共用了 5066 毫秒,超过 5
    秒了。接着我们做一个对比,用 ID 代替 class:

     

  • 9. 给选择器一个上下文。
  • jQuery 选择器中有一个这样的选择器,它能指定上下文。jQuery(expression,context); 通过它,能缩小选择器在 DOM
    中搜索的范围,达到节省时间,提高效率。普通方式:$(‘.myDiv’) 改进方式:$(‘.myDiv’,$(“#listItem”))。

     

  • 10. 慎用. live() 方法(应该说尽量不要使用)。
  • 这是 jQuery1.3.1 版本之后增加的方法,这个方法的功能就是为新增的 DOM
    元素动态绑定事件。但对于效率来说,这个方法比较占用资源。所以请尽量不要使用它。例如有这么一段代码:

     

     1 <script type="text/javascript">
     2     $(function(){
     3 
     4         $("p").click(function(){
     5             alert($(this).text());
     6         });
     7 
     8         $("button").click(function(){
     9             $("<p>this is second p</p>").appendTo("body");
    10         });
    11     })
    12 </script>
    13 <body>
    14     <p>this is first p</p>
    15     <button>add</button>
    16 </body>
    

     

     

     

    运行后,你会发现新增的 p 元素,并没用被绑定 click 事件。你可以改成. live(“click”) 方式解决此问题,代码如下:

     

     1 $(function() {
     2     $("p").live("click",function() {
     3         // 改成live方式
     4         alert($(this).text());
     5     });
     6 
     7     $("button").click(function() {
     8         $("<p>this is second p</p>").appendTo("body");
     9     });
    10 })
    

     

     

     

    但我并不建议大家这么做,我想用另一种方式去解决这个问题,代码如下:

     

     1 $(function() {
     2     $("p").click(function() {
     3         alert($(this).text());
     4     });
     5 
     6     $("button").click(function() {
     7         $("<p>this is second p</p>").click(function() {
     8             // 为新增的元素重新绑定一次
     9             alert($(this).text());
    10         }).appendTo("body");
    11     });
    12 })
    

     

     

     

    虽然我把绑定事件重新写了一次,代码多了点,但这种方式的效率明显高于 live() 方式,特别是在频繁的 DOM 操作中,这点非常明显。


    80. 请解释. end() 的用途。

     

    在官方的 API 上是这样描述 end() 方法的:“回到最近的一个 "破坏性"
    操作之前。即,将匹配的元素列表变为前一次的状态。”;看样子好像是找到最后一次操作的元素的上一元素,在如下的例子中:html 代码:

    复制代码代码如下:

    1 <div>测试内容1</div><div>测试内容2</div>
    

    jQuery 代码:

    复制代码代码如下:

    1 $('<p>新增内容</p>').appendTo('div').addClass('c1').end().addClass('c2');
    

    复制代码代码如下:

     

     

     

    1 <div>测试内容1
    2     <p>新增内容</p>
    3 </div>
    4 <div>测试内容2
    5     <p>新增内容</p>
    6 </div>
    

     

     

     

    这里我就有一点不太明白了,怎么只有第一个 <p> 标签有两个样式,end()方法后返回的是什么,
    在火狐里添加了监控,得到如下结果:1.$('<p > 新增内容 </p>').appendTo('div')
    返回的是:[p,p]对象数组,即新增后的两个 p 标签;2.$('<p > 新增内容
    </p>').appendTo('div').addClass('c1') 返回的是:[p.c1,p.c1]对象数组,即添加了 c1 类样式后的 p
    对象数组;3.$('<p > 新增内容 </p>').appendTo('div').addClass('c1').end()
    返回的是 [p.c1], 是第 1 个 < div > 中的 < p>, 在 2 操作中,最后“破坏” 的是第 2 个 <
    div > 中的 < p>, 所以他的前一次操作的对象是第 1 个 < div > 中的 < p>, 返回的就是它;
    4.$('<p > 新增内容
    </p>').appendTo('div').addClass('c1').end().addClass('c2') 返回的仍然是第 1 个
    < div > 中的 < p>; 现在算是有点明白了,关键是要搞清楚最后一次操作的元素的上一元素是什么。


    81. 你如何给一个事件处理函数命名空间,为什么要这样做?

     

    任何作为 type 参数的字符串都是合法的;如果一个字符串不是原生的 JavaScript
    事件名,那么这个事件处理函数会绑定到一个自定义事件上。这些自定义事件绝对不会由浏览器触发,但可以通过使用. trigger()或者.
    triggerHandler()在其他代码中手动触发。如果 type 参数的字符串中包含一个点 (.)
    字符,那么这个事件就看做是有命名空间的了。这个点字符就用来分隔事件和他的命名空间。举例来说,如果执行.
    bind('click.name',handler),那么字符串中的 click 是事件类型,而字符串 name
    就是命名空间。命名空间允许我们取消绑定或者触发一些特定类型的事件,而不用触发别的事件。参考 unbind()来获取更多信息。

    jQuery 的 bind/unbind 方法应该说使用很简单,而且大多数时候可能并不会用到,取而代之的是直接用 click/keydown
    之类的事件名风格的方法来做事件绑定操作。

    但假设如下情况:需要在运行时根据用户交互的结果进行不同 click 事件处理逻辑的绑定,因而理论上会无数次对某一个事件进行 bind/unbind
    操作。但又希望 unbind 的时候只把自己绑上去的处理逻辑给释放掉而不是所有其他地方有可能的额外的同一事件绑定逻辑。这时候如果直接用.
    click()/.bind('click') 加上. unbind('click') 来进行重复绑定的话,被 unbind 掉的将是所有绑定在元素上的
    click 处理逻辑,潜在会影响到该元素其他第三方的行为。

    当然如果在 bind 的时候是显示定义了 function 变量的话,可以在 unbind 的时候提供 function 作为第二个参数来指定只
    unbind 其中一个处理逻辑,但实际应用中很可能会碰到各种进行匿名函数绑定的情况。对于这种问题,jQuery
    的解决方案是使用事件绑定的命名空间。即在事件名称后添加. something 来区分自己这部分行为逻辑范围。

    比如用. bind('click.myCustomRoutine',function(){...}); 同样是把匿名函数绑定到 click
    事件(你可以用自己的命名空间多次绑定不同的行为方法上去),当 unbind 的时候用. unbind('click.myCustomRoutine')
    即可释放所有绑定到. myCustomRoutine 命名空间的 click 事件,而不会解除其他通过. bind('click')
    或另外的命名空间所绑定的事件行为。同时,使用命令空间还可以让你一次性 unbind 所有此命名空间下的自定义事件绑定,通过.
    unbind('.myCustomRoutine') 即可。要注意的是,jQuery 的命名空间并不支持多级空间。

    因为在 jQuery 里面,如果用. unbind('click.myCustomRoutine.myCustomSubone'),解除的是命名空间分别为
    myCustomRoutine 和 myCustomSubone 的两个并列命名空间下的所有 click 事件,而不是 "myCustomRoutine 下的
    myCustomSubone 子空间"。


    82. 请说出你可以传递给 jQuery 方法的四种不同值。

     

    选择器(字符串),HTML(字符串),回调函数,HTML 元素,对象,数组,元素数组,jQuery 对象等。


    83. 什么是效果队列?

     

    jQuery
    中有个动画队列的机制。当我们对一个对象添加多次动画效果时后添加的动作就会被放入这个动画队列中,等前面的动画完成后再开始执行。可是用户的操作往往都比动画快,如果用户对一个对象频繁操作时不处理动画队列就会造成队列堆积,影响到效果。jQuery
    中有 stop 这个方法可以停止当前执行的动画,并且它有两个布尔参数,默认值都为 false。第一个参数为 true 时会清空动画队列,第二个参数为 true
    时会瞬间完成掉当前动画。所以,我们经常使用 obj.stop(true,true) 来停止动画。但是这还不够!正如 jQuery
    文档中的说法,即使第二个参数为 true,也仅仅是把当前在执行的动画跳转到完成状态。这时第一个参数如果也为
    true,后面的队列就会被清空。如果一个效果需要多个动画同时处理,我们仅完成其中的一个而把后面的队列丢弃了,这就会出现意料之外的结果。


    84. 请指出. get(),[],eq() 的区别。

     

    eq: 返回是一个 jquery 对象作用是将匹配的元素集合缩减为一个元素。这个元素在匹配元素集合中的位置变为 0,而集合长度变成 1。

    get: 是一个 html 对象数组作用是取得其中一个匹配的元素。num 表示取得第几个匹配的元素。


    85. 请指出. bind(),.live() 和. delegate() 的区别。

     

    在操纵 DOM 的语境中,document 是根节点。现在我们可以较容易地说明. bind()、.live() 和. delegate() 的不同之处了。

    .bind()

    1 $('a').bind('click', function() {alert("That tickles!")});
    

    这是最简单的绑定方法了。JQuery 扫描文档找出所有的 $(‘a’) 元素,并把 alert 函数绑定到每个元素的 click 事件上。

    .live()

    1 $('a').live('click', function() {alert("That tickles!")});
    

    JQuery 把 alert 函数绑定到 $(document) 元素上,并使用’click’和’a’作为参数。任何时候只要有事件冒泡到 document
    节点上,它就查看该事件是否是一个 click 事件,以及该事件的目标元素与’a’这一 CSS 选择器是否匹配,如果都是的话,则执行函数。

    live 方法还可以被绑定到具体的元素 (或 context) 而不是 document 上,像这样:

    1 $('a', $('#container')[0]).live(...);
    

    .delegate()

    1 $('#container').delegate('a', 'click', function() {alert("That tickles!")});
    

    JQuery 扫描文档查找 $(‘#container’),并使用 click 事件和’a’这一 CSS 选择器作为参数把 alert 函数绑定到
    $(‘#container’) 上。任何时候只要有事件冒泡到 $(‘#container’) 上,它就查看该事件是否是 click
    事件,以及该事件的目标元素是否与 CCS 选择器相匹配。如果两种检查的结果都为真的话,它就执行函数。

    可以注意到,这一过程与. live() 类似,但是其把处理程序绑定到具体的元素而非 document 这一根上。精明的 JS’er
    们可能会做出这样的结论,即 $('a').live()==$(document).delegate('a'),是这样吗? 嗯,不,不完全是。

    为什么. delegate() 要比. live() 好用?

    基于几个原因,人们通常更愿意选用 jQuery 的 delegate 方法而不是 live 方法。考虑下面的例子:

    1 $('a').live('click', function(){blah()}); // 或者
    2 $(document).delegate('a', 'click', function(){blah()});
    

    速度

    后者实际上要快过前者,因为前者首先要扫描整个的文档查找所有的 $(‘a’) 元素,把它们存成 jQuery 对象。尽管 live
    函数仅需要把’a’作为串参数传递以用做之后的判断,但是 $() 函数并未知道被链接的方法将会是. live()。而另一方面,delegate
    方法仅需要查找并存储 $(document) 元素。

    一种寻求避开这一问题的方法是调用在 $(document).ready() 之外绑定的 live,这样它就会立即执行。在这种方式下,其会在 DOM
    获得填充之前运行,因此就不会查找元素或是创建 jQuery 对象了。

    灵活性和链能力

    live 函数也挺令人费解的。想想看,它被链到 $(‘a’) 对象集上,但其实际上是在 $(document)
    对象上发生作用。由于这个原因,它能够试图以一种吓死人的方式来把方法链到自身上。实际上,我想说的是,以 $.live(‘a’,…) 这一形式作为一种全局性的
    jQuery 方法,live 方法会更具意义一些。

    仅支持 CSS 选择器

    最后一点,live 方法有一个非常大的缺点,那就是它仅能针对直接的 CSS 选择器做操作,这使得它变得非常的不灵活。

     

  • 为什么选择. live() 或. delegate() 而不是. bind()?
  • 毕竟,bind 看起来似乎更加的明确和直接,难道不是吗? 嗯,有两个原因让我们更愿意选择 delegate 或 live 而不是 bind:

    为了把处理程序附加到可能还未存在于 DOM 中的 DOM 元素之上。因为 bind
    是直接把处理程序绑定到各个元素上,它不能把处理程序绑定到还未存在于页面中的元素之上。

    如果你运行了 $(‘a’).bind(…),而后新的链接经由 AJAX 加入到了页面中,则你的 bind
    处理程序对于这些新加入的链接来说是无效的。而另一方面 live 和 delegate
    则是被绑定到另一个祖先节点上,因此其对于任何目前或是将来存在于该祖先元素之内的元素都是有效的。

    或者为了把处理程序附加到单个元素上或是一小组元素之上,监听后代元素上的事件而不是循环遍历并把同一个函数逐个附加到 DOM 中的 100
    个元素上。把处理程序附加到一个 (或是一小组) 祖先元素上而不是直接把处理程序附加到页面中的所有元素上,这种做法带来了性能上的好处。

    停止传播

    最后一个我想做的提醒与事件传播有关。通常情况下,我们可以通过使用这样的事件方法来终止处理函数的执行:

    1 $('a').bind('click', function(e) {
    2     e.preventDefault();
    3     // 或者 e.stopPropagation();
    4 });
    

    不过,当我们使用 live 或是 delegate
    方法的时候,处理函数实际上并没有在运行,需要等到事件冒泡到处理程序实际绑定的元素上时函数才会运行。而到此时为止,我们的其他的来自. bind()
    的处理函数早已运行了。


    86. 请指出 $ 和 $.fn 的区别,或者说出 $.fn 的用途。

     

    Jquery 为开发插件提供了两个方法,分别是:

    1 $.extend(obj);
    2 $.fn.extend(obj);
    

     

  • 1. 那么这两个分别是什么意思?
  • $.extend(obj); 是为了扩展 jquery 本身,为类添加新的方法。

    $.fn.extend(obj); 给 JQUERY 对象添加方法。

     

  • 2.$.fn 中的 fn 是什么意思,其实是 prototype,即 $.fn=$.prototype;
  • 具体用法请看下面的例子:

     

    1 $.extend({
    2 
    3     add:function(a, b) {
    4         return a+b;
    5     }
    6 })
    7 
    8 $.add(5,8); // return 13
    

     

     

     

    注意没有,这边的调用直接调用,前面不用任何对象。直接 $.+ 方法名

    $.fn.extend(obj);对 prototype 进行扩展,为 jquery 类添加成员函数,jquery 类的实例可以使用这个成员函数。

     

     1 $.fn.extend({
     2 
     3     clickwhile:function(){
     4 
     5         $(this).click(function(){
     6             alert($(this).val())
     7         })
     8     }
     9 })
    10 
    11 $('input').clickwhile(); // 当点击输入框会弹出该对象的Value值
    

     

     

     

    注意调用时候前面是有对象的。即 $('input') 这么个东西。


    87. 请写出一个函数实现 N! 的计算。N 取很大的值时,该怎么办?

     

    使用循环、递归都能写出函数。

    当 N 取值很大时,应该考虑把数值转化为字符串再进行运算。大数乘法再转化为大数加法运算,其具体算法应该有不少 C 语言实现,可以参考一下。


    88.modulo(12,5) //2 ;问题:实现满足上述结果的 modulo 函数。


    89."i'm a lasagna hog".split("").reverse().join(""); 问题:上面的语句的返回值是什么?

     

    答案:"goh angasal a m'i";


    90.(window.foo||(window.foo="bar")); 问题:window.foo 的值是什么?

     

    答案:"bar" 只有 window.foo 为假时的才是上面答案,否则就是它本身的值。

    var foo="Hello";(function(){var
    bar="World";alert(foo+bar);})();alert(foo+bar);


    91. 问题:上面两个 alert 的结果是什么?

     

    答案:"Hello World" 和 ReferenceError:bar is not defined


    92.var foo=[];foo.push(1);foo.push(2); 问题:foo.length 的值是什么?

    答案:2

     

    有趣的问题:

    91. 你编写过的最酷的代码是什么?其中你最自豪的是什么?

    参考:100行JS实现HTML5的3D贪吃蛇游戏

    92. 在你使用过的开发工具中,最喜欢哪个?

    93. 你有什么业余项目吗?是哪种类型的?

    94. 你最爱的 IE 特性是什么?

 

jonsam ng

jonsam ng

文章作者

海阔凭鱼跃,天高任鸟飞。

前端面试题集锦(6)
41. 解释下 JavaScript 中 this 是如何工作的。   this 永远指向函数运行时所在的对象(所处的运行环境[对象]),而不是函数被创建时所在的对象(运行时而非创建时)。匿名函数…
扫描二维码继续阅读
2019-10-14