前端笔试题总结

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

将此方法添加至 Array.prototype 实现,代码如下:

Array.prototype.duplicator = function(){
  var l = this.length,i;
      for(i=0;i<l;i++){
       this.push(this[i]) 
       }
}

请实现一个遍历至 100 的 for loop 循环,在能被 3 整除时输出 “fizz”,在能被 5 整除时输出 “buzz”,在能同时被 3 和 5 整除时输出 “fizzbuzz”。

for (var i = 1; i <= 30; i++) {

    if (i % 3 === 0) {
        if (i % 5 === 0) {
            alert('fizzbuzz' + i);
            continue;
        }
        alert('fizz' + i);
        continue;
    } else if (i % 5 === 0) {
        if (i % 3 === 0) {
            alert('fizzbuzz' + i);
            continue;
        }
        alert('buzz' + i);
        continue;
    }
}

下面的代码将输出什么到控制台,为什么?

(function(){ 
    var a = b = 3;
})(); 
console.log("a defined? " + (typeof a !== 'undefined'));
console.log("b defined? " + (typeof b !== 'undefined'));

输出: a defined? false b defined? true

考虑以下两个函数。它们会返回相同的东西吗? 为什么相同或为什么不相同?

function foo1(){ return {
    bar: "hello"
};
}function foo2(){ return
{
    bar: "hello"
};
}

返回不相同: foo1 returns:Object {bar: "hello"}foo2 returns:undefined

分号在JavaScript中是一个可选项(尽管省略它们通常是非常糟糕的形式)。其结果就是,当碰到 foo2()中包含 return语句的代码行(代码行上没有其他任何代码),分号会立即自动插入到返回语句之后。也不会抛出错误,因为代码的其余部分是完全有效的,即使它没有得到调用或做任何事情(相当于它就是是一个未使用的代码块,定义了等同于字符串 "hello"的属性 bar)。

这种行为也支持放置左括号于JavaScript代码行的末尾,而不是新代码行开头的约定。正如这里所示,这不仅仅只是JavaScript中的一个风格偏好。

下列代码将输出什么?并解释原因

console.log(0.1 + 0.2);console.log(0.1 + 0.2 == 0.3);

JavaScript中的数字和浮点精度的处理相同,因此,可能不会总是产生预期的结果。
以上所提供的例子就是一个演示了这个问题的典型例子。但出人意料的是,它会输出:
0.30000000000000004false

下列代码行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

比较明显而易见的那部分:
1 和 4之所以放在前面,是因为它们是通过简单调用 console.log() 而没有任何延迟输出的
2 之所以放在 3的后面,是因为 2 是延迟了1000毫秒(即,1秒)之后输出的,而 3 是延迟了0毫秒之后输出的。
好的。但是,既然 3 是0毫秒延迟之后输出的,那么是否意味着它是立即输出的呢?如果是的话,那么它是不是应该在 4 之前输出,既然 4 是在第二行输出的?
要回答这个问题,你需要正确理解JavaScript的事件和时间设置。
浏览器有一个事件循环,会检查事件队列和处理未完成的事件。例如,如果时间发生在后台(例如,脚本的 onload 事件)时,浏览器正忙(例如,处理一个 onclick),那么事件会添加到队列中。当onclick处理程序完成后,检查队列,然后处理该事件(例如,执行 onload 脚本)。
同样的, setTimeout() 也会把其引用的函数的执行放到事件队列中,如果浏览器正忙的话。
当setTimeout()的第二个参数为0的时候,它的意思是“尽快”执行指定的函数。具体而言,函数的执行会放置在事件队列的下一个计时器开始。但是请注意,这不是立即执行:函数不会被执行除非下一个计时器开始。这就是为什么在上述的例子中,调用 console.log(4) 发生在调用 console.log(3) 之前(因为调用 console.log(3) 是通过setTimeout被调用的,因此会稍微延迟)。

写一个简单的函数(少于80个字符),要求返回一个布尔值指明字符串是否为回文结构。

下面这个函数在 str 是回文结构的时候返回true,否则,返回false。

function isPalindrome(str) {
    str = str.replace(/\W/g, '').toLowerCase(); return (str == str.split('').reverse().join(''));
}

例如:

console.log(isPalindrome("level")); // logs 'true'console.log(isPalindrome("levels")); // logs 'false'console.log(isPalindrome("A car, a man, a maraca")); // logs 'true'

写一个 sum方法,在使用下面任一语法调用时,都可以正常工作

console.log(sum(2,3)); // Outputs 5
console.log(sum(2)(3)); // Outputs 5

(至少)有两种方法可以做到:

方法1:


function sum(x) { 
    if (arguments.length == 2) { 
            return arguments[0] + arguments[1];
        }else {
            return function(y) { 
            return x + y; 
        };
    }
}

方法2:

function sum(x, y) { 
    if (y !== undefined) { 
        return x + y;
    } else { 
        return function(y) { 
            return x + y; 
        };
    }
}

请看下面的代码片段

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)当用户点击“Button 4”的时候会输出什么到控制台,为什么?
无论用户点击什么按钮,数字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);

    });

下面的代码将输出什么到控制台,为什么?

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" 

调用数组对象的 reverse() 方法并不只返回反顺序的阵列,它也反转了数组本身的顺序(即,在这种情况下,指的是 arr1)。
reverse() 方法返回一个到数组本身的引用(在这种情况下即,arr1)。其结果为,arr2 仅仅是一个到 arr1的引用(而不是副本)。因此,当对 arr2做了任何事情(即当我们调用 arr2.push(arr3);)时,arr1 也会受到影响,因为 arr1 和 arr2 引用的是同一个对象。

注意:
传递数组到另一个数组的 push() 方法会让整个数组作为单个元素映射到数组的末端。其结果是,语句 arr2.push(arr3); 在其整体中添加 arr3 作为一个单一的元素到 arr2 的末端(也就是说,它并没有连接两个数组,连接数组是 concat() 方法的目的)。
和Python一样,JavaScript标榜数组方法调用中的负数下标,例如 slice() 可作为引用数组末尾元素的方法:例如,-1下标表示数组中的最后一个元素,等等。

下面的代码将输出什么到控制台,为什么?

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" 都不能转换成数值,因此,"A" - "B"的结果是 NaN,然后再和字符串 "2" 连接,得到 "NaN2" 。
例6: "A" - "B" + 2 输出: NaN 说明:参见前一个例子, "A" - "B" 结果为 NaN。但是,应用任何运算符到NaN与其他任何的数字运算对象,结果仍然是 NaN。

下面的递归代码在数组列表偏大的情况下会导致堆栈溢出。在保留递归模式的基础上,你怎么解决这个问题?

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不为空,timeout函数(nextListItem)就会被推到事件队列,该函数退出,因此就清空调用堆栈。当事件队列运行其timeout事件,且进行到下一个 item 时,定时器被设置为再次调用 extListItem。因此,该方法从头到尾都没有直接的递归调用,所以无论迭代次数的多少,调用堆栈保持清空的状态。

下面的代码将输出什么

for (var i = 0; i < 5; i++) {
    setTimeout(function() { console.log(i); }, i * 1000 );
}

解释你的答案。闭包在这里能起什么作用?
上面的代码不会按预期显示值0,1,2,3,和4,而是会显示5,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到控制台。

以下代码行将输出什么到控制台?

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 = 11 || 2 = 10 && 1 = 01 && 2 = 2

在JavaScript中, || 和 &&都是逻辑运算符,用于在从左至右计算时,返回第一个可完全确定的“逻辑值”。
或( || )运算符。在形如 X||Y的表达式中,首先计算X 并将其解释执行为一个布尔值。如果这个布尔值true,那么返回true(1),不再计算 Y,因为“或”的条件已经满足。如果这个布尔值为false,那么我们仍然不能知道 X||Y是真是假,直到我们计算 Y,并且也把它解释执行为一个布尔值。

因此, 0 || 1 的计算结果为true(1),同理计算1 || 2。
与( &&)运算符。在形如 X&&Y的表达式中,首先计算 X并将其解释执行为一个布尔值。如果这个布尔值为 false,那么返回 false(0),不再计算 Y,因为“与”的条件已经失败。如果这个布尔值为true,但是,我们仍然不知道 X&&Y 是真是假,直到我们去计算 Y,并且也把它解释执行为一个布尔值。

不过,关于 &&运算符有趣的地方在于,当一个表达式计算为“true”的时候,那么就返回表达式本身。这很好,虽然它在逻辑表达式方面计算为“真”,但如果你希望的话也可用于返回该值。这就解释了为什么,有些令人奇怪的是, 1 && 2返回 2(而不是你以为的可能返回 true 或 1)。

执行下面的代码时将输出什么?请解释。

console.log(false == '0')
console.log(false === '0')

代码将输出:

true false

在JavaScript中,有两种等式运算符。三个等于运算符 === 的作用类似传统的等于运算符:如果两侧的表达式有着相同的类型和相同的值,那么计算结果为true。而双等于运算符,会只强制比较它们的值。因此,总体上而言,使用 ===而不是 ==的做法更好。 !==vs !=亦是同理。

以下代码将输出什么?并解释你的答案。

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]均相当于a["[object Object]"] ,并可以互换使用。因此,设置或引用 a[c]和设置或引用 a[b]完全相同。

以下代码行将输出什么到控制台?

console.log((function f(n){return ((n > 1) ? n * f(n-1) : n)})(10));

代码将输出10!的值(即10!或3628800)。
原因是:
命名函数 f()递归地调用本身,当调用 f(1)的时候,只简单地返回1。下面就是它的调用过程:
f(1): returns n, which is 1f(2): returns 2 f(1), which is 2f(3): returns 3 f(2), which is 6f(4): returns 4 f(3), which is 24f(5): returns 5 f(4), which is 120f(6): returns 6 f(5), which is 720f(7): returns 7 f(6), which is 5040f(8): returns 8 f(7), which is 40320f(9): returns 9 f(8), which is 362880f(10): returns 10 * f(9), which is 3628800

请看下面的代码段。控制台将输出什么,为什么?

(function(x) { 
    return (function(y) {         
            console.log(x);
        })(2)
})(1);

控制台将输出 1,即使从来没有在函数内部设置过x的值。原因是:
闭包是一个函数,连同在闭包创建的时候,其范围内的所有变量或函数一起。在JavaScript中,闭包是作为一个“内部函数”实施的:即,另一个函数主体内定义的函数。闭包的一个重要特征是,内部函数仍然有权访问外部函数的变量。
因此,在本例中,由于 x未在函数内部中定义,因此在外部函数范围中搜索定义的变量 x,且被发现具有1的值。

下面的代码将输出什么到控制台,为什么

var hero = {
    _name: 'John Doe',
    getSecretIdentity: function (){ 
        return this._name;
    }
}; 
var stoleSecretIdentity = hero.getSecretIdentity;
console.log(stoleSecretIdentity());
console.log(hero.getSecretIdentity());

代码将输出:

undefinedJohn Doe

第一个 console.log之所以输出 undefined,是因为我们正在从 hero对象提取方法,所以调用了全局上下文中(即窗口对象)的 stoleSecretIdentity(),而在此全局上下文中, _name属性不存在。
其中一种修复stoleSecretIdentity() 函数的方法如下:
var stoleSecretIdentity = hero.getSecretIdentity.bind(hero);

创建一个给定页面上的一个DOM元素,就会去访问元素本身及其所有子元素(不只是它的直接子元素)的函数。对于每个被访问的元素,函数应该传递元素到提供的回调函数

此函数的参数为:
DOM元素
回调函数(将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
    }
}

看代码说出打印值?

这道题的代码具体不清了,考查的是数组类似下面这样

var a = [,1],
    b = [,1,,1],
    c = [1,];
    console.log(a.length); 
    console.log(b.length);
    console.log(c.length);
    

打印: 2 4 1

以下哪些方法属于全局函数?在window对象下

A eval
B parseFloat
C setTimeout
D alert
E enscape

clipboard.png

答案: ABCDE

下面哪段代码会报错?

A var a = ()
B var b = {}
C var c = []
D var d = 

答案: A

以下哪些方法可以在前端跨域?

A JSONP
B script 标签
C iframe 和 location.hash
D flash
E postMessage

答案: ABCDE
参考:http://www.cnblogs.com/rainma...
clipboard.png

看下列代码,将会输出什么?(考查变量声明提升)

var foo = 1;
function(){
    console.log(foo);
    var foo = 2;
    console.log(foo);
}

输出undefined 和 2

以下代码输出什么?(考查逻辑表达式)

console.log(1 && 2);

输出: 2

以下代码输出什么?(javascript)

console.log(1+"2"+"2"); 
console.log(1+ +"2"+"2");
console.log(1+ -"1"+2);
console.log(1+ -"1"+"2");
console.log(+"1"+2);
console.log(1+ "2");
console.log("A"-"B"+"2");
console.log("A"-"B"+2);

输出: 122 32 2 02 3 12 NaN2 NaN

阅读 681

推荐阅读