JavaScript面试题
29个 JavaScript 基本面试问题和解答
1、使用typeof bar ===“object”来确定bar是否是一个对象时有什么潜在的缺陷?这个陷阱如何避免?
答:
尽管typeof bar ===“object”是检查bar是否是对象的可靠方法,但JavaScript中令人惊讶的问题是null也被认为是一个对象!因此,对于大多数开发人员来说,下面的代码会将真实(而不是错误)记录到控制台:
var bar = null;
console.log(typeof bar === "object"); // logs true!
只要知道这一点,就可以通过检查bar是否为空来轻松避免该问题:
console.log((bar !== null) && (typeof bar === "object")); // logs false
为了在我们的答案更加的完整,还有两件事值得注意:
首先,如果bar是一个函数,上面的解决方案将返回false。在大多数情况下,这是所期望的行为,但是在您希望函数返回true的情况下,您可以将上述解决方案修改为:
console.log((bar !== null) && ((typeof bar === "object") || (typeof bar === "function")));
其次,如果bar是数组,则上述解决方案将返回true(例如,如果var bar = [];)。在大多数情况下,这是所希望的行为,因为数组确实是对象,但是在您想要对数组也是false的情况下,可以将上述解决方案修改为:
console.log((bar !== null) && (typeof bar === "object") && (toString.call(bar) !== "[object Array]"));
但是,还有一个替代方法对空值,数组和函数返回false,但对于对象则为true:
console.log((bar !== null) && (bar.constructor === Object));
或者,如果您使用jQuery:
console.log((bar !== null) && (typeof bar === "object") && (! $.isArray(bar)));
ES5使得数组的情况非常简单,包括它自己的空检查:
console.log(Array.isArray(bar));
2、下面的代码将输出到控制台的是什么,为什么?
(function(){
var a = b = 3;
})();
console.log("a defined? " + (typeof a !== 'undefined'));
console.log("b defined? " + (typeof b !== 'undefined'));
由于a和b都在函数的封闭范围内定义,并且由于它们所在的行以var关键字开头,因此大多数JavaScript开发人员会希望typeof a和typeof b在上面的示例中都未定义。
但是,情况并非如此。这里的问题是大多数开发人员错误地理解语句var a = b = 3;以下简写为:
var b = 3;
var a = b;
但实际上,var a = b = 3;其实是速记:
b = 3;
var a = b;
因此(如果您不使用严格模式),代码片段的输出将为:
a defined? false
b defined? true
但是如何在封闭函数的范围之外定义b?那么,因为声明var a = b = 3;是语句b = 3的简写;并且var a = b; b最终成为一个全局变量(因为它不在var关键字后面),因此它仍然在作用域内,即使在封闭函数之外。
注意,在严格模式下(即,使用strict),语句var a = b = 3;会产生一个ReferenceError的运行时错误:b没有定义,从而避免了可能导致的任何头headfakes/bugs。 (这就是为什么你应该在你的代码中使用strict,一个重要的例子!)
3、下面的代码将输出到控制台的是什么?,为什么?
var myObject = {
foo: "bar",
func: function() {
var self = this;
console.log("outer func: this.foo = " + this.foo);
console.log("outer func: self.foo = " + self.foo);
(function() {
console.log("inner func: this.foo = " + this.foo);
console.log("inner func: self.foo = " + self.foo);
}());
}
};
myObject.func();
以上代码将输出到控制台:
outer func: this.foo = bar
outer func: self.foo = bar
inner func: this.foo = undefined
inner func: self.foo = bar
在外部函数中,this和self都引用myObject,因此都可以正确地引用和访问foo。
但在内部函数中,这不再指向myObject。因此,this.foo在内部函数中是未定义的,而对局部变量self的引用仍然在范围内并且可以在那里访问。
4、在功能块中封装JavaScript源文件的全部内容的重要性和原因是什么?
这是一种日益普遍的做法,被许多流行的JavaScript库(jQuery,Node.js等)所采用。这种技术在文件的全部内容周围创建一个闭包,这可能最重要的是创建一个私有名称空间,从而有助于避免不同JavaScript模块和库之间的潜在名称冲突。
这种技术的另一个特点是为全局变量提供一个容易引用(可能更短)的别名。例如,这通常用于jQuery插件。 jQuery允许您使用jQuery.noConflict()来禁用对jQuery名称空间的 引 用 。 如 果 这 样 做 了 , 你 的 代 码 仍 然 可 以 使 用 引用。如果这样做了,你的代码仍然可以使用 引用。如果这样做了,你的代码仍然可以使用使用闭包技术,如下所示:
(function($) { /* jQuery plugin code referencing $ */ } )(jQuery);
5、在JavaScript源文件的开头包含’use strict’的意义和有什么好处?
答:
这里最简单也是最重要的答案是use strict是一种在运行时自动执行更严格的JavaScript代码解析和错误处理的方法。如果代码错误被忽略或失败,将会产生错误或抛出异常。总的来说,这是一个很好的做法。
严格模式的一些主要优点包括:
- 使调试更容易。 如果代码错误本来会被忽略或失败,那么现在将会产生错误或抛出异常,从而更快地发现代码中的问题,并更快地指引它们的源代码。
- 防止意外全局。 如果没有严格模式,将值赋给未声明的变量会自动创建一个具有该名称的全局变量。这是JavaScript中最常见的错误之一。在严格模式下,尝试这样做会引发错误。
- 消除隐藏威胁。在没有严格模式的情况下,对null或undefined的这个值的引用会自动强制到全局。这可能会导致许多headfakes和pull-out-your-hair类型的错误。在严格模式下,引用null或undefined的这个值会引发错误
- 不允许重复的参数值。 严格模式在检测到函数的重复命名参数(例如,函数foo(val1,val2,val1){})时会引发错误,从而捕获代码中几乎可以肯定存在的错误,否则您可能会浪费大量的时间追踪。
- 注意:它曾经是(在ECMAScript 5中)strict模式将禁止重复的属性名称(例如var object = {foo:“bar”,foo:“baz”};)但是从ECMAScript 2015 开始,就不再有这种情况了。
- 使eval()更安全。 eval()在严格模式和非严格模式下的行为方式有些不同。最重要的是,在严格模式下,在eval()语句内部声明的变量和函数不会在包含范围中创建(它们是以非严格模式在包含范围中创建的,这也可能是问题的常见来源)。
- 抛出无效的使用错误的删除符。 删除操作符(用于从对象中删除属性)不能用于对象的不可配置属性。当试图删除一个不可配置的属性时,非严格代码将自动失败,而在这种情况下,严格模式会引发错误。
6、考虑下面的两个函数。他们都会返回同样的值吗?为什么或者为什么不?
function foo1(){
return {
bar: "hello"
};
}
function foo2(){
return
{
bar: "hello"
};
}
console.log("foo1 returns:");
console.log(foo1());
console.log("foo2 returns:");
console.log(foo2());
令人惊讶的是,这两个函数不会返回相同的结果,会产生:
foo1 returns:
Object {bar: "hello"}
foo2 returns:
undefine
这不仅令人惊讶,而且特别令人烦恼的是,foo2()返回未定义而没有引发任何错误。
原因与JavaScript中分号在技术上是可选的事实有关(尽管忽略它们通常是非常糟糕的形式)。因此,在foo2()中遇到包含return语句的行(没有其他内容)时,会在return语句之后立即自动插入分号。
由于代码的其余部分是完全有效的,即使它没有被调用或做任何事情(它只是一个未使用的代码块,它定义了一个属性栏,它等于字符串“hello”),所以不会抛出任何错误。
这种行为也被认为是遵循了在JavaScript中将一行开头大括号放在行尾的约定,而不是在新行的开头。如此处所示,这不仅仅是JavaScript中的一种风格偏好。
7、什么是NaN?它的类型是什么?如何可靠地测试一个值是否等于NaN?
NaN属性表示“不是数字”的值。这个特殊值是由于一个操作数是非数字的(例如“abc”/ 4)或者因为操作的结果是非数字而无法执行的。
测试数字是否等于NaN的半可靠方法是使用内置函数isNaN(),但即使使用 isNaN()也不是一个好的解决方案。.
一个更好的解决方案要么是使用value!==值,如果该值等于NaN,那么只会生成true。另外,ES6提供了一个新的Number.isNaN()函数 ,它与旧的全局isNaN()函数不同,也更加可靠。
8、下面的代码输出什么?解释你的答案。
console.log(0.1 + 0.2);
console.log(0.1 + 0.2 == 0.3)
对这个问题的一个有教养的回答是:“你不能确定。它可能打印出0.3和true,或者可能不打印。 JavaScript中的数字全部用浮点精度处理,因此可能不会总是产生预期的结果。“
0.30000000000000004
false
一个典型的解决方案是比较两个数字与特殊常数Number.EPSILON之间的绝对差值:
function areTheNumbersAlmostEqual(num1, num2) {
return Math.abs( num1 - num2 ) < Number.EPSILON;
}
console.log(areTheNumbersAlmostEqual(0.1 + 0.2, 0.3));
讨论写函数的可能方法isInteger(x),它确定x是否是一个整数。
这听起来很平凡,事实上,ECMAscript 6为此正好引入了一个新的Number.isInteger()函数,这是微不足道的。但是,在ECMAScript 6之前,这有点复杂,因为没有提供与Number.isInteger()方法等价的方法。
问题在于,在ECMAScript规范中,整数只在概念上存在;即数值始终作为浮点值存储。
考虑到这一点,最简单,最清洁的ECMAScript-6之前的解决方案(即使将非数字值(例如字符串或空值)传递给该函数,该解决方案也具有足够的可靠性以返回false)将成为以下用法按位异或运算符:
function isInteger(x) { return (x ^ 0) === x; }
下面的解决方案也可以工作,尽管不如上面那样高雅
function isInteger(x) { return Math.round(x) === x; }
请注意,在上面的实现中Math.ceil()或Math.floor()可以同样使用(而不是Math.round())。
或者:
function isInteger(x) { return (typeof x === 'number') && (x % 1 === 0); }
一个相当常见的不正确的解决方案如下:
function isInteger(x) { return parseInt(x, 10) === x; }
虽然这个基于parseInt的方法对许多x值很有效,但一旦x变得相当大,它将无法正常工作。问题是parseInt()在解析数字之前将其第一个参数强制转换为字符串。因此,一旦数字变得足够大,其字符串表示将以指数形式呈现(例如1e + 21)。因此,parseInt()将尝试解析1e + 21,但是当它到达e字符时将停止解析,因此将返回值1.观察:
> String(1000000000000000000000)
'1e+21'
> parseInt(1000000000000000000000, 10)
1
> parseInt(1000000000000000000000, 10) === 1000000000000000000000
false
9、执行下面的代码时,按什么顺序将数字1-4记录到控制台?为什么?
(function() {
console.log(1);
setTimeout(function(){console.log(2)}, 1000);
setTimeout(function(){console.log(3)}, 0);
console.log(4);
})();
1
4
3
2
10、编写一个简单的函数(少于160个字符),返回一个布尔值,指示字符串是否是palindrome(回文(指顺读和倒读都一样的词语))。
如果str是回文,以下一行函数将返回true;否则,它返回false。
function isPalindrome(str) {
str = str.replace(/\W/g, '').toLowerCase();
return (str == str.split('').reverse().join(''));
}
eg
console.log(isPalindrome("level")); // logs 'true'
console.log(isPalindrome("levels")); // logs 'false'
console.log(isPalindrome("A car, a man, a maraca")); // logs 'true'
11、写一个sum方法,当使用下面的语法调用时它将正常工作。
console.log(sum(2,3)); // Outputs 5
console.log(sum(2)(3)); // Outputs 5
有(至少)两种方法可以做到这一点:
function sum(x) {
if (arguments.length === 2) {
return arguments[0] + arguments[1];
} else {
return function(y) { return x + y; };
}
}
在JavaScript中,函数提供对参数对象的访问,该对象提供对传递给函数的实际参数的访问。这使我们能够使用length属性在运行时确定传递给函数的参数的数量
如果传递两个参数,我们只需将它们相加并返回。
否则,我们假设它是以sum(2)(3)的形式被调用的,所以我们返回一个匿名函数,它将传递给sum()(在本例中为2)的参数和传递给匿名函数的参数(这种情况3)
function sum(x, y) {
if (y !== undefined) {
return x + y;
} else {
return function(y) { return x + y; };
}
}
当函数被调用时,JavaScript不需要参数的数量来匹配函数定义中参数的数量。如果传递的参数数量超过了函数定义中参数的数量,则超出的参数将被忽略。另一方面,如果传递的参数数量少于函数定义中的参数数量,则在函数内引用时,缺少的参数将具有未定义的值。因此,在上面的例子中,通过简单地检查第二个参数是否未定义,我们可以确定函数被调用的方式并相应地继续。
12、考虑下面的代码片段
for (var i = 0; i < 5; i++) {
var btn = document.createElement('button');
btn.appendChild(document.createTextNode('Button ' + i));
btn.addEventListener('click', function(){ console.log(i); });
document.body.appendChild(btn);
}
(a) 当用户点击“按钮4”时,什么被记录到控制台?为什么?
(b) 提供一个或多个可按预期工作的替代实现。
答:
(a) 无论用户点击哪个按钮,数字5将始终记录到控制台。这是因为,在调用onclick方法(对于任何按钮)时,for循环已经完成,并且变量i已经具有值5.(如果受访者知道足够的话就可以获得奖励点数关于执行上下文,变量对象,激活对象和内部“范围”属性如何影响闭包行为。
(b) 使这项工作的关键是通过将它传递给新创建的函数对象来捕获每次通过for循环的i的值。以下是四种可能的方法来实现这一点:
for (var i = 0; i < 5; i++) {
var btn = document.createElement('button');
btn.appendChild(document.createTextNode('Button ' + i));
btn.addEventListener('click', (function(i) {
return function() { console.log(i); };
})(i));
document.body.appendChild(btn);
}
或者,您可以将新的匿名函数中的整个调用包装为btn.addEventListener:
for (var i = 0; i < 5; i++) {
var btn = document.createElement('button');
btn.appendChild(document.createTextNode('Button ' + i));
(function (i) {
btn.addEventListener('click', function() { console.log(i); });
})(i);
document.body.appendChild(btn);
}
或者,我们可以通过调用数组对象的原生forEach方法来替换for循环:
['a', 'b', 'c', 'd', 'e'].forEach(function (value, i) {
var btn = document.createElement('button');
btn.appendChild(document.createTextNode('Button ' + i));
btn.addEventListener('click', function() { console.log(i); });
document.body.appendChild(btn);
});
最后,最简单的解决方案,如果你在ES6 / ES2015上下文中,就是使用let i而不是var i:
for (let i = 0; i < 5; i++) {
var btn = document.createElement('button');
btn.appendChild(document.createTextNode('Button ' + i));
btn.addEventListener('click', function(){ console.log(i); });
document.body.appendChild(btn);
}
13、假设d是范围内的“空”对象:
var d = {};
使用下面的代码完成了什么?
[ 'zebra', 'horse' ].forEach(function(k) {
d[k] = undefined;
});
上面显示的代码片段在对象d上设置了两个属性。理想情况下,对具有未设置键的JavaScript对象执行的查找评估为未定义。但是运行这段代码会将这些属性标记为对象的“自己的属性”。
这是确保对象具有一组给定属性的有用策略。将该对象传递给Object.keys将返回一个包含这些设置键的数组(即使它们的值未定义)。
14、下面的代码将输出到控制台,为什么?
var arr1 = "john".split('');
var arr2 = arr1.reverse();
var arr3 = "jones".split('');
arr2.push(arr3);
console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1));
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"
将数组传递给另一个数组的push()方法会将整个数组作为单个元素推入数组的末尾。结果,声明arr2.push(arr3);将arr3作为一个整体添加到arr2的末尾(即,它不连接两个数组,这就是concat()方法的用途)
15、下面的代码将输出到控制台,为什么?
console.log(1 + "2" + "2");
console.log(1 + +"2" + "2");
console.log(1 + -"1" + "2");
console.log(+"1" + "1" + "2");
console.log( "A" - "B" + "2");
console.log( "A" - "B" + 2);
"122"
"32"
"02"
"112"
"NaN2"
NaN
这里的基本问题是JavaScript(ECMAScript)是一种松散类型的语言,它对值执行自动类型转换以适应正在执行的操作。让我们来看看这是如何与上面的每个例子进行比较。
示例1:1 +“2”+“2”输出:“122”说明:第一个操作在1 +“2”中执行。由于其中一个操作数(“2”)是一个字符串,所以JavaScript假定需要执行字符串连接,因此将1的类型转换为“1”,1 +“2”转换为“12”。然后,“12”+“2”产生“122”。
示例2:1 + +“2”+“2”输出:“32”说明:根据操作顺序,要执行的第一个操作是+“2”(第一个“2”之前的额外+被视为一个一元运算符)。因此,JavaScript将“2”的类型转换为数字,然后将一元+符号应用于它(即将其视为正数)。结果,下一个操作现在是1 + 2,当然这会产生3.但是,我们有一个数字和一个字符串之间的操作(即3和“2”),所以JavaScript再次转换数值赋给一个字符串并执行字符串连接,产生“32”。
示例3:1 + - “1”+“2”输出:“02”说明:这里的解释与前面的示例相同,只是一元运算符是 - 而不是+。因此,“1”变为1,然后在应用 - 时将其变为-1,然后将其加1到产生0,然后转换为字符串并与最终的“2”操作数连接,产生“02”。
示例4:+“1”+“1”+“2”输出:“112”说明:尽管第一个“1”操作数是基于其前面的一元+运算符的数值类型转换的,当它与第二个“1”操作数连接在一起时返回一个字符串,然后与最终的“2”操作数连接,产生字符串“112”。
示例5:“A” - “B”+“2”输出:“NaN2”说明:由于 - 运算符不能应用于字符串,并且既不能将“A”也不能将“B”转换为数值, “ - ”B“产生NaN,然后与字符串”2“串联产生”NaN2“。
例6:“A” - “B”+2输出:NaN说明:在前面的例子中,“A” - “B”产生NaN。但是任何运算符应用于NaN和其他数字操作数仍然会产生NaN。
16、如果数组列表太大,以下递归代码将导致堆栈溢出。你如何解决这个问题,仍然保留递归模式?
var list = readHugeList();
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
nextListItem();
}
};
通过修改nextListItem函数可以避免潜在的堆栈溢出,如下所示:
var list = readHugeList();
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
setTimeout( nextListItem, 0);
}
};
堆栈溢出被消除,因为事件循环处理递归,而不是调用堆栈。当nextListItem运行时,如果item不为null,则将超时函数(nextListItem)推送到事件队列,并且函数退出,从而使调用堆栈清零。当事件队列运行超时事件时,将处理下一个项目,并设置一个计时器以再次调用nextListItem。因此,该方法从头到尾不经过直接递归调用即可处理,因此调用堆栈保持清晰,无论迭代次数如何。
17、什么是JavaScript中的“闭包”?举一个例子。
闭包是一个内部函数,它可以访问外部(封闭)函数的作用域链中的变量。闭包可以访问三个范围内的变量;具体来说: (1)变量在其自己的范围内, (2)封闭函数范围内的变量 (3)全局变量。
var globalVar = "xyz";
(function outerFunc(outerArg) {
var outerVar = 'a';
(function innerFunc(innerArg) {
var innerVar = 'b';
console.log(
"outerArg = " + outerArg + "\n" +
"innerArg = " + innerArg + "\n" +
"outerVar = " + outerVar + "\n" +
"innerVar = " + innerVar + "\n" +
"globalVar = " + globalVar);
})(456);
})(123);
在上面的例子中,innerFunc,outerFunc和全局名称空间的变量都在innerFunc的范围内。上面的代码将产生以下输出:
outerArg = 123
innerArg = 456
outerVar = a
innerVar = b
globalVar = xyz
18、以下代码的输出是什么:
for (var i = 0; i < 5; i++) {
setTimeout(function() { console.log(i); }, i * 1000 );
}
解释你的答案。如何在这里使用闭包?
显示的代码示例不会显示值0,1,2,3和4,这可能是预期的;而是显示5,5,5,5。
这是因为循环内执行的每个函数将在整个循环完成后执行,因此所有函数都会引用存储在i中的最后一个值,即5。
通过为每次迭代创建一个唯一的作用域,可以使用闭包来防止这个问题,并将该变量的每个唯一值存储在其作用域中,如下所示:
for (var i = 0; i < 5; i++) {
(function(x) {
setTimeout(function() { console.log(x); }, x * 1000 );
})(i);
}
这会产生将0,1,2,3和4记录到控制台的可能结果。
在ES2015上下文中,您可以在原始代码中简单地使用let而不是var:
for (let i = 0; i < 5; i++) {
setTimeout(function() { console.log(i); }, i * 1000 );
}
19、以下几行代码输出到控制台?
console.log("0 || 1 = "+(0 || 1));
console.log("1 || 2 = "+(1 || 2));
console.log("0 && 1 = "+(0 && 1));
console.log("1 && 2 = "+(1 && 2));
解释你的答案。
该代码将输出以下四行:
0 || 1 = 1
1 || 2 = 1
0 && 1 = 0
1 && 2 = 2
20 、下面的代码执行时输出是什么?说明。
console.log(false == '0')
console.log(false === '0')
true
false
在JavaScript中,有两套相等运算符。三重相等运算符 === 的行为与任何传统的相等运算符相同:如果两侧的两个表达式具有相同的类型和相同的值,则计算结果为true。然而,双等号运算符在比较它们之前试图强制这些值。因此,通常使用 === 而不是 == 。对于 !== vs!=也是如此。
21、以下代码的输出是什么?解释你的答案。
var a={},
b={key:'b'},
c={key:'c'};
a[b]=123;
a[c]=456;
console.log(a[b]);
此代码的输出将是456(不是123)。
原因如下:设置对象属性时,JavaScript会隐式地将参数值串联起来。在这种情况下,由于b和c都是对象,它们都将被转换为“[object Object]”。因此,a [b]和a [c]都等价于[“[object Object]”],并且可以互换使用。因此,设置或引用[c]与设置或引用[b]完全相同。
22、以下代码将输出到控制台中.
console.log((function f(n){return ((n > 1) ? n * f(n-1) : n)})(10));
该代码将输出10阶乘的值(即10!或3,628,800)。
原因如下:
命名函数f()以递归方式调用自身,直到它调用f(1),它简单地返回1.因此,这就是它的作用:
f(1): returns n, which is 1
f(2): returns 2 * f(1), which is 2
f(3): returns 3 * f(2), which is 6
f(4): returns 4 * f(3), which is 24
f(5): returns 5 * f(4), which is 120
f(6): returns 6 * f(5), which is 720
f(7): returns 7 * f(6), which is 5040
f(8): returns 8 * f(7), which is 40320
f(9): returns 9 * f(8), which is 362880
f(10): returns 10 * f(9), which is 3628800
23 、考虑下面的代码片段。控制台的输出是什么,为什么?
(function(x) {
return (function(y) {
console.log(x);
})(2)
})(1);
输出将为1,即使x的值从未在内部函数中设置。原因如下:
正如我们的JavaScript招聘指南中所解释的,闭包是一个函数,以及创建闭包时在范围内的所有变量或函数。在JavaScript中,闭包被实现为“内部函数”;即在另一功能的主体内定义的功能。闭包的一个重要特征是内部函数仍然可以访问外部函数的变量。
因此,在这个例子中,因为x没有在内部函数中定义,所以在外部函数的作用域中搜索一个定义的变量x,该变量的值为1。
24、以下代码将输出到控制台以及为什么
var hero = {
_name: 'John Doe',
getSecretIdentity: function (){
return this._name;
}
};
var stoleSecretIdentity = hero.getSecretIdentity;
console.log(stoleSecretIdentity());
console.log(hero.getSecretIdentity());
这段代码有什么问题,以及如何解决这个问题。
该代码将输出:
undefined
John Doe
第一个console.log打印未定义,因为我们从hero对象中提取方法,所以stoleSecretIdentity()在_name属性不存在的全局上下文(即窗口对象)中被调用。
修复stoleSecretIdentity()函数的一种方法如下:
var stoleSecretIdentity = hero.getSecretIdentity.bind(hero);
25、创建一个函数,给定页面上的DOM元素,将访问元素本身及其所有后代(不仅仅是它的直接子元素)。对于每个访问的元素,函数应该将该元素传递给提供的回调函数。
该函数的参数应该是:1.一个 DOM 元素;2.一个回调函数(以DOM元素作为参数)
访问树中的所有元素(DOM)是经典的深度优先搜索算法应用程序。以下是一个示例解决方案:
function Traverse(p_element,p_callback) {
p_callback(p_element);
var list = p_element.children;
for (var i = 0; i < list.length; i++) {
Traverse(list[i],p_callback); // recursive call
}
}
27、在JavaScript中测试您的这些知识:以下代码的输出是什么?
var length = 10;
function fn() {
console.log(this.length);
}
var obj = {
length: 5,
method: function(fn) {
fn();
arguments[0]();
}
};
obj.method(fn, 1);
输出:
10
2
为什么不是10和5?
首先,由于fn作为函数方法的参数传递,函数fn的作用域(this)是窗口。 var length = 10;在窗口级别声明。它也可以作为window.length或length或this.length来访问(当这个===窗口时)。
方法绑定到Object obj,obj.method用参数fn和1调用。虽然方法只接受一个参数,但调用它时已经传递了两个参数;第一个是函数回调,其他只是一个数字。
当在内部方法中调用fn()时,该函数在全局级别作为参数传递,this.length将有权访问在Object obj中定义的var length = 10(全局声明)而不是length = 5。
现在,我们知道我们可以使用arguments []数组访问JavaScript函数中的任意数量的参数。
因此arguments0只不过是调用fn()。在fn里面,这个函数的作用域成为参数数组,并且记录参数[]的长度将返回2。
因此输出将如上所述。
28、你如何克隆一个对象?
var obj = {a: 1 ,b: 2}
var objclone = Object.assign({},obj);
现在objclone的值是{a:1,b:2},但指向与obj不同的对象。
但请注意潜在的缺陷:Object.clone()只会执行浅拷贝,而不是深拷贝。这意味着嵌套的对象不会被复制。他们仍然引用与原始相同的嵌套对象:
let obj = {
a: 1,
b: 2,
c: {
age: 30
}
};
var objclone = Object.assign({},obj);
console.log('objclone: ', objclone);
obj.c.age = 45;
console.log('After Change - obj: ', obj); // 45 - This also changes
console.log('After Change - objclone: ', objclone); // 45
29、如何在数组的开头添加元素?最后如何添加一个?
var myArray = ['a', 'b', 'c', 'd'];
myArray.push('end');
myArray.unshift('start');
console.log(myArray); // ["start", "a", "b", "c", "d", "end"]
使用ES6,可以使用扩展运算符:
myArray = ['start', ...myArray];
myArray = [...myArray, 'end'];
JavaScript
1、JavaScript中如何检测一个变量是一个String类型?请写出函数实现
方法1、
function isString(obj){
return typeof(obj) === "string"? true: false;
// returntypeof obj === "string"? true: false;
}
方法2、
function isString(obj){
return obj.constructor === String? true: false;
}
方法3.
function isString(obj){
return Object.prototype.toString.call(obj) === "[object String]"?true:false;
}
如:
var isstring = isString('xiaoming');
console.log(isstring); // true
2、请用js去除字符串空格?
方法一:使用replace正则匹配的方法
去除所有空格: str = str.replace(/\s*/g,"");
去除两头空格: str = str.replace(/^\s*|\s*KaTeX parse error: Expected group after '^' at position 36: … str.replace( /^̲\s*/, “”); 去除右空…)/g, “”);
str为要去除空格的字符串,实例如下:
var str = " 23 23 ";
var str2 = str.replace(/\s*/g,"");
console.log(str2); // 2323
方法二:使用str.trim()方法
str.trim()局限性:无法去除中间的空格,实例如下:
var str = " xiao ming ";
var str2 = str.trim();
console.log(str2); //xiao ming
同理,str.trimLeft(),str.trimRight()分别用于去除字符串左右空格。
方法三:使用jquery,$.trim(str)方法
$.trim(str)局限性:无法去除中间的空格,实例如下:
var str = " xiao ming ";
var str2 = $.trim(str)
console.log(str2); // xiao ming
3、你如何获取浏览器URL中查询字符串中的参数?
测试地址为:http://www.runoob.com/jquery/misc-trim.html?channelid=12333&name=xiaoming&age=23
实例如下:
function showWindowHref(){
var sHref = window.location.href;
var args = sHref.split('?');
if(args[0] == sHref){
return "";
}
var arr = args[1].split('&');
var obj = {};
for(var i = 0;i< arr.length;i++){
var arg = arr[i].split('=');
obj[arg[0]] = arg[1];
}
return obj;
}
var href = showWindowHref(); // obj
console.log(href['name']); // xiaoming
4、js 字符串操作函数
我这里只是列举了常用的字符串函数,具体使用方法,请参考网址。
-
concat() – 将两个或多个字符的文本组合起来,返回一个新的字符串。
-
indexOf() – 返回字符串中一个子串第一处出现的索引。如果没有匹配项,返回 -1 。
-
charAt() – 返回指定位置的字符。
-
lastIndexOf() – 返回字符串中一个子串最后一处出现的索引,如果没有匹配项,返回 -1 。
-
match() – 检查一个字符串是否匹配一个正则表达式。
-
substr() 函数 – 返回从string的startPos位置,长度为length的字符串
-
substring() – 返回字符串的一个子串。传入参数是起始位置和结束位置。
-
slice() – 提取字符串的一部分,并返回一个新字符串。
-
replace() – 用来查找匹配一个正则表达式的字符串,然后使用新字符串代替匹配的字符串。
-
search() – 执行一个正则表达式匹配查找。如果查找成功,返回字符串中匹配的索引值。否则返回 -1 。
-
split() – 通过将字符串划分成子串,将一个字符串做成一个字符串数组。
-
length – 返回字符串的长度,所谓字符串的长度是指其包含的字符的个数。
-
toLowerCase() – 将整个字符串转成小写字母。
-
toUpperCase() – 将整个字符串转成大写字母。
5、怎样添加、移除、移动、复制、创建和查找节点?
1)创建新节点
createDocumentFragment() //创建一个DOM片段
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点
2)添加、移除、替换、插入
appendChild() //添加
removeChild() //移除
replaceChild() //替换
insertBefore() //插入
3)查找
getElementsByTagName() //通过标签名称
getElementsByName() //通过元素的Name属性的值
getElementById() //通过元素Id,唯一性
6、写出3个使用this的典型应用
(1)、在html元素事件属性中使用,如:
<input type=”button” onclick=”showInfo(this);” value=”点击一下”/>
(2)、构造函数
function Animal(name, color) {
this.name = name;
this.color = color;
}
(3)、input点击,获取值
<input type="button" id="text" value="点击一下" />
<script type="text/javascript">
var btn = document.getElementById("text");
btn.onclick = function() {
alert(this.value); //此处的this是按钮元素
}
</script>
(4)、apply()/call()求数组最值
var numbers = [5, 458 , 120 , -215 ];
var maxInNumbers = Math.max.apply(this, numbers);
console.log(maxInNumbers); // 458
var maxInNumbers = Math.max.call(this,5, 458 , 120 , -215);
console.log(maxInNumbers); // 458
7、什么是跨域?跨域请求资源的方法有哪些?
1、什么是跨域?
由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。存在跨域的情况:
-
网络协议不同,如http协议访问https协议。
-
端口不同,如80端口访问8080端口。
-
域名和域名对应ip,如www.a.com访问20.205.28.90.
2、跨域请求资源的方法:
(1)、porxy代理
定义和用法:proxy代理用于将请求发送给后台服务器,通过服务器来发送请求,然后将请求的结果传递给前端。
实现方法:通过nginx代理;
注意点:1、如果你代理的是https协议的请求,那么你的proxy首先需要信任该证书(尤其是自定义证书)或者忽略证书检查,否则你的请求无法成功。
(2)、CORS 【Cross-Origin Resource Sharing】
定义和用法:是现代浏览器支持跨域资源请求的一种最常用的方式。
使用方法:一般需要后端人员在处理请求数据的时候,添加允许跨域的相关操作。如下:
res.writeHead(200, {
"Content-Type": "text/html; charset=UTF-8",
"Access-Control-Allow-Origin":'http://localhost',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'X-Requested-With, Content-Type'
});
(3)、jsonp
定义和用法:通过动态插入一个script标签。浏览器对script的资源引用没有同源限制,同时资源加载到页面后会立即执行(没有阻塞的情况下)。
特点:通过情况下,通过动态创建script来读取他域的动态资源,获取的数据一般为json格式。
实例如下
<script>
function testjsonp(data) {
console.log(data.name); // 获取返回的结果
}
</script>
<script>
var _script = document.createElement('script');
_script.type = "text/javascript";
_script.src = "http://localhost:8888/jsonp?callback=testjsonp";
document.head.appendChild(_script);
</script>
缺点:
1、这种方式无法发送post请求(这里)
2、另外要确定jsonp的请求是否失败并不容易,大多数框架的实现都是结合超时时间来判定。
8、谈谈垃圾回收机制方式及内存管理
回收机制方式
1、定义和用法:垃圾回收机制(GC:Garbage Collection),执行环境负责管理代码执行过程中使用的内存。
2、原理:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行。
3、实例如下:
function fn1() {
var obj = {name: 'hanzichi', age: 10};
}
function fn2() {
var obj = {name:'hanzichi', age: 10};
return obj;
}
var a = fn1();
var b = fn2();
fn1中定义的obj为局部变量,而当调用结束后,出了fn1的环境,那么该块内存会被js引擎中的垃圾回收器自动释放;在fn2被调用的过程中,返回的对象被全局变量b所指向,所以该块内存并不会被释放。
4、垃圾回收策略:标记清除(较为常用)和引用计数。
标记清除:
定义和用法:当变量进入环境时,将变量标记"进入环境",当变量离开环境时,标记为:“离开环境”。某一个时刻,垃圾回收器会过滤掉环境中的变量,以及被环境变量引用的变量,剩下的就是被视为准备回收的变量。
到目前为止,IE、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。
引用计数:
定义和用法:引用计数是跟踪记录每个值被引用的次数。
基本原理:就是变量的引用次数,被引用一次则加1,当这个引用计数为0时,被视为准备回收的对象。
内存管理
1、什么时候触发垃圾回收?
垃圾回收器周期性运行,如果分配的内存非常多,那么回收工作也会很艰巨,确定垃圾回收时间间隔就变成了一个值得思考的问题。
IE6的垃圾回收是根据内存分配量运行的,当环境中的变量,对象,字符串达到一定数量时触发垃圾回收。垃圾回收器一直处于工作状态,严重影响浏览器性能。
IE7中,垃圾回收器会根据内存分配量与程序占用内存的比例进行动态调整,开始回收工作。
2、合理的GC方案:(1)、遍历所有可访问的对象; (2)、回收已不可访问的对象。
3、GC缺陷:(1)、停止响应其他操作;
4、GC优化策略:(1)、分代回收(Generation GC);(2)、增量GC
9、开发过程中遇到的内存泄露情况,如何解决的?
1、定义和用法:
内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。C#和Java等语言采用了自动垃圾回收方法管理内存,几乎不会发生内存泄露。我们知道,浏览器中也是采用自动垃圾回收方法管理内存,但由于浏览器垃圾回收方法有bug,会产生内存泄露。
2、内存泄露的几种情况:
(1)、当页面中元素被移除或替换时,若元素绑定的事件仍没被移除,在IE中不会作出恰当处理,此时要先手工移除事件,不然会存在内存泄露。
实例如下:
<div id="myDiv">
<input type="button" value="Click me" id="myBtn">
</div>
<script type="text/javascript">
var btn = document.getElementById("myBtn");
btn.onclick = function(){
document.getElementById("myDiv").innerHTML = "Processing...";
}
</script>
解决方法如下:
<div id="myDiv">
<input type="button" value="Click me" id="myBtn">
</div>
<script type="text/javascript">
var btn = document.getElementById("myBtn");
btn.onclick = function(){
btn.onclick = null;
document.getElementById("myDiv").innerHTML = "Processing...";
}
</script>
(2)、由于是函数内定义函数,并且内部函数–事件回调的引用外暴了,形成了闭包。闭包可以维持函数内局部变量,使其得不到释放。
实例如下:
function bindEvent(){
var obj=document.createElement("XXX");
obj.onclick=function(){
//Even if it's a empty function
}
}
解决方法如下
function bindEvent(){
var obj=document.createElement("XXX");
obj.onclick=function(){
//Even if it's a empty function
}
obj=null;
}
10、javascript面向对象中继承实现?
面向对象的基本特征有:封闭、继承、多态。
在JavaScript中实现继承的方法:
-
原型链(prototype chaining)
-
call()/apply()
-
混合方式(prototype和call()/apply()结合)
-
对象冒充
继承的方法如下:
1、prototype原型链方式
function teacher(name){
this.name = name;
}
teacher.prototype.sayName = function(){
console.log("name is "+this.name);
}
var teacher1 = new teacher("xiaoming");
teacher1.sayName();
function student(name){
this.name = name;
}
student.prototype = new teacher()
var student1 = new student("xiaolan");
student1.sayName();
// name is xiaoming
// name is xiaolan
2、call()/apply()方法
function teacher(name,age){
this.name = name;
this.age = age;
this.sayhi = function(){
alert('name:'+name+", age:"+age);
}
}
function student(){
var args = arguments;
teacher.call(this,args[0],args[1]);
// teacher.apply(this,arguments);
}
var teacher1 = new teacher('xiaoming',23);
teacher1.sayhi();
var student1 = new student('xiaolan',12);
student1.sayhi();
// alert: name:xiaoming, age:23
// alert: name:xiaolan, age:12
3、混合方法【prototype,call/apply】
function teacher(name,age){
this.name = name;
this.age = age;
}
teacher.prototype.sayName = function(){
console.log('name:'+this.name);
}
teacher.prototype.sayAge = function(){
console.log('age:'+this.age);
}
function student(){
var args = arguments;
teacher.call(this,args[0],args[1]);
}
student.prototype = new teacher();
var student1 = new student('xiaolin',23);
student1.sayName();
student1.sayAge();
// name:xiaolin
// age:23
4.对象冒充
function Person(name,age){
this.name = name;
this.age = age;
this.show = function(){
console.log(this.name+", "+this.age);
}
}
function Student(name,age){
this.student = Person; //将Person类的构造函数赋值给this.student
this.student(name,age); //js中实际上是通过对象冒充来实现继承的
delete this.student; //移除对Person的引用
}
var s = new Student("小明",17);
s.show();
var p = new Person("小花",18);
p.show();
// 小明, 17
// 小花, 18
javascript相关程序计算题
1、判断一个字符串中出现次数最多的字符,统计这个次数
var str = 'asdfssaaasasasasaa';
var json = {};
for (var i = 0; i < str.length; i++) {
if(!json[str.charAt(i)]){
json[str.charAt(i)] = 1;
}else{
json[str.charAt(i)]++;
}
};
var iMax = 0;
var iIndex = '';
for(var i in json){
if(json[i]>iMax){
iMax = json[i];
iIndex = i;
}
}
console.log('出现次数最多的是:'+iIndex+'出现'+iMax+'次');
结果如下:出现次数最多的是:a出现9次
JavaScript 数组(Array)对象
1、Array相关的属性和方法
这里只是做了相关的列举,具体的使用方法,参考网址
Array 对象属性
constructor 返回对创建此对象的数组函数的引用。
var test=new Array();
if (test.constructor==Array)
{
document.write("This is an Array");
}
if (test.constructor==Boolean)
{
document.write("This is a Boolean");
}
if (test.constructor==Date)
{
document.write("This is a Date");
}
if (test.constructor==String)
{
document.write("This is a String");
}
length 设置或返回数组中元素的数目。
prototype 使您有能力向对象添加属性和方法。
Array 对象方法
concat() 连接两个或更多的数组,并返回结果。
var arr = [1,2,3,4];
var arr2 = [5,6,7,8];
var arr3 = arr.concat(arr2);
console.log(arr3); // 连接之后返回的数组为:[1, 2, 3, 4, 5, 6, 7, 8]
join() 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。
var arr = ['xiao','lin','qiqi','mingtian'];
var arr2 = arr.join(',');
console.log(arr2); // 根据','隔开返回的字符串为:"xiao,lin,qiqi,mingtian"
pop() 删除并返回数组的最后一个元素。
var arr = [2,3,4,5];
var arr2 = arr.pop();
console.log(arr2); // 删除的数组的最后一个元素为:5
console.log(arr); // 删除元素之后的数组为:[2, 3, 4]
shift() 删除并返回数组的第一个元素
var arr = [2,3,4,5];
var arr2 = arr.shift();
console.log(arr2); // 删除的数组的第一个元素为:2
console.log(arr); // 删除元素之后的数组为:[3, 4,5]
push() 向数组的末尾添加一个或更多元素,并返回新的长度。
var arr = [2,3,4,5];
var arr2 = arr.push(6);
console.log(arr2); // 返回的数组长度:5
console.log(arr); // [2, 3, 4, 5, 6]
unshift() 向数组的开头添加一个或更多元素,并返回新的长度。
var arr = ['xiao','ming','qiqi','aiming'];
var arr1 = arr.unshift('lang');
console.log(arr1); // 返回的数组的长度: 5
console.log(arr); //向数组开头添加元素返回的结果:["lang", "xiao", "ming", "qiqi", "aiming"]
reverse() 颠倒数组中元素的顺序。
var arr = [2,3,4,5];
arr.reverse();
console.log(arr); // [5, 4, 3, 2]
slice() 从某个已有的数组返回选定的元素
var arr = [2,3,4,5];
var arr2 = arr.slice(1,3);
console.log(arr2); // 截取区间返回的数组为:[3, 4]
console.log(arr); // [2, 3, 4, 5]
sort() 对数组的元素进行排序
借助排序函数,实现数值由小到大排序
function sortNumber(a,b){
return a - b
}
var arr = [23,30,42,5];
var arr2 = arr.sort(sortNumber);
console.log(arr2); // [5, 23, 30, 42]
console.log(arr); // [5, 23, 30, 42]
借助排序函数,实现数值由大到小排序
function sortNumber(a,b){
return b - a
}
var arr = [23,30,42,5];
var arr2 = arr.sort(sortNumber);
console.log(arr2); // [42, 30, 23, 5]
console.log(arr); // [42, 30, 23, 5]
splice() 删除元素,并向数组添加新元素。
语法:arrayObject.splice(index,howmany,item1,.....,itemX)
index:必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
howmany:必需。要删除的项目数量。如果设置为 0,则不会删除项目。
item1, ..., itemX:可选。向数组添加的新项目。
// 创建一个新数组,并向其添加一个元素
var arr = [1,2,3,4];
arr.splice(2,0,5);
console.log(arr); // [1, 2, 5, 3, 4]
// 删除位于 index 2 的元素,并添加一个新元素来替代被删除的元素:
var arr = [1,2,3,4];
arr.splice(2,1,5);
console.log(arr); // [1, 2, 5, 4]
toSource() 返回该对象的源代码。
览器支持
只有 Gecko 核心的浏览器(比如 Firefox)支持该方法,也就是说 IE、Safari、Chrome、Opera 等浏览器均不支持该方法。
<script type="text/javascript">
function employee(name,job,born){
this.name=name;
this.job=job;
this.born=born;
}
var bill = new employee("Bill Gates","Engineer",1985);
document.write(bill.toSource());
</script>
输出:({name:"Bill Gates", job:"Engineer", born:1985})
toString() 把数组转换为字符串,并返回结果。
var arr = ['xiao','ming','qiqi','aiming'];
arr.toString();
console.log(arr); // ["xiao", "ming", "qiqi", "aiming"]
toLocaleString() 把数组转换为本地数组,并返回结果。
var arr = ['xiao','ming','qiqi','aiming'];
arr.toLocaleString();
console.log(arr); // ["xiao", "ming", "qiqi", "aiming"]
valueOf() 返回数组对象的原始值
var arr = ['xiao','ming','qiqi','aiming'];
arr.valueOf('lang');
console.log(arr); // ["xiao", "ming", "qiqi", "aiming"]
2、编写一个方法 去掉一个数组的重复元素
方法一:
var arr = [0,2,3,4,4,0,2];
var obj = {};
var tmp = [];
for(var i = 0 ;i< arr.length;i++){
if( !obj[arr[i]] ){
obj[arr[i]] = 1;
tmp.push(arr[i]);
}
}
console.log(tmp);
结果如下: [0, 2, 3, 4]
方法二:
var arr = [2,3,4,4,5,2,3,6],
arr2 = [];
for(var i = 0;i< arr.length;i++){
if(arr2.indexOf(arr[i]) < 0){
arr2.push(arr[i]);
}
}
console.log(arr2);
结果为:[2, 3, 4, 5, 6]
方法三:
var arr = [2,3,4,4,5,2,3,6];
var arr2 = arr.filter(function(element,index,self){
return self.indexOf(element) === index;
});
console.log(arr2);
结果为:[2, 3, 4, 5, 6]
3、求数组的最值?
方法一:
求数组最大值:Math.max.apply(null,arr);
var arr = [3,43,23,45,65,90];
var max = Math.max.apply(null,arr);
console.log(max);
// 90
求数组最小值:Math.min.apply(null,arr);
var arr = [3,43,23,45,65,90];
var min = Math.min.apply(null,arr);
console.log(min);
// 3
方法二:Array.max = function(arr){} / Array.min = function(arr){}
var array = [3,43,23,45,65,90];
Array.max = function( array ){
return Math.max.apply( Math, array );
};
Array.min = function( array ){
return Math.min.apply( Math, array );
};
var max = Array.max(array);
console.log(max); // 90
var min = Array.min(array);
console.log(min); // 3
方法三:Array.prototype.max = function(){};Array.prototype.min = function(){};
求数组最大值(基本思路:将数组中的第一个值赋值给变量max ,将数组进行循环与max进行比较,将数组中的大值赋给max,最后返回max;)
var arr = [3,43,23,45,65,90];
Array.prototype.max = function() {
var max = this[0];
var len = this.length;
for (var i = 0; i < len; i++){
if (this[i] > max) {
max = this[i];
}
}
return max;
}
var max = arr.max();
console.log(max); // 90
求数组最小值:
var arr = [3,43,23,45,65,90];
Array.prototype.min = function() {
var min = this[0];
var len = this.length;
for(var i = 0;i< len;i++){
if(this[i] < min){
min = this[i];
}
}
return min;
}
var min = arr.min();
console.log(min); // 3
4、数组排序相关
结合sort和函数排序:
数组由小到大进行排序:sort,sortnum;
var arr = [3,43,23,45,65,90];
function sortnum(a,b){
return a-b;
}
arr = arr.sort(sortnum);
console.log(arr);
// [3, 23, 43, 45, 65, 90]
数组由大到小进行排序:sort,sortnum;
var arr = [3,43,23,45,65,90];
function sortnum(a,b){
return a+b;
}
arr = arr.sort(sortnum);
console.log(arr);
// [90, 65, 45, 23, 43, 3]
冒泡排序:即实现数组由小到大进行排序;思路为:每次比较相邻的两个数,如果后一个比前一个小,换位置。如果要实现由大到小排序,使用reverse()即可;
var arr = [3, 1, 4, 6, 5, 7, 2];
function bubbleSort(arr) {
var len = arr.length;
for (var i = len; i >= 2; --i) {
for (var j = 0; j < i - 1; j++) {
if (arr[j + 1] < arr[j]) {
var temp;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
var arr2 = bubbleSort(arr);
console.log(arr2); // [1, 2, 3, 4, 5, 6, 7]
var arr3 = arr2.reverse();
console.log(arr3); // [7, 6, 5, 4, 3, 2, 1]
快速排序:
思路:采用二分法,取出中间数,数组每次和中间数比较,小的放到左边,大的放到右边。
var arr = [3, 1, 4, 6, 5, 7, 2];
function quickSort(arr) {
if(arr.length == 0) {
return []; // 返回空数组
}
var cIndex = Math.floor(arr.length / 2);
var c = arr.splice(cIndex, 1);
var l = [];
var r = [];
for (var i = 0; i < arr.length; i++) {
if(arr[i] < c) {
l.push(arr[i]);
} else {
r.push(arr[i]);
}
}
return quickSort(l).concat(c, quickSort(r));
}
console.log(quickSort(arr));
//[1, 2, 3, 4, 5, 6, 7]
5、数组的翻转(非reverse())
方法一:
var arr = [1,2,3,4];
var arr2 = [];
while(arr.length) {
var num = arr.pop(); //删除数组最后一个元素并返回被删除的元素
arr2.push(num);
}
console.log(arr2);
// [4, 3, 2, 1]
方法二:
var arr = [1,2,3,4];
var arr2 = [];
while(arr.length){
var num = arr.shift(); //删除数组第一个元素并返回被删除的元素
arr2.unshift(num);
}
console.log(arr2);
jquery相关
1、 jQuery 库中的 $() 是什么?
( ) 函 数 是 j Q u e r y ( ) 函 数 的 别 称 。 () 函数是 jQuery() 函数的别称。 ()函数是jQuery()函数的别称。() 函数用于将任何对象包裹成 jQuery 对象,接着你就被允许调用定义在 jQuery 对象上的多个不同方法。你可以将一个选择器字符串传入 $() 函数,它会返回一个包含所有匹配的 DOM 元素数组的 jQuery 对象。
2、如何找到所有 HTML select 标签的选中项?
$('[name=selectname] :selected')
3、$(this) 和 this 关键字在 jQuery 中有何不同?
$(this) 返回一个 jQuery 对象,你可以对它调用多个 jQuery 方法,比如用 text() 获取文本,用val() 获取值等等。
而 this 代表当前元素,它是 JavaScript 关键词中的一个,表示上下文中的当前 DOM 元素。你不能对它调用 jQuery 方法,直到它被 $() 函数包裹,例如 $(this)。
4、jquery怎么移除标签onclick属性?
获得a标签的onclick属性:
(
"
a
"
)
.
a
t
t
r
(
"
o
n
c
l
i
c
k
"
)
删
除
o
n
c
l
i
c
k
属
性
:
("a").attr("onclick") 删除onclick属性:
("a").attr("onclick")删除onclick属性:(“a”).removeAttr(“onclick”);
设置onclick属性:$(“a”).attr(“onclick”,“test();”);
5、jquery中addClass,removeClass,toggleClass的使用。
$(selector).addClass(class):为每个匹配的元素添加指定的类名
$(selector).removeClass(class):从所有匹配的元素中删除全部或者指定的类,删除class中某个值;
$(selector).toggleClass(class):如果存在(不存在)就删除(添加)一个类
$(selector).removeAttr(class);删除class这个属性;
6、JQuery有几种选择器?
(1)、基本选择器:#id,class,element,*;
(2)、层次选择器:parent > child,prev + next ,prev ~ siblings
(3)、基本过滤器选择器::first,:last ,:not ,:even ,:odd ,:eq ,:gt ,:lt
(4)、内容过滤器选择器: :contains ,:empty ,:has ,:parent
(5)、可见性过滤器选择器::hidden ,:visible
(6)、属性过滤器选择器:[attribute] ,[attribute=value] ,[attribute!=value] ,[attribute^=value] ,[attribute$=value] ,[attribute*=value]
(7)、子元素过滤器选择器::nth-child ,:first-child ,:last-child ,:only-child
(8)、表单选择器: :input ,:text ,:password ,:radio ,:checkbox ,:submit 等;
(9)、表单过滤器选择器::enabled ,:disabled ,:checked ,:selected
7、jQuery中的Delegate()函数有什么作用?
delegate()会在以下两个情况下使用到:
1、如果你有一个父元素,需要给其下的子元素添加事件,这时你可以使用delegate()了,代码如下:
$("ul").delegate("li", "click", function(){ $(this).hide(); });
2、当元素在当前页面中不可用时,可以使用delegate()
8、$(document).ready()方法和window.onload有什么区别?
(1)、window.onload方法是在网页中所有的元素(包括元素的所有关联文件)完全加载到浏览器后才执行的。
(2)、$(document).ready() 方法可以在DOM载入就绪时就对其进行操纵,并调用执行绑定的函数。
9、如何用jQuery禁用浏览器的前进后退按钮?
<script type="text/javascript" language="javascript">
$(document).ready(function() {
window.history.forward(1);
//OR window.history.forward(-1);
});
</script>
10、 jquery中 . g e t ( ) 提 交 和 .get()提交和 .get()提交和.post()提交有区别吗?
相同点:都是异步请求的方式来获取服务端的数据;
异同点:
1、请求方式不同: . g e t ( ) 方 法 使 用 G E T 方 法 来 进 行 异 步 请 求 的 。 .get() 方法使用GET方法来进行异步请求的。 .get()方法使用GET方法来进行异步请求的。.post() 方法使用POST方法来进行异步请求的。
2、参数传递方式不同:get请求会将参数跟在URL后进行传递,而POST请求则是作为HTTP消息的实体内容发送给Web服务器的,这种传递是对用户不可见的。
3、数据传输大小不同:get方式传输的数据大小不能超过2KB 而POST要大的多
4、安全问题: GET 方式请求的数据会被浏览器缓存起来,因此有安全问题。
11、写出一个简单的$.ajax()的请求方式?
$.ajax({
url:'http://www.baidu.com',
type:'POST',
data:data,
cache:true,
headers:{},
beforeSend:function(){},
success:function(){},
error:function(){},
complete:function(){}
});
12、jQuery的事件委托方法bind 、live、delegate、on之间有什么区别?
(1)、bind 【jQuery 1.3之前】
定义和用法:主要用于给选择到的元素上绑定特定事件类型的监听函数;
语法:bind(type,[data],function(eventObject));
特点:
(1)、适用于页面元素静态绑定。只能给调用它的时候已经存在的元素绑定事件,不能给未来新增的元素绑定事件。
(2)、当页面加载完的时候,你才可以进行bind(),所以可能产生效率问题。
实例如下:$( “#members li a” ).bind( “click”, function( e ) {} );
(2)、live 【jQuery 1.3之后】
定义和用法:主要用于给选择到的元素上绑定特定事件类型的监听函数;
语法:live(type, [data], fn);
特点:
(1)、live方法并没有将监听器绑定到自己(this)身上,而是绑定到了this.context上了。
(2)、live正是利用了事件委托机制来完成事件的监听处理,把节点的处理委托给了document,新添加的元素不必再绑定一次监听器。
(3)、使用live()方法但却只能放在直接选择的元素后面,不能在层级比较深,连缀的DOM遍历方法后面使用,即 ( “ u l ” " ) . l i v e . . . 可 以 , 但 (“ul”").live...可以,但 (“ul”").live...可以,但(“body”).find(“ul”).live…不行;
实例如下:$( document ).on( “click”, “#members li a”, function( e ) {} );
(3)、delegate 【jQuery 1.4.2中引入】
定义和用法:将监听事件绑定在就近的父级元素上
语法:delegate(selector,type,[data],fn)
特点:
(1)、选择就近的父级元素,因为事件可以更快的冒泡上去,能够在第一时间进行处理。
(2)、更精确的小范围使用事件代理,性能优于.live()。可以用在动态添加的元素上。
实例如下:
$("#info_table").delegate(“td”,“click”,function(){/显示更多信息/});
$(“table”).find("#info").delegate(“td”,“click”,function(){/显示更多信息/});
(4)、on 【1.7版本整合了之前的三种方式的新事件绑定机制】
定义和用法:将监听事件绑定到指定元素上。
语法:on(type,[selector],[data],fn)
实例如下:$("#info_table").on(“click”,“td”,function(){/显示更多信息/});参数的位置写法与delegate不一样。
说明:on方法是当前JQuery推荐使用的事件绑定方法,附加只运行一次就删除函数的方法是one()。
总结:.bind(), .live(), .delegate(),.on()分别对应的相反事件为:.unbind(),.die(), .undelegate(),.off()