代碼輸出問題
一、以下代碼打印結果?
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
解析:
由於var 、function關鍵詞聲明的變量,會被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); // ['研發部 前端', '研發部 測試']
算法問題答案不唯一,若有更好實現方法,歡迎留言交流!共勉!💪