收藏 javascript-questions 這個倉庫很久了,趁著周末來鍛煉下自己的 JS 基礎水平 因為逐漸也在承擔一些面試工作,順便摘錄一些個人覺得比較適合面試的題目和方向 事件流(捕獲、冒泡) 源鏈接 以下代碼點擊結果是啥? <div onclick="console.log('div')" ...
收藏 javascript-questions 這個倉庫很久了,趁著周末來鍛煉下自己的 JS 基礎水平
因為逐漸也在承擔一些面試工作,順便摘錄一些個人覺得比較適合面試的題目和方向
事件流(捕獲、冒泡)
以下代碼點擊結果是啥?
<div onclick="console.log('div')">
<p onclick="console.log('p')">
Click here!
</p>
</div>
答案
-> 依次列印 p
和 div
這道題考查事件流,在事件傳播中,有三個階段,捕獲->目標->冒泡,按照標簽來看,就是 div->p->div,但是預設只在冒泡階段處理事件
如果需要在捕獲階段處理事件,可用 addEventLister
並且傳入第三個參數為 true
(預設是 false
,即預設冒泡階段處理)
<div id="div">
<p id="p">
Click here!
</p>
</div>
<script>
document.getElementById('div').addEventListener('click', () => console.log('div'), true)
document.getElementById('p').addEventListener('click', () => console.log('p'), true)
</script>
call、apply、bind
以下代碼輸出結果是啥?
const person = { name: 'Lydia' };
function sayHi(age) {
return `${this.name} is ${age}`;
}
console.log(sayHi.call(person, 21));
console.log(sayHi.bind(person, 21));
答案
-> Lydia is 21
[Function: bound sayHi]
這道題主要考查 call 和 bind,當然我們面試的時候一般會把 apply 拿出來一起考察下
call、apply 以及 bind 都是為了改變 this 指向而生,而 call 和 apply 用法比較像,都會在函數調用時改變 this 指向,唯一的區別是 apply 的第二個參數是數組,而 call 從第二個參數開始都是實際傳入該函數中的值。bind 與他們不一樣,它返回的還是函數
new
以下代碼輸出結果是啥?
function Car() {
this.make = 'Lamborghini';
return { make: 'Maserati' };
}
const myCar = new Car();
console.log(myCar.make);
答案
-> Maserati
這題考查 new。如果對構造函數以及 new 比較瞭解的就會知道,構造函數里,如果返回一個非 null 的對象(包括數組),則將該對象值賦值給新建的對象
瞭解更多可以參考 一道有意思的筆試題引發的對於 new 操作符的思考
JSON.stringify
以下代碼輸出結果是啥?
const settings = {
username: 'lydiahallie',
level: 19,
health: 90,
};
const data = JSON.stringify(settings, ['level', 'health']);
console.log(data);
答案
-> {"level":19,"health":90}
這道題主要考查 JSON.stringify
的第二個參數,當然它還能傳入第三個參數
語法:JSON.stringify(value[, replacer [, space]])
。詳見 JSON.stringify
replacer
replacer 參數可以是一個函數或者一個數組。作為函數,它有兩個參數,鍵(key)和值(value),它們都會被序列化
在開始時,replacer 函數會被傳入一個空字元串作為 key 值,代表著要被 stringify 的這個對象。隨後每個對象或數組上的屬性會被依次傳入
const settings = {
username: 'lydiahallie',
level: 19,
health: 90,
};
const data = JSON.stringify(settings, (k, v) => {
console.log(k, v);
return v
});
console.log(data);
/*
// 這裡其實最開始列印了個空字元串。在開始時,replacer 函數會被傳入一個空字元串作為 key 值,代表著要被 stringify 的這個對象
{ username: 'lydiahallie', level: 19, health: 90 }
username lydiahallie
level 19
health 90
{"username":"lydiahallie","level":19,"health":90}
*/
const settings = {
username: 'lydiahallie',
level: 19,
health: 90,
};
const data = JSON.stringify(settings, (k, v) => {
if (k === 'level') return v * 2
return v
});
console.log(data);
// {"username":"lydiahallie","level":38,"health":90}
當傳入數組的時候,相對比較簡單,就是需要 picked 的 key(但是沒法深度 pick,只能 pick 第一層)
space
用來格式化,可以傳入數字或者字元串
const settings = {
username: 'lydiahallie',
level: 19,
health: 90,
};
const data = JSON.stringify(settings, null, 2); // 這時候和傳入 ' '(length 2)效果一樣
console.log(data);
/*
{
"username": "lydiahallie",
"level": 19,
"health": 90
}
*/
const settings = {
username: 'lydiahallie',
level: 19,
health: 90,
};
const data = JSON.stringify(settings, null, 'hello');
console.log(data);
/*
{
hello"username": "lydiahallie",
hello"level": 19,
hello"health": 90
}
*/
函數參數解構
以下代碼輸出結果是啥?
const myFunc = ({ x, y, z }) => {
console.log(x, y, z);
};
myFunc(1, 2, 3);
答案
-> undefined
undefined
undefined
這道題比較簡單,myFunc 函數需要的是一個對象參數,並且有 key x
y
和 z
,但是傳入的並不是對象,所以都取了他們的預設值 undefined
如果傳入對象,但是並沒有對應的 key,也同樣是 undefined
const myFunc = ({ x, y, z }) => {
console.log(x, y, z);
};
myFunc({ x: 1 }); // 1 undefined undefined
函數中的剩餘參數
以下代碼輸出結果是啥?
function getItems(fruitList, ...args, favoriteFruit) {
return [...fruitList, ...args, favoriteFruit]
}
getItems(["banana", "apple"], "pear", "orange")
答案
-> SyntaxError
這道題就比較腦經急轉彎了,剩餘參數只能放在最後,其實我覺得像上面代碼那樣其實也有使用場景,不知道未來會不會支持這樣的寫法
展開運算符
以下代碼輸出結果是啥?
const person = {
name: 'Lydia',
age: 21,
};
const changeAge = (x = { ...person }) => (x.age += 1);
const changeAgeAndName = (x = { ...person }) => {
x.age += 1;
x.name = 'Sarah';
};
changeAge(person);
changeAgeAndName();
console.log(person);
答案
-> { name: 'Lydia', age: 22 }
這道題的考點是展開運算符在對象中的使用。一般我們會用展開運算符來複制一個對象
當調用 changeAge(person)
時,參數傳入了 person,所以 (x.age += 1)
實際操作的和 person 是一個對象引用。而調用 changeAgeAndName()
時,因為沒有傳入參數,所以 x 其實是 { ...person }
,而這個其實是 person 的一個淺拷貝
可以考查下將 (x = { ...person })
改成 (x = person)
||
以下代碼輸出結果是啥?
const one = false || {} || null;
const two = null || false || '';
const three = [] || 0 || true;
console.log(one, two, three);
答案
-> {}
""
true
這道題的考點是 ||
和假值。 ||
運算符會返回第一個真值,如果都為假值,則返回最後一個值
這裡可以順便考查 &&
。&&
操作符,一旦遇到了假值,便會停止往後
console.log(true && 0); // 0
console.log(true && 0 && true); // 0
console.log(1 && 2); // 2
console.log(1 && false); // false
console.log('' && false); // ''
falsy
以下哪些值是假值?
0;
new Number(0);
('');
(' ');
new Boolean(false);
undefined;
答案
-> 0
''
undefined
JS 中的假值有以下幾種:
false
''
NaN
undefined
null
0
/-0
0n
(BigInt(0))
除此之外,new Number
、new Boolean
創建的其實都是對象,都是真值
對象中 key 重覆
以下代碼輸出結果是啥?
const obj = { a: 'one', b: 'two', a: 'three' };
console.log(obj);
答案
-> { a: 'three', b: 'two' }
這其實更像是一個規範,當對象中 key 重覆的時候,value 會被後面的替換,但是 key 的位置會是第一次出現的位置
一般是對象合併的時候會用到:
const oldObj = {
name: 'fish',
age: 30
}
const coverdObj = { name: 'lessfish' }
const newObj = {
...oldObj,
...coverdObj
}
console.log(newObj); // { name: 'lessfish', age: 30 }
Array Operators
有好幾道和數組操作有關的題
這題 主要考查 push 操作返回 push 後的數組長度,這題 也是類似
這題 主要考查哪些數組操作會改變原來的數組(splice)
這題 主要考查 reduce 的使用
[1, 2, 3, 4].reduce((x, y) => console.log(x, y));
這題還是比較有意思的,reduce 函數調用中如果有第二個參數,則會被當作第一次迭代中的 previous 值(也就是第一個參數 x
),如果沒有第二個參數,則數組第一個元素會被當作第一次迭代的 previous
所以上面的代碼,如果 reduce 有第二個參數,會被迭代 4 次,如果沒有,則是迭代三次
再看代碼,因為沒有第二個參數,所以第一次迭代參數是 1
和 2
,reduce 迭代中的返回會被當作下次迭代的 previous,但是這裡沒返回,所以就是 undefined
,而第二個參數 y
就是數組元素
Object Operators
這題 主要考查用 Object.defineProperty
定義的對象中的屬性預設不可枚舉,不能用 Object.keys
拿到
這題 主要考查 Object.freeze
,顧名思義它能冰凍住對象,使得對象不能增、刪、修改鍵值對(但是註意,它只是 freeze 了第一層,可以參考 這題)
這題 考查 Object.seal
,它能阻止對象新增、刪除屬性,但是對於已有的屬性依然可以修改其值(註意和 freeze 一樣同樣只是第一層)
delete
以下代碼輸出結果是啥?
const name = 'Lydia';
age = 21;
console.log(delete name);
console.log(delete age);
答案
-> false
true
這題考查 delete
操作
首先 delete
返回一個布爾值,代表是否刪除成功。然後 var
const
let
定義的變數,都不能被刪除,只有全局變數才能被刪除
這裡要註意下 name
,因為瀏覽器中預設掛了個全局變數 name
,所以 delete name
是 ok 的
暫時性死區
這兩題都是暫時性死區相關,註意下 var
和 let
const
是有差異的,var
的話會變數聲明提升(但是是 undefined),但是 let
和 const
並不會初始化
Object toString
以下代碼輸出結果是啥?
const animals = {};
let dog = { emoji: '