首頁  >  文章  >  web前端  >  24 個 JavaScript 面試題

24 個 JavaScript 面試題

小云云
小云云原創
2017-11-16 15:14:404594瀏覽

24 個 JavaScript 面試題

如今越來越多的人選擇了程式設計師這一行業,當我們學成之後就要出來找工作了,那麼在面試的時候有的人不免會很緊張膽怯。在程式設計師面試的時候,除了有面試官還有專門考核能力的面試題,所以本篇文章就講述的是24 個 JavaScript 面試題,這下小伙伴們有福利了,還不趕快來get!

推薦相關文章2020年最全js面試題整理(最新)

1、使用typeof bar === "object" 判斷bar 是不是一個物件有神馬潛在的弊端?如何避免這種弊端?

let obj = {};let arr = [];console.log(typeof obj === 'object');  //trueconsole.log(typeof arr === 'object');  //trueconsole.log(typeof null === 'object');  //tru

從上面的輸出結果可知,typeof bar === "object" 並不能準確判斷 bar 就是一個 Object。可以透過 Object.prototype.toString.call(bar) === "[object Object]" 來避免這種弊端:

let obj = {};let arr = [];console.log(Object.prototype.toString.call(obj));
  //[object Object]console.log(Object.prototype.toString.call(arr));  //[object Array]console.log(Object.prototype.toString.call(null));  //[object Null]

2、下面的程式碼會在 console 輸出神馬?為什麼?

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

這跟變數作用域有關,輸出換成下面的:

console.log(b); //3console,log(typeof a); //undefined

拆解一下自執行函數中的變數賦值:

b = 3;var a = b;

所以b 變成了全域變量,而a 是自執行函數的局部變數。

3、下面的程式碼會在 console 輸出神馬?為什麼?

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();

第一個和第二個的輸出不難判斷,在ES6 之前,JavaScript 只有函數作用域,所以func 中的IIFE 有自己的獨立作用域,並且它能訪問到外部作用域中的self,所以第三個輸出會報錯,因為this 在可訪問到的作用域內是undefined,第四個輸出是bar。如果你知道閉包,也很容易解決的:

(function(test) {console.log("inner func:  this.foo = " + test.foo);  //'bar'console.log("inner func:  self.foo = " + self.foo);}(self));

如果對閉包不熟悉,可以戳此:從作用域鏈談閉包

4、將JavaScript 程式碼包含在一個函數塊中有神馬意思呢?為什麼要這麼做?

換句話說,為什麼要用立即執行函數表達式(Immediately-Invoked Function Expression)。

IIFE 有兩個比較經典的使用場景,一是類似於在循環中定時輸出資料項,二是類似 JQuery/Node 的插件和模組開發。

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

上面的輸出並不是你以為的0,1,2,3,4,而輸出的全部是5,這時IIFE 就能有用了:

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

而在JQuery /Node 的插件和模組開發中,為避免變數污染,也是一個大大的IIFE:

(function($) { 
        //代码
 } )(jQuery);

5、在嚴格模式('use strict')下進行JavaScript 開發有神馬好處?

消除Javascript語法的一些不合理、不嚴謹之處,減少一些怪異行為;

#消除程式碼運作的一些不安全之處,保證程式碼運作的安全性;

提高編譯器效率,增加運行速度;

為未來新版本的Javascript做好鋪墊。

6、下面兩個函數的回傳值是一樣的嗎?為什麼?

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

在程式語言中,基本上都是使用分號(;)將語句分隔開,可以增加程式碼的可讀性和整潔性。而在JS中,如若語句各佔獨立一行,通常可以省略語句間的分號(;),JS 解析器會根據能否正常編譯來決定是否自動填入分號:

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

在上述情況下,為了正確解析程式碼,就不會自動填入分號了,但是對於return 、break、continue 等語句,如果後面緊跟換行,解析器一定會自動在後面填充分號( ; ),所以上面的第二個函數就變成這樣:

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

所以第二個函數是回傳undefined了。

7、神馬是 NaN,它的類型是神馬?怎麼測試一個值是否等於NaN?

NaN 是Not a Number 的縮寫,JavaScript 的一種特殊數值,其型別是Number,可以透過isNaN(param) 來判斷一個值是否是NaN:

console.log(isNaN(NaN)); //trueconsole.log(isNaN(23));
 //falseconsole.log(isNaN(&#39;ds&#39;)); //trueconsole.log(isNaN(&#39;32131sdasd&#39;));
  //trueconsole.log(NaN === NaN); //falseconsole.log(NaN === undefined); 
  //falseconsole.log(undefined === undefined); //falseconsole.log(typeof NaN);
   //numberconsole.log(Object.prototype.toString.call(NaN)); 
   //[object Number]ES6 中,isNaN() 成为了 Number 的静态方法:Number.isNaN().

8、解釋一下下面程式碼的輸出

console.log(0.1 + 0.2);   //0.30000000000000004console.log(0.1 + 0.2 == 0.3);  //false

JavaScript 中的number 類型就是浮點型,JavaScript 中的浮點數採用IEEE-754 格式的規定,這是一種二進位表示法,可以精確地表示分數,例如1/2,1/8,1/1024,每個浮點數佔64位數。但是,二進制浮點數表示法並不能精確的表示類似0.1這樣 的簡單的數字,會有捨入誤差。

由於採用二進位,JavaScript 也不能​​有限表示 1/10、1/2 等這樣的分數。在二進位中,1/10(0.1)被表示為0.00110011001100110011…… 注意0011 是無限重複的,這是捨入誤差造成的,所以對於0.1 + 0.2 這樣的運算,操作數會先被轉成二進制,然後再計算:

0.1 => 0.0001 1001 1001 1001…(無限循環)
0.2 => 0.0011 0011 0011 0011…(無限循環)

雙精度浮點數的小數部分最多支援52 位,所以兩者相加之後得到這麼一串0.0100110011001100110011001100110011001100...因浮點數小數位的限製而截斷的二進制數字,這時候,再把它轉換為十進制小數位的限製而截斷的二進制數字,這時候,再把它轉換為十進制。

对于保证浮点数计算的正确性,有两种常见方式。

一是先升幂再降幂:

function add(num1, num2){
  let r1, r2, m;
  r1 = (&#39;&#39;+num1).split(&#39;.&#39;)[1].length;
  r2 = (&#39;&#39;+num2).split(&#39;.&#39;)[1].length;
  m = Math.pow(10,Math.max(r1,r2));
  return (num1 * m + num2 * m) / m;}console.log(add(0.1,0.2));   //0.3console.log(add(0.15,0.2256)); //0.3756

二是是使用内置的 toPrecision() 和 toFixed() 方法,注意,方法的返回值字符串。

function add(x, y) {
    return x.toPrecision() + y.toPrecision()}console.log(add(0.1,0.2));  //"0.10.2"

9、实现函数 isInteger(x) 来判断 x 是否是整数

可以将 x 转换成10进制,判断和本身是不是相等即可:

function isInteger(x) { return parseInt(x, 10) === x; }

ES6 对数值进行了扩展,提供了静态方法 isInteger() 来判断参数是否是整数:

Number.isInteger(25) // trueNumber.isInteger(25.0) // trueNumber.isInteger(25.1) // falseNumber.isInteger("15") // falseNumber.isInteger(true) // false

JavaScript能够准确表示的整数范围在 -2^53 到 2^53 之间(不含两个端点),超过这个范围,无法精确表示这个值。ES6 引入了Number.MAX_SAFE_INTEGER 和 Number.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限,并提供了 Number.isSafeInteger() 来判断整数是否是安全型整数。

10、在下面的代码中,数字 1-4 会以什么顺序输出?为什么会这样输出?

(function() {
    console.log(1); 
    setTimeout(function(){console.log(2)}, 1000); 
    setTimeout(function(){console.log(3)}, 0); 
    console.log(4);})();

这个就不多解释了,主要是 JavaScript 的定时机制和时间循环,不要忘了,JavaScript 是单线程的。

11、写一个少于 80 字符的函数,判断一个字符串是不是回文字符串

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

12、写一个按照下面方式调用都能正常工作的 sum 方法

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

针对这个题,可以判断参数个数来实现:

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

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

var arr1 = "john".split(&#39;&#39;); j o h nvar arr2 = arr1.reverse(); n h o jvar arr3 = "jones".split(&#39;&#39;); j o n e s
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));

会输出什么呢?你运行下就知道了,可能会在你的意料之外。

MDN 上对于 reverse() 的描述是酱紫的:

>DescriptionThe reverse method transposes the elements of the calling array object in place, mutating the array, and returning a reference to the array.>reverse()

会改变数组本身,并返回原数组的引用。>slice 的用法请参考:slice

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

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);

输出什么,自己去运行吧,需要注意三个点:

多个数字和数字字符串混合运算时,跟操作数的位置有关

console.log(2 + 1 + &#39;3&#39;); / /‘33’console.log(&#39;3&#39; + 2 + 1); //&#39;321&#39;

数字字符串之前存在数字中的正负号(+/-)时,会被转换成数字

console.log(typeof &#39;3&#39;);   // stringconsole.log(typeof +&#39;3&#39;);  //number

同样,可以在数字前添加 '',将数字转为字符串

console.log(typeof 3);   // numberconsole.log(typeof (&#39;&#39;+3));  //string

对于运算结果不能转换成数字的,将返回 NaN

console.log(&#39;a&#39; * &#39;sd&#39;);   //NaNconsole.log(&#39;A&#39; - &#39;B&#39;);  // NaN

这张图是运算转换的规则

24 個 JavaScript 面試題

15、如果 list 很大,下面的这段递归代码会造成堆栈溢出。如果在不改变递归模式的前提下修善这段代码?

var list = readHugeList();var nextListItem = function() {
    var item = list.pop();
    if (item) {
        // process the list item...
        nextListItem();
    }};

原文上的解决方式是加个定时器:

var list = readHugeList();var nextListItem = function() {
    var item = list.pop();
    if (item) {
        // process the list item...
        setTimeout( nextListItem, 0);
    }};

解决方式的原理请参考第10题。

16、什么是闭包?举例说明

可以参考此篇:从作用域链谈闭包(去看看)

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

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

请往前面翻,参考第4题,解决方式已经在上面了

18、解释下列代码的输出

console.log("0 || 1 = "+(0 || 1));console.log("1 || 2 = "+(1 || 2));console.log("0 && 1 = "+(0 && 1));console.log("1 && 2 = "+(1 && 2));

逻辑与和逻辑或运算符会返回一个值,并且二者都是短路运算符:

逻辑与返回第一个是 false 的操作数 或者 最后一个是 true的操作数

console.log(1 && 2 && 0);  //0console.log(1 && 0 && 1);  //0console.log(1 && 2 && 3);  //3

如果某个操作数为 false,则该操作数之后的操作数都不会被计算

逻辑或返回第一个是 true 的操作数 或者 最后一个是 false的操作数

console.log(1 || 2 || 0); //1console.log(0 || 2 || 1); //2console.log(0 || 0 || false); //false

如果某个操作数为 true,则该操作数之后的操作数都不会被计算

如果逻辑与和逻辑或作混合运算,则逻辑与的优先级高:

console.log(1 && 2 || 0); //2console.log(0 || 2 && 1); //1console.log(0 && 2 || 1); //1

在 JavaScript,常见的 false 值:

0, &#39;0&#39;, +0, -0, false, &#39;&#39;,null,undefined,null,NaN

要注意空数组([])和空对象({}):

console.log([] == false) //trueconsole.log({} == false) //falseconsole.log(Boolean([])) //trueconsole.log(Boolean({})) //true

所以在 if 中,[] 和 {} 都表现为 true:

24 個 JavaScript 面試題

19、解释下面代码的输出

console.log(false == '0')console.log(false === '0')请参考前面第14题运算符转换规则的图。

20、解释下面代码的输出

var a={},
    b={key:&#39;b&#39;},
    c={key:&#39;c&#39;};a[b]=123;a[c]=456;console.log(a[b]);

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

The reason for this is as follows: When setting an object property, JavaScript will implicitly stringify the parameter value. In this case, since b and c are both objects, they will both be converted to "[object Object]". As a result, a[b] anda[c] are both equivalent to a["[object Object]"] and can be used interchangeably. Therefore, setting or referencing a[c] is precisely the same as setting or referencing a[b].

21、解释下面代码的输出

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

结果是10的阶乘。这是一个递归调用,为了简化,我初始化 n=5,则调用链和返回链如下:

24 個 JavaScript 面試題

22、解释下面代码的输出

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

输出1,闭包能够访问外部作用域的变量或参数。

23、解释下面代码的输出,并修复存在的问题

var hero = {    _name: &#39;John Doe&#39;,    getSecretIdentity: function (){        return this._name;    }};
var stoleSecretIdentity = hero.getSecretIdentity;console.log(stoleSecretIdentity());console.log(hero.getSecretIdentity());
将 getSecretIdentity 赋给 stoleSecretIdentity,等价于定义了 stoleSecretIdentity 函数:
var stoleSecretIdentity =  function (){return this._name;}

stoleSecretIdentity 的上下文是全局环境,所以第一个输出 undefined。若要输出 John Doe,则要通过 call 、apply 和 bind 等方式改变 stoleSecretIdentity 的this 指向(hero)。

第二个是调用对象的方法,输出 John Doe。

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

函数接受两个参数:

DOM
指定的回调函数

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

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
   }}

以上就是24个JavaScript的面试题,不管你是不是准备要出去找工作了,我相信这一套题目对大家都很有帮助。

相关推荐:

php初级面试题之简述题(一)

php初级面试题之简述题(二)

关于javascript常见面试题_javascript技巧

最让人容易出错的10道php面试题

php面试题中笔试题目的汇总

以上是24 個 JavaScript 面試題的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn