
以下題目,本人驗證無誤,查閱了相關資料,得出解析部分并做了相關總結,希望對正準備跳槽或找工作的你有幫助!
1、寫出執行結果,并解釋原因
var a = [0];
if ([0]) {
console.log(a == true);
} else {
console.log("wut");
}
// 輸出什麼?
答案及解析
答案 : false
解析 : if(condition)判斷時,會把condition轉換成boolean然後做判斷,[0]是一個有值的list,是以轉成boolean是true,
而A == B的比較時,如果A和B的類型不一樣,會先把A和B轉化成相同的type,通常轉為number
//分成以下步驟
//把true轉化成number,true變成1
[0] == 1;
//list是object
//先看[0].valueOf(),結果還是[0]
//再看[0].toString(),結果是“0” type是string
"0" == 1;
//把“0” string轉化成number,“0”變成0,0不等于1
0 == 1; //結果是false
2、寫出執行結果,并解釋原因
[1 < 2 < 3, 3 < 2 < 1] //解析成什麼?
答案及解析
答案 : [true,true]
解析 : 運算符優先級,分步解析
1 < 2 < 3 => true < 3 => 1 < 3 => true
3 < 2 < 1 => false < 1 => 0 < 1 => true
3、寫出執行結果,并解釋原因
function foo() { }
var oldName = foo.name;
foo.name = "bar";
[oldName, foo.name]
答案及解析
答案 : ['foo','foo']
解析 : 函數的name是隻讀屬性不可修改
4、寫出執行結果,并解釋原因
var lowerCaseOnly = /^[a-z]+$/;
[lowerCaseOnly.test(null), lowerCaseOnly.test()]
答案及解析
答案 : [true, true]
解析 : 正則容易忽視的坑,test在檢測時會隐性将内容轉為字元串,其實等同于:[lowerCaseOnly.test('null'), lowerCaseOnly.test('undefined')]
5、寫出執行結果,并解釋原因
if ('http://giftwrapped.com/picture.jpg'.match('.gif')) {
console.log('a gif file')
} else {
console.log('not a gif file')
}
答案及解析
答案 : 'a gif file'
解析 : 正則的隐式轉換,match方法第一個參數接收一個正規表達式或者一個字元串,但如果是字元串會隐式轉為正則,
是以上述代碼等同于:'http://giftwrapped.com/picture.jpg'.match(/.gif/)
而在正則中 點 . 表示通配符,是以成功比對到/gif,比對成功,輸出a gif file。
6、寫出執行結果,并解釋原因
function user(obj) {
obj.name = "北京"
obj = new Object()
obj.name = "上海"
}
let person = new Object();
user(person);
console.log(person.name);
答案及解析
答案 : 北京
解析 : 對象作為參數,傳遞進去的是這個對象的位址,
1. obj.name是給person這個對象指派;
2. obj = new Object(),把obj指向另一個對象,
3. obj.name現在是給這個新對象指派,不影響person這個變量指向的對象;
4. 兩個obj指向的對象的引用位址不同。
所有函數的參數都是按值傳遞的。
5. 基本類型的傳遞同基本類型變量的指派一樣,按值傳遞,在函數體内修改參數的值,不會影響到函數外部。
6. 引用類型的值傳遞同引用類型變量的指派一樣,按引用傳遞,傳入函數的是原始值的位址,是以在函數内部修改參數,将會影響到原始值。
7、寫出執行結果,并解釋原因
let x, y;
try {
throw new Error();
}
catch (x) {
x = 1;
y = 2;
var a = 3
console.log(a); // ?
console.log(x); // ?
}
console.log(a); // ?
console.log(x); // ?
console.log(y); // ?
答案及解析
答案 : 3 1 3 undefined 2
解析 :
1. catch的作用域,其實并不是常見的塊級作用域,且不能綁定自己的内部聲明的變量(如a)。
2. catch建立的塊作用域,隻對catch的參數x有效。
3. 對于在内部聲明的變量,catch并沒有建立一個新的作用域,隻是一個普通的代碼塊。是以塊外仍可通路
8、寫出執行結果,并解釋原因
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指向、運算符優先級、原型、繼承、全局變量污染、對象屬性及原型屬性優先級
解析 : 為強調重點内容,在下方使用标記語言描述。
解析:
第一問 getValue():
- 直接調用,關注點在4,5上:
- JS存在一種變量聲明被提升的機制,函數聲明會被提升到作用域的最前面,即使寫在最後,也還是會被提升至最前面。
- 函數表達式和函數聲明的差別,函數聲明解析時會提升,函數表達式的值是在JS運作時确定,并且在表達式指派完成後,該函數才能調用
- 函數聲明5被函數表達式4覆寫, 輸出4
第二問 fn().getValue():
- 執行fn函數,調用fn函數傳回值對象的 getValue 屬性函數;
- 此時 getValue 函數沒有用var進行聲明,已将外層作用域的getValue函數修改;
- fn函數傳回this,此時函數執行确定this指向window對象,相當于執行window.getValue(),而getValue已經被修改成console.log(1), 輸出1
第三問 getValue():
- 執行完第6步,getValue函數已被修改,console.log(1), 輸出1
第四問 new fn.getValue():
- 考察JS的運算符優先級問題,
- 點的優先級高于new無參數清單,相當于new (fn.getValue())
- 當點運算完後有個括号(),此時就是變成new有參數清單,優先級高于函數執行,是以直接執行new。這也是為什麼遇到()不先函數調用再new。
- 最終相當于将 getValue函數,作為構造函數來執行, 輸出2
第五問 new fn().getValue()
- 這裡帶括号是new 有參數清單,new有參數清單的優先級與點的優先級相同,按從左到右的順序執行。
- 先執行有參數清單,再執行點的優先級,最後再函數調用
- fn作為構造函數有傳回值,在JS中構造函數的傳回值可有可無
- 沒有傳回值:傳回執行個體化的對象
- 有傳回值:檢查其傳回值是否為引用類型
- 非引用類型:基本類型則與無傳回值相同,實際傳回其執行個體化對象。
- 引用類型:實際傳回值為這個引用類型
- fn 函數傳回的是this,this在構造函數中本來就代表目前執行個體化對象, 最終fn傳回執行個體化對象。調用對象的getValue方法,而構造函數中沒有getValue,調用原型對象(prototype)上的getValue函數。輸出3。
9、寫出執行結果,并解釋原因
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
解析 : 為強調重點内容,在下方使用标記語言描述。
解析
第一問:fn()
- 任意函數裡嵌套非箭頭函數,嵌套函數内this 在未指定的情況下,指向的是 window 對象,這裡執行 fn會列印window.length,
- let聲明的變量有形成塊級作用域,且不存在聲明提升,length屬性并沒有挂載到window對象中。(test:let a = 1; window.a // undefined)
- 此時列印的便是window自帶的length屬性,表示iframe個數,預設為0。輸出0。
第二問:arguments[0]()
- arguments類數組是函數參數的引用, arguments[0]指向 fn,
- arguments[0]() 是作為 arguments對象的屬性[0]來調用 fn的,誰調用 this 就指向誰;是以 fn 中的 this 指向arguments(對象的屬性調用方法,this指向該對象)
- arguments有兩個參數,fn和1,是以argumengts.length = 2 ,輸出2。
擴充
- [function fn(){console.log(this.length)}][0](); // 1
- 數組也是對象,調用數組對象的0屬性,函數作為數組對象的屬性調用,函數中的this 當然指向這個數組,是以傳回數組的length
10、寫出執行結果,并解釋原因
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
解析 : 為強調重點内容,在下方使用标記語言描述。
本題主要考察this指向問題,推薦閱讀《一文搞懂 this、apply、call、bind》
解析
第一問 foo.bar()
- foo調用,this指向foo , 輸出20。
第二問 (foo.bar)()
- 表達式加了括号,括号的作用是改變表達式的運算順序,而在這加與不加并無影響,相當于foo.bar(), 輸出20。
第三問 (foo.bar=foo.bar)()
- 等号運算相當于重新給foo.bar定義,相當于一個匿名函數指派給一個全局變量,foo.bar是在window作用域下,this指代的是window,輸出10。
foo.bar = function () {
var a = 10;
return this.a;
}
第四問 (foo.bar,foo.bar)()
- 逗号運算符求解過程是:先計算表達式1的值,再計算表達式2的值,……一直計算到表達式n的值,最後整個逗号運算符的傳回值是最後一個表達式的值。
- 經過逗号運算符後,就是純函數,不再是對象方法的引用,是以this指向window,輸出10。
- 技巧:經過指派,運算符運算後,都是純函數,不是對象方法的引用。函數中this指向都是windows。
11、寫出執行結果,并解釋原因
function getName(){
return
{
name:'Echoyya'
}
}
console.log(getName()); // ?
答案及解析
答案 : undefined
解析 : 如果continue、break、return、throw 這四個語句後面,直接跟換行符,則會自動添加分号
12、寫出執行結果,并解釋原因
const num = parseInt("2*4",10);
console.log(num); // ?
答案及解析
答案 : 2
解析 : parseInt會檢查字元串中的字元是否合法. 一旦遇到一個在指定進制(第二個參數)中不合法的字元後,立即停止解析并且忽略後面所有的字元。
*為非法數字。是以隻解析到 2,并将其解析為十進制的2. 值即為 2
13、寫出執行結果,并解釋原因
var x = 20;
var temp = {
x: 40,
foo: function () {
var x = 10;
console.log(this.x);
}
};
(temp.foo, temp.foo)(); // ?
答案及解析
答案 : 20
技巧 : 經過指派,運算符運算後,都是純函數,不是對象方法的引用。函數中this指向都是windows。
解析 : 逗号操作符會從左到右計算它的操作數,傳回最後一個操作數的值。是以(temp.foo, temp.foo)();等價于var fun = temp.foo;
fun();fun調用時this指向window,是以傳回 20。
14、寫出執行結果,并解釋原因
const company = { name: "Echoyya" };
Object.defineProperty(company, "address", { value: "北京" });
console.log(company); // ?
console.log(Object.keys(company)); // ?
答案及解析
答案 : {name:"Echoyya",address:"北京"}, ["name"]
解析 : defineProperty方法可以給對象添加一個新屬性,或者修改已經存在的屬性。而使用defineProperty給對象添加屬性之後,屬性預設為不可枚舉,
Object.keys方法僅傳回對象中可枚舉的屬性,是以隻列印name
15、寫出執行結果,并解釋原因
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
解析 : 一進制操作符 ++ 先傳回操作值, 再執行自增操作值。
1. num1 是10,因為 inNum 函數先傳回 num 的值,在執行 num 自增
2. num2 是10,因為 num1 作為參數傳入 inPaNum,同理函數先傳回 number 的值,在執行 number 自增
16、寫出執行結果,并解釋原因
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中可以使用參數預設值, 函數未傳參,或參數為undefined,将使用參數預設值。
2. 解構 value 對象并指派給一個新對象,是以 x 的預設值為 {number:10} 。
3. 預設參數在調用時才會計算,每次調用函數,都會建立一個新的對象。調用 multiply(),x的預設值都為 {number:10},是以輸出 20
4. 調用 multiply(value),實際上修改了 value.number的值,輸出 20
5. 再次調用調用 multiply(value),value.number之前被修改為 20,是以輸出 40。
17、寫出執行結果,并解釋原因
// index.js
console.log('running index.js');
import { sum } from './sum.js';
console.log(sum(1, 2));
// sum.js
console.log('running sum.js');
export const sum = (a, b) => a + b;
答案及解析
答案 : running sum.js, running index.js, 3
解析 : import指令是編譯階段執行的,在運作之前。是以被導入的子產品會先運作,而導入子產品的檔案會後執行。
這是CommonJS 中 require() 和 import之間的差別。require()可以在運作代碼時按需加載。
如果使用 require,那麼running index.js、running sum.js、 3會被依次列印。
18、寫出執行結果,并解釋原因
function addToList(item, list) {
return list.push(item);
}
const result = addToList("company", ["yideng"]);
console.log(result); // ?
答案及解析
答案 : 2
解析 : push()方法傳回新數組的長度。若想傳回新數組,應該在push之後傳回list。
19、實作(5).add(3).minus(2) 功能
// 實作 (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('請輸入數字~');
}
return this + number;
};
Number.prototype.minus = function (number) {
if (typeof number !== 'number') {
throw new Error('請輸入數字~');
}
return this - number;
};
console.log((5).add(3).minus(2)); // 6
20、不使用模運算符的情況下,檢查一個數是否是偶數
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,利用奇數除以2會有小數的特點
function isEven(num){
return parseInt(num/2) === Math.round(num/2);
}