25个最基本的JavaScript面试问题及答案

本文由vwin德赢网 – 小峰原创翻译,转载请看清文末的转载要求,欢迎参加咱们的付费投稿计划

1.运用 typeof bar === "object" 来确认 bar 是否是目标的潜在圈套是什么?怎样避免这个圈套?

尽管 typeof bar === "object" 是查看 bar 是否目标的牢靠办法,令人惊奇的是在JavaScript中 null 也被认为是目标!

因而,令大多数开发人员惊奇的是,下面的代码将输出 true (而不是false) 到控制台:

var bar = null;
console.log(typeof bar === "object");  // logs true!

只需清楚这一点,一同查看 bar 是否为 null,就能够很简略地避免问题:

console.log((bar !== null) && (typeof bar === "object"));  // logs false

要答全问题,还有其他两件作业值得留意:

首要,上述处理计划将回来 false,当 bar 是一个函数的时分。在大多数情况下,这是期望行为,但当你也想对函数回来 true 的话,你能够修正上面的处理计划为:

console.log((bar !== null) && ((typeof bar === "object") || (typeof bar === "function")));

第二,上述处理计划将回来 true,当 bar 是一个数组(例如,当 var bar = [];)的时分。在大多数情况下,这是期望行为,因为数组是真实的目标,但当你也想对数组回来 false 时,你能够修正上面的处理计划为:

console.log((bar !== null) && (typeof bar === "object") && (toString.call(bar) !== "[object Array]"));

或许,假如你运用jQuery的话:

console.log((bar !== null) && (typeof bar === "object") && (! $.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 在上面的比如中都是undefined。

可是,现实并非如此。这儿的问题是,大多数开发人员将句子 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 关键字),因而依然在规模内乃至关闭函数之外。

需求留意的是,在严厉办法下(即运用 use strict),句子var a = b = 3; 将生成ReferenceError: b is not defined的运转时过错,然后避免任何不然或许会导致的headfakes /bug。 (仍是你为什么应该天经地义地在代码中运用 use 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

在内部函数中, this 不再指向 myObject。其成果是,this.foo 没有在内部函数中被界说,相反,指向到本地的变量self 坚持在规模内,而且能够拜访。 (在ECMA 5之前,在内部函数中的this 将指向大局的 window 目标;反之,因为作为ECMA 5,内部函数中的功用this 是未界说的。)

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中最常见的过错之一。在严厉办法下,这样做的话会抛出过错。
  • 消除 this 强制。假如没有严厉办法,引证null或未界说的值到 this 值会主动强制到大局变量。这或许会导致许多令人头痛的问题和让人恨不能拔自己头发的bug。在严厉办法下,引证 null或未界说的 this 值会抛出过错。
  • 不答应重复的特色称号或参数值。当检测到目标(例如,var object = {foo: "bar", foo: "baz"};)中重复命名的特色,或检测到函数中(例如,function foo(val1, val2, val1){})重复命名的参数时,严厉办法会抛出过错,因而捕捉简直能够肯定是代码中的bug能够避免糟蹋很多的盯梢时刻。
  • 使eval() 更安全。在严厉办法和非严厉办法下,eval() 的行为办法有所不同。最清楚明了的是,在严厉办法下,变量和声明在 eval() 句子内部的函数不会在包括规模内创立(它们会在非严厉办法下的包括规模中被创立,这也是一个常见的问题源)。
  • 在 delete运用无效时抛出过错。delete操作符(用于从目标中删去特色)不能用在目标不行装备的特色上。当企图删去一个不行装备的特色时,非严厉代码将静静地失利,而严厉办法将在这样的情况下抛出反常。

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:
undefined

这不仅是令人惊奇,而且特别让人困惑的是, foo2()回来undefined却没有任何过错抛出。

原因与这样一个现实有关,即分号在JavaScript中是一个可选项(尽管省掉它们一般是十分糟糕的办法)。其成果便是,当碰到 foo2()中包括 return句子的代码行(代码行上没有其他任何代码),分号会当即主动刺进到回来句子之后。

也不会抛出过错,因为代码的其余部分是彻底有用的,即便它没有得到调用或做任何作业(适当于它便是是一个未运用的代码块,界说了等同于字符串 "hello"的特色 bar)。

这种行为也支撑放置左括号于JavaScript代码行的结尾,而不是新代码行最初的约好。正如这儿所示,这不仅仅只是JavaScript中的一个风格偏好。

7. NaN 是什么?它的类型是什么?你怎样牢靠地测验一个值是否等于 NaN ?

NaN 特色代表一个“不是数字”的值。这个特别的值是因为运算不能履行而导致的,不能履行的原因要么是因为其间的运算目标之一非数字(例如, "abc" / 4),要么是因为运算的成果非数字(例如,除数为零)。

尽管这看上去很简略,但 NaN 有一些令人惊奇的特色,假如你不知道它们的话,或许会导致令人头痛的bug。

首要,尽管 NaN 意味着“不是数字”,可是它的类型,不论你信不信,是 Number

console.log(typeof NaN === "number");  // logs "true"

此外, NaN 和任何东西比较——乃至是它自己自身!——成果是false:

console.log(NaN === NaN);  // logs "false"

一种半牢靠的办法来测验一个数字是否等于 NaN,是运用内置函数 isNaN(),但即便运用 isNaN() 依然并非是一个完美的处理计划。

一个更好的处理办法是运用 value !== value,假如值等于NaN,只会发作true。别的,ES6供给了一个新的 Number.isNaN() 函数,这是一个不同的函数,而且比老的大局 isNaN() 函数更牢靠。

8.下列代码将输出什么?并解说原因。

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

一个略微有点VWIN首页根底的答复是:“你不能确认。或许会输出“0.3”和“true”,也或许不会。JavaScript中的数字和浮点精度的处理相同,因而,或许不会总是发作预期的成果。“

以上所供给的比如便是一个演示了这个问题的典型比如。但出其不意的是,它会输出:

0.30000000000000004
false

9.谈论写函数 isInteger(x) 的或许办法,用于确认x是否是整数。

这或许听起来是小菜一碟,但现实上,这很琐碎,因为ECMAScript 6引入了一个新的正以此为意图 Number.isInteger() 函数。可是,之前的ECMAScript 6,会更杂乱一点,因为没有供给相似的 Number.isInteger() 办法。

问题是,在ECMAScript标准阐明中,整数只概念上存在:即,数字值总是存储为浮点值。

考虑到这一点,最简略又最洁净的ECMAScript6之前的处理办法(一同也十分稳健地回来 false ,即便一个非数字的值,如字符串或 null ,被传递给函数)如下:

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

10.下列代码行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被调用的,因而会略微推迟)。

11.写一个简略的函数(少于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'

12.写一个 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; };
  }
}

在JavaScript中,函数能够供给到 arguments 目标的拜访,arguments 目标供给传递到函数的实践参数的拜访。这使咱们能够运用 length 特色来确认在运转时传递给函数的参数数量。

假如传递两个参数,那么只需加在一同,并回来。

不然,咱们假定它被以 sum(2)(3)这样的办法调用,所以咱们回来一个匿名函数,这个匿名函数兼并了传递到 sum()的参数和传递给匿名函数的参数。

办法2

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

当调用一个函数的时分,JavaScript不要求参数的数目匹配函数界说中的参数数量。假如传递的参数数量大于函数界说中参数数量,那么剩余参数将简略地被疏忽。另一方面,假如传递的参数数量小于函数界说中的参数数量,那么短少的参数在函数中被引证时将会给一个 undefined值。所以,在上面的比如中,简略地查看第2个参数是否未界说,就能够相应地确认函数被调用以及进行的办法。

13.请看下面的代码片段:

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”的时分会输出什么到控制台,为什么?(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);
});

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"

arr1 和 arr2 在上述代码履行之后,两者相同了,原因是:

  • 调用数组目标的 reverse() 办法并不只回来反次序的阵列,它也反转了数组自身的次序(即,在这种情况下,指的是 arr1)。
  •  reverse() 办法回来一个到数组自身的引证(在这种情况下即,arr1)。其成果为,arr2 仅仅是一个到 arr1的引证(而不是副本)。因而,当对 arr2做了任何作业(即当咱们调用 arr2.push(arr3);)时,arr1 也会受到影响,因为 arr1 和 arr2 引证的是同一个目标。

这儿有几个侧面点有时分会让你在答复这个问题时,阴沟里翻船:

传递数组到另一个数组的 push() 办法会让整个数组作为单个元素映射到数组的结尾。其成果是,句子 arr2.push(arr3); 在其全体中增加 arr3 作为一个单一的元素到 arr2 的结尾(也便是说,它并没有衔接两个数组,衔接数组是 concat() 办法的意图)。

和Python相同,JavaScript标榜数组办法调用中的负数下标,例如 slice() 可作为引证数组结尾元素的办法:例如,-1下标表明数组中的最终一个元素,等等。

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" 都不能转化成数值,因而,"A" - "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不为空,timeout函数(nextListItem)就会被推到事情行列,该函数退出,因而就清空调用仓库。当事情行列运转其timeout事情,且进行到下一个 item 时,定时器被设置为再次调用 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,和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到控制台。

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

在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)。

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]均适当于a["[object Object]"] ,并能够交换运用。因而,设置或引证 a[c]和设置或引证 a[b]彻底相同。

22.以下代码即将输出什么到控制台?

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 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之所以输出 undefined,是因为咱们正在从 hero目标提取办法,所以调用了大局上下文中(即窗口目标)的 stoleSecretIdentity(),而在此大局上下文中, _name特色不存在。

其间一种修正stoleSecretIdentity() 函数的办法如下:

var stoleSecretIdentity = hero.getSecretIdentity.bind(hero);

25.创立一个给定页面上的一个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
   }
}

译文链接:http://www.jf68bumn.com/article/25-essential-javascript-interview-questions.html
英文原文:25 Essential JavaScript Interview Questions
翻译作者:vwin德赢网 – 小峰
转载必须在正文中标示并保存原文链接、译文链接和译者等信息。]

宣布我的谈论

撤销谈论
表情 插代码
vwin娱乐场

Hi,您需求填写昵称和邮箱!

  • 必填项
  • 必填项