面試題目分享

寫出結果,並解釋緣由
let a = {n:1};
let b = a;
a.x = a = {n:2};
console.log(a.x);
console.log(b.x);

解析
知識點:點的優先級大於等號的優先級;等號從右向左執行 promise

第一步:a=b, 因此a和b指向同一個內存地址
第二步:根據知識點,點號優先級高於,因此先執行a.x,即a和b都是 {n: 1, x: undefined}
第三步:等號從右向左執行,即a又變回了{n: 2}同時因而a指向了 { n : 2 } 再也不是以前的地址
第四步:執行a.x = a。左邊的a是舊的a,等於b
b = { n:1, x:{n:2} }異步

輸出:undefined {n: 2}async

拓展
let a = {n:1};
let b = a;
b.n = a.f = {n:2};
console.log(a);
console.log(b);

輸出: {n:{n:2}, f:{n:2}}, {n:{n:2}, f:{n:2}}函數

寫出結果,並解釋緣由
function user(obj){
    obj.name = 'aaa';
    obj = new Object();
    obj.name = 'bbb';
}
 
let person = new Object();
user(person);
console.log(person.name);

解析
引用類型作爲參數進入函數,即複製的是內存區域的指針,可以理解形參obj首先由argument[0]賦值,即obj = argument[0]。當person進到函數的過程第一步就是argument[0] = person,即person的指針給了argument[0]。當obj.name ='aaa';由於argument[0]指針是person,而obj = argument[0],所以obj也就是指向person。所以person.name='aaa'對象。obj = new Object();創建一個新對象並他的指針複製給變量obj,此時obj的指針不再指向person了。而person的指針是不會受到影響的this

寫出結果,並解釋緣由
var a1 = {}, b1 = '123', c1 = 123;
a1[b1] = 'b';
a1[c1] = 'c';
console.log(a1[b1]);
 
var a2 = {}, b2 = Symbol('123'), c2 = Symbol('123');
a2[b2] = 'b';
a2[c2] = 'c';
console.log(a2[b2]);
 
var a3 = {}, b3 = {key:'123'}, c3 = {key:'456'};
a3[b3] = 'b';
a3[c3] = 'c';
console.log(a3[b3]);

知識點:
1.對象的鍵名只能是字符串Symbol類型
2.其餘類型的鍵名會被轉化成字符串類型
3.對象轉字符串默認會調用 toString 方法
4.symbol是ES6 中新添加的數據類型。Symbol 本質上是一種惟一標識符,可用做對象的惟一屬性名,這樣其餘人就不會改寫或覆蓋你設置的屬性值
解析:
1.在 a1 中,a1[b1] = 'b' ; a1[c1] = 'c' ; c1 的鍵名會被轉換成字符串 '123' 覆蓋掉 b1 ,因此結果爲 'c'
2.在 a2 中,b2,c2 都是 Symbol 類型,不須要轉換,任何一個 Symbol 類型的值都是不相等的,因此不會覆蓋,結果爲 'b'
3.在 a3 中 , b3,c3 都是 object 類型,做爲鍵名會調用 toString 方法轉換爲字符串 [object Object],c3 覆蓋 b3 的值,結果爲 'c'
結果:
'c' , 'b' , 'c'prototype

寫出結果,並解釋緣由
async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
 
async function async2() {
    console.log('async2');
}
 
console.log('script start');
 
setTimeout(()=>{
    console.log('setTimeout');
},0)
 
async1();
 
new Promise((resolve)=>{
    console.log('promise1');
    resolve();
}).then(()=>{
    console.log('promise2');
});
 
console.log('script end');

知識點:
1.Promise 一旦被定義就會當即執行
2.Promise 優先於 setTimeout 宏任務,因此 setTimeout 回調會最後執行
3.Promise 的 resolve 和 reject  是異步執行的回調。因此 resolve() 會被放到回調隊列中,在主函數執行完和 setTimeout 以前調用
4.await 執行完後,會讓出線程。async 標記的函數會返回一個 Promise 對象
5.宏任務優先執行於微任務
常見宏任務:script、I/O 、setTimeout、setInterval
常見微任務:Promise.then catch finally、process.nextTick線程

答案
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout指針

拓展
console.log('1'); 
setTimeout(function() { 
    console.log('2'); 
    process.nextTick(function() { 
        console.log('3'); 
    }) 
    new  Promise(function(resolve) { 
        console.log('4'); 
        resolve(); 
     }).then(function() { 
        console.log('5') 
      }) 
})
process.nextTick(function() { 
    console.log('6'); 
}) 
new  Promise(function(resolve) { 
    console.log('7'); 
    resolve(); 
}).then(function() { 
    console.log('8');
})
setTimeout(function() { 
    console.log('9'); 
    process.nextTick(function() { 
        console.log('10'); 
     }) 
     new  Promise(function(resolve) { 
        console.log('11'); 
        resolve(); 
     }).then(function() { 
        console.log('12') 
     }) 
})

解析
第一個宏觀任務:
1.第一個宏觀任務script 做爲總體進入主線程 遇到console.log('1') 輸出1
2.遇到setTimeout,宏觀任務(如今第一個宏觀任務script尚未執行完畢 會分配到宏觀任務中 暫時還不會執行)
3.遇到下面的process.nextTick 分配到微觀任務中
4.遇到Promise,new Promise直接執行,輸出7。then被分發到微任務Event Queue中 5.此時的微觀任務表中:process.nextTick,Promise.then 則執行結果爲 6 8
第一個宏觀任務 執行結果 : 1 7 6 8
第二個宏觀任務:
1.第二個宏觀任務是第一個setTimeout
2.跟script的執行順序是同樣的 碰到console.log('2') 執行2 process.nextTick會分配到微觀任務 Promise會當即執行 而後.then分配到微觀任務
輸出結果:2 4 3 5
第三個宏觀任務:
第三個宏觀任務就是第二個setTimeout 和第二個宏觀任務執行順序同樣
輸出結果 9 11 10 12code

寫出結果,並解釋緣由
const num = {
    a:10,
    add(){
        return this.a + 2;
    },
    reduce:()=>this.a - 2
};
 
console.log(num.add());
console.log(num.reduce());

解析:
知識點函數體內的this對象,就是 定義 時所在的對象,而不是使用時所在的對象。
add是普通函數,而 reduce 是箭頭函數。對於箭頭函數,this 指向是他定義時的位置的環境,於普通函數不一樣。 這意味着當咱們調用 reduce 時,它不是指向 num 對象,而是指其定義時的環境(window)。沒有值 a 屬性,因此返回 undefined。undefined - 2 返回 NaN
輸出:NaN對象

寫出結果,並解釋緣由
var fullname = 'a';
var obj = {
    fullname: 'b',
    prop : {
        fullname: 'c',
        getFullname: function(){
            return this.fullname;
        }
    }
};
console.log(obj.prop.getFullname());
var test = obj.prop.getFullname;
console.log(test());

解析:
對第一個 obj.prop.getFullname() 而言,getFullname() 是做爲 obj.prop 對象的一個方法調用的,所以此時的執行環境應該是這個對象。
obj.prop.getFullname 被分配給 test 變量時,此時的執行環境變成了全局對象(window),緣由是 test 是在全局做用域下定義的。所以,此時 this 指向的是全局做用域的 fullname 變量,即 a

寫出結果,並解釋緣由
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();

解析:Foo.a() 這個是調用 Foo 函數的靜態方法 a,雖然 Foo 中有優先級更高的屬性方法 a,但 Foo 此時沒有被調用,因此此時輸出 Foo 的靜態方法 a 的結果:4let obj = new Foo(); 使用了 new 方法調用了函數,返回了函數實例對象,此時 Foo 函數內部的屬性方法初始化,原型鏈創建。 obj.a() ; 調用 obj 實例上的方法 a,該實例上目前有兩個 a 方法:一個是內部屬性方法,另外一個是原型上的方法。當這二者都存在時,首先查找 ownProperty ,若是沒有才去原型鏈上找,因此調用實例上的 a 輸出:2Foo.a() ; 根據第2步可知 Foo 函數內部的屬性方法已初始化,覆蓋了同名的靜態方法,因此輸出:1

相關文章
相關標籤/搜索