JS經典面試考題

代碼輸出問題

一、以下代碼打印結果?

const keyA = { a: 1 };
const keyB = { b: 2 };

const myObject = {
  [keyA]: 'aaa',
  [keyB]: 'bbb'
}

console.log(myObject);

答案:{ [object Object]: “bbb” }

解析:

上方代碼中的 [keyA][keyB] 得到的都是 [object object] , 所以 [keyB] 會把 [keyA] 覆蓋掉,myObject 最會只剩下 [object object] 屬性.

考查知識點: ES6中的屬性名表達式的使用 ----- 屬性名表達式如果是一個對象,默認情況下會自動將對象轉爲字符串:[object object].

二、以下代碼的打印結果?

 function Foo() {
   Foo.a = function () {
     console.log(1)
   }
   this.a = function () {
     console.log(2)
   }
 }
 Foo.prototype.a = function () {
   console.log(3)
 }
 Foo.a = function () {
   console.log(4)
 }
 Foo.a();
 let obj = new Foo();
 obj.a();
 Foo.a();

答案:4 2 1

解析:

 function Foo() {
   Foo.a = function () {
     console.log(1)
   }
   this.a = function () {
     console.log(2)
   }
 }
 // 以上只是Foo的構建方法,沒有產生實例,此刻也沒有執行

 Foo.prototype.a = function () {
   console.log(3)
 }
 // 此刻原型Foo上掛載了原型方法a, 方法輸出值爲3

 Foo.a = function () {
   console.log(4)
 }
 // 此刻Foo上掛載了直接方法a, 輸出值爲4

 Foo.a(); 
 // 由於Foo構建方法還沒有實例化,所以還沒有執行,這裏執行Foo上掛載的直接方法,輸出4

 let obj = new Foo();
 /* 
   這裏調用了Foo的構建方法,Foo的構建方法主要做了兩件事:
   1. 將全局Foo上的直接方法 a 替換爲一個輸出 1 的方法。
   2. 在新對象上掛載直接方法 a, 輸出值爲2.
 */ 

 obj.a();
 // 新對象上存在直接方法 a, 所以不需要去訪問原型鏈,所以使用的是構建方法裏定義的this.a
 
 Foo.a();
 // 這時,構建方法裏已經替換了全局Foo上的 a 方法,所以輸出 1

考查知識點構建函數不產生實例,不會執行.

三、以下代碼輸出結果?

String('11') == new String('11'); // (1)控制檯打印結果??
String('11') === new String('11'); // (2)控制檯打印結果??

答案:true, false

解析:

  • (1). ‘==’ 時做了隱式轉換,調用了 toString. 實際運行的是 String(‘11’) == new String(‘11’).toString();
  • (2). ‘===’ 兩邊類型不一樣,一個是string,一個是object.

考查知識點JS數據類型轉換.

四、以下代碼執行結果?

function test () {
  console.log('加油,你是最棒的!');
}
// 輸出變量
var test = 10;
// 調用函數
test();

答案:Uncaught TypeError: test is not a function

解析:

由於varfunction關鍵詞聲明的變量,會被JS解析器優先解析執行,被稱爲變量聲明提升機制
所以,以上代碼的執行步驟爲:
1. 先執行function test;(函數聲明)
2. 再執行 var test;(變量聲明)
4. 按照順序,接下來執行 test = 10;(賦值操作,此時test爲number類型)
5. 最後,當調用 test()函數時,卻發現test已經被複製爲10(number類型)
6. 所以會報錯:Uncaught TypeError: test is not a function

五、以下代碼打印結果?

var name = '前端';
(function () {
  if(typeof name == 'undefined') {
    var name = '後臺'
    console.log('Goodbye ' + name);
  }else{
    console.log('Hello ' + name);
  }
})()

答案:Goodbye 後臺

解析:

函數表達式不存在函數提升機制,自執行函數也屬於函數表達式. 上方代碼的執行步驟如下:

  • 1.先執行了 var name; (name變量聲明操作)
  • 2.又執行了function,由於此函數是IIFE, 所以會在定義時,立即執行。
  • 3.此時全局的name值爲前端,但IIFE函數內又存在name變量的聲明和賦值操作,由於變量聲明提升機制,內部的name會被提升至if語句外部執行,所以在進入if語句時,name的值仍爲undefined.
  • 4.相當於以下代碼的執行:
var name = '前端';
(function() {
	var name;
	if (typeof name == 'undefined') {
	  name = '後臺';
	  console.log('Goodbye ' + name);
	} else {
	  console.log('Hello ' + name);
	}
})();

擴展題目:把上方代碼做小小的改動

(function () {
  if (typeof name == 'undefined') {
    name = '後端';
    console.log('Goodbye ' + name);
  } else {
    console.log('Hello ' + name);
  }
})();
var name = '前端';

答案:初次打印Hello ------- 當刷新瀏覽器再次執行此代碼,打印結果爲:Hello 前端

解析:

  • 1.首先變量聲明提升,執行var name;(只聲明,未賦值)
  • 2.然後執行自執行函數function, 按說這裏獲取到的name值應該爲undefined,但由於window對象下有個默認的name屬性,默認值爲""(空字符串)。所以自執行函數內獲取到的name值爲空字符串。typeof “” 的值爲String, 不等於"undefined", 所以走else, 打印結果爲 Hello + “”,拼接空字符串結果仍爲Hello.
  • 3.爲何刷新瀏覽器時,打印結果變成了Hello 前端呢?是因爲name在第一次代碼執行結束時,被賦值爲前端,當再次刷新瀏覽器時,name的值已經被緩存了,從而得到了Hello 前端.

考查知識點變量提升機制、類型判斷、自執行函數.

JS底層和算法問題

六、實現一個Object.create函數

作用:Object.create()方法創建一個新對象,使用現有的對象來提供新創建的對象的__proto__.

Object.create = function (o) {
  var F = function () {}; // (1)
  F.prototype = o; // (2)
  return new F(); // (3)
}

解析:

  • (1).創建一個構造函數.
  • (2).把構造函數的原型指向o.
  • (3).例化對象並返回.

七、a, b, c, d, e共5個字母的全排列,分別打印出排列組合方式。

方法一: 循環+遞歸解決

let str = ['a', 'b', 'c', 'd', 'e'];
let count = 0;
function group(s) {
	for (var i = 0,length = str.length;i < length;i++) {
		if(s.length == length - 1) {
			if(s.indexOf(str[i]) < 0) {
				count ++;
				console.log(s + str[i]);
			}
			continue;
		}

		if(s.indexOf(str[i]) < 0) {
			group(s + str[i]);
		}
	}
}
group(''); 

方法二:兩次循環解決

let arr = [];
for(var i = 12345;i <= 54321;i++){
	let j = i.toString();
	if (j.indexOf('1') > -1 && j.indexOf('2') > -1 && j.indexOf('3') > -1 && j.indexOf('4') > -1 && j.indexOf('5') > -1) {
		arr.push(j)
	}
}

let res = arr.map(item => {
	let iArr = item.split('');
	iArr[iArr.indexOf('1')] = 'a'
	iArr[iArr.indexOf('2')] = 'b'
	iArr[iArr.indexOf('3')] = 'c'
	iArr[iArr.indexOf('4')] = 'd'
	iArr[iArr.indexOf('5')] = 'e'
	item = iArr.join()
	return item;
})
console.log(res);

八、求多個數組之間的交集

解決代碼:

const arr1 = [1, 2, 4, 3];
const arr2 = [3, 4, 5];
const arr3 = [3, 6, 4, 7];
const arr4 = [4, 5, 3, 8, 7];
const handle = (...arr) => {
  // console.log(...arr)
  return arr.reduce((rst, ele, i) => {
    // console.log(rst, ele, i)
    return rst.filter(item => ele.includes(item));
  });
}

const res = handle(arr1, arr2, arr3);
console.log(res)

答案:[4, 3]

注: reduce的具體用法,參考網址爲:Array.prototype.reduce解析MDN

九、實現單間的數組去重

使用ES6中的set方法:

var arr = [1,2,3,4,5,4,5,5];
[...new Set(arr)];

// 或者
var arr = [1,2,3,4,5,4,5,5];
Array.from(new Set(arr));

結果:[1,2,3,4,5]

注: Set爲ES6提供的新的數據結構,它的特性是成員值都是唯一的,不可重複.

十、實現js函數重載

函數重載的定義:函數名相同,不同輸入對應着不同的輸出. 示例代碼如下:

// addMethod
function addMethod(object, name, fn) {
  var old = object[name]
  object[name] = function() {
    if(fn.length === arguments.length) {
      return fn.apply(this, arguments)
    } else if(typeof old === 'function') {
      return old.apply(this, arguments)
    }
  }
}

var people = {
  values: ['研發部 前端', '研發部 後臺', '研發部 測試']
}

// 下面開始通過addMethod來實現對people.find方法的重載
// 不傳參數時,返回people.values裏面所有的元素
addMethod(people, 'find', function() {
  return this.values
})


// 傳一個參數時,按first-name的匹配進行返回
addMethod(people, 'find', function(firstName) {
  var ret = []
  for(var i = 0;i < this.values.length; i++) {
    if(this.values[i].indexOf(firstName) === 0) {
      ret.push(this.values[i])
    }
  }
  return ret
})


// 傳兩個參數時,返回first-name和last-name都匹配的元素
addMethod(people, 'find', function(firstName, lastName) {
  var ret = []
  for(var i = 0; i < this.values.length; i++) {
    if(this.values[i] === (firstName + '' + lastName)) {
      ret.push(this.values[i])
    }
  }
  return ret
})

var res = people.find('研發部')
console.log(res)

不同輸入狀態下的輸出結果:

// 不傳參時
var res = people.find();
console.log(res); // ['研發部 前端', '研發部 後臺', '研發部 測試']

// 傳一個參數時
var res = people.find('研發部 前端');
console.log(res); // ['研發部 前端']

// 傳兩個參數時
var res = people.find('研發部 前端', '研發部 測試');
console.log(res); // ['研發部 前端', '研發部 測試']

算法問題答案不唯一,若有更好實現方法,歡迎留言交流!共勉!💪

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章