20道精選的面試題附答案,進(jìn)來看看能答對多少(二)

來源 | https://www.cnblogs.com/echoyya/p/14550258.html
1、寫出執(zhí)行結(jié)果,并解釋原因
var a = [0];if ([0]) {console.log(a == true);} else {console.log("wut");}// 輸出什么?
答案 : false解析 : if(condition)判斷時,會把condition轉(zhuǎn)換成boolean然后做判斷,[0]是一個有值的list,所以轉(zhuǎn)成boolean是true,而A == B的比較時,如果A和B的類型不一樣,會先把A和B轉(zhuǎn)化成相同的type,通常轉(zhuǎn)為number//分成以下步驟//把true轉(zhuǎn)化成number,true變成1[0] == 1;//list是object//先看[0].valueOf(),結(jié)果還是[0]//再看[0].toString(),結(jié)果是“0” type是string"0" == 1;//把“0” string轉(zhuǎn)化成number,“0”變成0,0不等于10 == 1; //結(jié)果是false
2、寫出執(zhí)行結(jié)果,并解釋原因
[] //解析成什么?答案及解析
答案 : [true,true]解析 : 運算符優(yōu)先級,分步解析1 < 2 < 3 => true < 3 => 1 < 3 => true3 < 2 < 1 => false < 1 => 0 < 1 => true
3、寫出執(zhí)行結(jié)果,并解釋原因
function foo() { }var oldName = foo.name;foo.name = "bar";[oldName, foo.name]
答案及解析
答案 : ['foo','foo']解析 : 函數(shù)的name是只讀屬性不可修改
4、寫出執(zhí)行結(jié)果,并解釋原因
var lowerCaseOnly = /^[a-z]+$/;[lowerCaseOnly.test(null), lowerCaseOnly.test()]
答案及解析
答案 : [true, true]解析 : 正則容易忽視的坑,test在檢測時會隱性將內(nèi)容轉(zhuǎn)為字符串,其實等同于:[lowerCaseOnly.test('null'), lowerCaseOnly.test('undefined')]
5、寫出執(zhí)行結(jié)果,并解釋原因
if ('http://giftwrapped.com/picture.jpg'.match('.gif')) {console.log('a gif file')} else {console.log('not a gif file')}
答案及解析
答案 : 'a gif file'解析 : 正則的隱式轉(zhuǎn)換,match方法第一個參數(shù)接收一個正則表達(dá)式或者一個字符串,但如果是字符串會隱式轉(zhuǎn)為正則,所以上述代碼等同于:'http://giftwrapped.com/picture.jpg'.match(/.gif/)而在正則中 點 . 表示通配符,所以成功匹配到/gif,匹配成功,輸出a gif file。
6、寫出執(zhí)行結(jié)果,并解釋原因
function user(obj) {obj.name = "北京"obj = new Object()obj.name = "上海"}let person = new Object();user(person);console.log(person.name);
答案及解析
答案 : 北京解析 : 對象作為參數(shù),傳遞進(jìn)去的是這個對象的地址,1. obj.name是給person這個對象賦值;2. obj = new Object(),把obj指向另一個對象,3. obj.name現(xiàn)在是給這個新對象賦值,不影響person這個變量指向的對象;4. 兩個obj指向的對象的引用地址不同。所有函數(shù)的參數(shù)都是按值傳遞的。5. 基本類型的傳遞同基本類型變量的賦值一樣,按值傳遞,在函數(shù)體內(nèi)修改參數(shù)的值,不會影響到函數(shù)外部。6. 引用類型的值傳遞同引用類型變量的賦值一樣,按引用傳遞,傳入函數(shù)的是原始值的地址,因此在函數(shù)內(nèi)部修改參數(shù),將會影響到原始值。
7、寫出執(zhí)行結(jié)果,并解釋原因
let x, y;try {throw new Error();}catch (x) {x = 1;y = 2;var a = 3console.log(a); // ?console.log(x); // ?}console.log(a); // ?console.log(x); // ?console.log(y); // ?
答案及解析
答案 : 3 1 3 undefined 2解析 :1. catch的作用域,其實并不是常見的塊級作用域,且不能綁定自己的內(nèi)部聲明的變量(如a)。2. catch創(chuàng)建的塊作用域,只對catch的參數(shù)x有效。3. 對于在內(nèi)部聲明的變量,catch并沒有創(chuàng)建一個新的作用域,只是一個普通的代碼塊。因此塊外仍可訪問
8、寫出執(zhí)行結(jié)果,并解釋原因
function fn() {getValue = function () { console.log(1); };return this;}fn.getValue = function () { console.log(2);};fn.prototype.getValue = function () {console.log(3);};var getValue = function () {console.log(4);};function getValue() {console.log(5);}getValue(); // ?fn().getValue(); // ?getValue(); // ?new fn.getValue(); // ?new fn().getValue(); // ?
答案及解析
答案 : 4 1 1 2 3考察 : 變量定義提升、this指向、運算符優(yōu)先級、原型、繼承、全局變量污染、對象屬性及原型屬性優(yōu)先級解析 : 為強調(diào)重點內(nèi)容,在下方使用標(biāo)記語言描述。
第一問 getValue():
直接調(diào)用,關(guān)注點在4,5上:
JS存在一種變量聲明被提升的機制,函數(shù)聲明會被提升到作用域的最前面,即使寫在最后,也還是會被提升至最前面。
函數(shù)表達(dá)式和函數(shù)聲明的區(qū)別,函數(shù)聲明解析時會提升,函數(shù)表達(dá)式的值是在JS運行時確定,并且在表達(dá)式賦值完成后,該函數(shù)才能調(diào)用
函數(shù)聲明5被函數(shù)表達(dá)式4覆蓋, 輸出4
執(zhí)行fn函數(shù),調(diào)用fn函數(shù)返回值對象的 getValue 屬性函數(shù);
此時 getValue 函數(shù)沒有用var進(jìn)行聲明,已將外層作用域的getValue函數(shù)修改;
fn函數(shù)返回this,此時函數(shù)執(zhí)行確定this指向window對象,相當(dāng)于執(zhí)行window.getValue(),而getValue已經(jīng)被修改成console.log(1), 輸出1
執(zhí)行完第6步,getValue函數(shù)已被修改,console.log(1), 輸出1
考察JS的運算符優(yōu)先級問題,
點的優(yōu)先級高于new無參數(shù)列表,相當(dāng)于new (fn.getValue())
當(dāng)點運算完后有個括號(),此時就是變成new有參數(shù)列表,優(yōu)先級高于函數(shù)執(zhí)行,所以直接執(zhí)行new。這也是為什么遇到()不先函數(shù)調(diào)用再new。
最終相當(dāng)于將 getValue函數(shù),作為構(gòu)造函數(shù)來執(zhí)行, 輸出2
這里帶括號是new 有參數(shù)列表,new有參數(shù)列表的優(yōu)先級與點的優(yōu)先級相同,按從左到右的順序執(zhí)行。
先執(zhí)行有參數(shù)列表,再執(zhí)行點的優(yōu)先級,最后再函數(shù)調(diào)用
fn作為構(gòu)造函數(shù)有返回值,在JS中構(gòu)造函數(shù)的返回值可有可無
沒有返回值:返回實例化的對象
有返回值:檢查其返回值是否為引用類型
非引用類型:基本類型則與無返回值相同,實際返回其實例化對象。
引用類型:實際返回值為這個引用類型
fn 函數(shù)返回的是this,this在構(gòu)造函數(shù)中本來就代表當(dāng)前實例化對象, 最終fn返回實例化對象。調(diào)用對象的getValue方法,而構(gòu)造函數(shù)中沒有g(shù)etValue,調(diào)用原型對象(prototype)上的getValue函數(shù)。輸出3。
9、寫出執(zhí)行結(jié)果,并解釋原因
let length = 10;function fn() {console.log(this.length);}var obj = {length: 5,method: function (fn) {fn();arguments[0]();}};obj.method(fn, 1);
答案及解析
答案 : 0 2解析 : 為強調(diào)重點內(nèi)容,在下方使用標(biāo)記語言描述。
第一問:fn()
任意函數(shù)里嵌套非箭頭函數(shù),嵌套函數(shù)內(nèi)this 在未指定的情況下,指向的是 window 對象,這里執(zhí)行 fn會打印window.length,
let聲明的變量有形成塊級作用域,且不存在聲明提升,length屬性并沒有掛載到window對象中。(test:let a = 1; window.a // undefined)
此時打印的便是window自帶的length屬性,表示iframe個數(shù),默認(rèn)為0。輸出0。
arguments類數(shù)組是函數(shù)參數(shù)的引用, arguments[0]指向 fn,
arguments[0]() 是作為 arguments對象的屬性[0]來調(diào)用 fn的,誰調(diào)用 this 就指向誰;所以 fn 中的 this 指向arguments(對象的屬性調(diào)用方法,this指向該對象)
arguments有兩個參數(shù),fn和1,因此argumengts.length = 2 ,輸出2。
[function fn(){console.log(this.length)}][0](); // 1 數(shù)組也是對象,調(diào)用數(shù)組對象的0屬性,函數(shù)作為數(shù)組對象的屬性調(diào)用,函數(shù)中的this 當(dāng)然指向這個數(shù)組,所以返回數(shù)組的length
10、寫出執(zhí)行結(jié)果,并解釋原因
var a=10;var foo={a:20,bar:function(){var a=30;return this.a;}}console.log(foo.bar()); // ?console.log((foo.bar)()); // ?console.log((foo.bar=foo.bar)()); // ?console.log((foo.bar,foo.bar)()); // ?
答案及解析
答案 : 20 20 10 10解析 : 為強調(diào)重點內(nèi)容,在下方使用標(biāo)記語言描述。
第一問 foo.bar()
foo調(diào)用,this指向foo , 輸出20。
表達(dá)式加了括號,括號的作用是改變表達(dá)式的運算順序,而在這加與不加并無影響,相當(dāng)于foo.bar(), 輸出20。
等號運算相當(dāng)于重新給foo.bar定義,相當(dāng)于一個匿名函數(shù)賦值給一個全局變量,foo.bar是在window作用域下,this指代的是window,輸出10。
foo.bar = function () {var a = 10;return this.a;}
第四問 (foo.bar,foo.bar)()
逗號運算符求解過程是:先計算表達(dá)式1的值,再計算表達(dá)式2的值,……一直計算到表達(dá)式n的值,最后整個逗號運算符的返回值是最后一個表達(dá)式的值。
經(jīng)過逗號運算符后,就是純函數(shù),不再是對象方法的引用,所以this指向window,輸出10。
技巧:經(jīng)過賦值,運算符運算后,都是純函數(shù),不是對象方法的引用。函數(shù)中this指向都是windows。
11、寫出執(zhí)行結(jié)果,并解釋原因
function getName(){return{name:'Echoyya'}}console.log(getName()); // ?
答案及解析
答案 : undefined解析 : 如果continue、break、return、throw 這四個語句后面,直接跟換行符,則會自動添加分號
12、寫出執(zhí)行結(jié)果,并解釋原因
const num = parseInt("2*4",10);console.log(num); // ?
答案及解析
答案 : 2解析 : parseInt會檢查字符串中的字符是否合法. 一旦遇到一個在指定進(jìn)制(第二個參數(shù))中不合法的字符后,立即停止解析并且忽略后面所有的字符。*為非法數(shù)字。所以只解析到 2,并將其解析為十進(jìn)制的2. 值即為 2
13、寫出執(zhí)行結(jié)果,并解釋原因
var x = 20;var temp = {x: 40,foo: function () {var x = 10;console.log(this.x);}};(temp.foo, temp.foo)(); // ?
答案及解析
答案 : 20技巧 : 經(jīng)過賦值,運算符運算后,都是純函數(shù),不是對象方法的引用。函數(shù)中this指向都是windows。解析 : 逗號操作符會從左到右計算它的操作數(shù),返回最后一個操作數(shù)的值。所以(temp.foo, temp.foo)();等價于var fun = temp.foo;fun();fun調(diào)用時this指向window,因此返回 20。
14、寫出執(zhí)行結(jié)果,并解釋原因
const company = { name: "Echoyya" };Object.defineProperty(company, "address", { value: "北京" });console.log(company); // ?console.log(Object.keys(company)); // ?
答案及解析
答案 : {name:"Echoyya",address:"北京"}, ["name"]解析 : defineProperty方法可以給對象添加一個新屬性,或者修改已經(jīng)存在的屬性。而使用defineProperty給對象添加屬性之后,屬性默認(rèn)為不可枚舉,Object.keys方法僅返回對象中可枚舉的屬性,因此只打印name
15、寫出執(zhí)行結(jié)果,并解釋原因
let num = 10;const inNum = () => num++;const inPaNum = number => number++;const num1 = inNum();const num2 = inPaNum(num1);console.log(num1); // ?console.log(num2); // ?
答案及解析
答案 : 10 10解析 : 一元操作符 ++ 先返回操作值, 再執(zhí)行自增操作值。1. num1 是10,因為 inNum 函數(shù)先返回 num 的值,在執(zhí)行 num 自增2. num2 是10,因為 num1 作為參數(shù)傳入 inPaNum,同理函數(shù)先返回 number 的值,在執(zhí)行 number 自增
16、寫出執(zhí)行結(jié)果,并解釋原因
const value = { number: 10 };const multiply = (x = { ...value }) => {console.log(x.number *= 2);};multiply(); // ?multiply(); // ?multiply(value); // ?multiply(value); // ?
答案及解析
答案 : 20 20 20 40解析 :1. ES6中可以使用參數(shù)默認(rèn)值, 函數(shù)未傳參,或參數(shù)為undefined,將使用參數(shù)默認(rèn)值。2. 解構(gòu) value 對象并賦值給一個新對象,因此 x 的默認(rèn)值為 {number:10} 。3. 默認(rèn)參數(shù)在調(diào)用時才會計算,每次調(diào)用函數(shù),都會創(chuàng)建一個新的對象。調(diào)用 multiply(),x的默認(rèn)值都為 {number:10},因此輸出 204. 調(diào)用 multiply(value),實際上修改了 value.number的值,輸出 205. 再次調(diào)用調(diào)用 multiply(value),value.number之前被修改為 20,因此輸出 40。
17、寫出執(zhí)行結(jié)果,并解釋原因
// index.jsconsole.log('running index.js');import { sum } from './sum.js';console.log(sum(1, 2));// sum.jsconsole.log('running sum.js');export const sum = (a, b) => a + b;
答案及解析
答案 : running sum.js, running index.js, 3解析 : import命令是編譯階段執(zhí)行的,在運行之前。因此被導(dǎo)入的模塊會先運行,而導(dǎo)入模塊的文件會后執(zhí)行。這是CommonJS 中 require() 和 import之間的區(qū)別。require()可以在運行代碼時按需加載。如果使用 require,那么running index.js、running sum.js、 3會被依次打印。
18、寫出執(zhí)行結(jié)果,并解釋原因
function addToList(item, list) {return list.push(item);}const result = addToList("company", ["yideng"]);console.log(result); // ?
答案及解析
答案 : 2解析 : push()方法返回新數(shù)組的長度。若想返回新數(shù)組,應(yīng)該在push之后返回list。
19、實現(xiàn)(5).add(3).minus(2) 功能
// 實現(xiàn) (5).add(3).minus(2) 功能console.log((5).add(3).minus(2)); // 6
答案及解析
答案 :Number.prototype.add = function (number) {if (typeof number !== 'number') {throw new Error('請輸入數(shù)字~');}return this + number;};Number.prototype.minus = function (number) {if (typeof number !== 'number') {throw new Error('請輸入數(shù)字~');}return this - number;};console.log((5).add(3).minus(2)); // 6
20、不使用模運算符的情況下,檢查一個數(shù)是否是偶數(shù)
isEven(num) // true Or false答案及解析
答案 :1)遞歸方式function isEven(num){const number = Math.abs(num); // 取絕對值if(number === 1) return false;if(number == 0 ) return true;return isEven(number -2);}-------------------------------------------------2)通過Math.round,利用奇數(shù)除以2會有小數(shù)的特點function isEven(num){return parseInt(num/2) === Math.round(num/2);}
本文完~
推薦閱讀
20道精選的面試題附答案,進(jìn)來看看能答對多少(一)
學(xué)習(xí)更多技能
請點擊下方公眾號
![]()
