問題回顧(JavaScript)

Q: primitive type & object

最後一行程式碼的輸出結果是什麼?請解釋這個結果是怎麼來的?

1
2
3
4
var a = [ 1, 2, 3 ];
var b = a;
a.push(5);
console.log(b)

A

  1. 最後一行程式碼的輸出結果

[1, 2, 3, 5]

  1. 請解釋這個結果是怎麼來的?
    主要是基本型別(primitive type)和物件(object)保存資料方式不同(記憶體的儲存方式不同),

基本型別
保存資料的方式是:pass by value

1
2
3
let a = 'hi'
let b = a
a = 'Hello, World'

第一行程式碼,hi assign 給變數 a ,就像一個叫做 a 的盒子,裡面放 ‘hi’ 的值。
a 再 assign 給 b ,就像另外新增了 b 的盒子裡面裝著 a 的值。
當重新賦值給 a 時不會改變 b 盒子內的值。
物件
保存資料的方式是:pass by Reference

1
2
3
4
var a = [ 1, 2, 3 ];
var b = a;
a.push(5);
console.log(b)

當 a 把物件 assign 給 b ,指派的是參照位置(reference),也就是 [1, 2, 3] 在記憶體的位置。
a 與 b 目前指向是同一個地方。
因此當 a 改變,b 也會隨著改變。

Q: Scope & setTimeout

在以下程式碼中,console.log(i) 輸出結果是?請解釋這個輸出結果是怎麼來的。

1
2
3
4
5
for(var i = 0; i < 5; i++){
setTimeout(function(){
console.log(i);
},1000);
}

另外,如果想確保輸出結果如下圖,請問要如何修改程式?為什麼?

0
1
2
3
4

A

  1. console.log(i) 輸出結果是?
1
2
3
4
5
5
5
5
5
5

造成這個結果原因主要是:

var
非同步 setTimeout

變數看宣告的位置不同會有不同的作用範圍:全域、區域,而 var 不受限制。就算在區域中(block)中宣告,仍然會改變區塊外的變數,造成全域變數污染的狀況。
以題目來說非同步執行的特性,每一次的 setTimeout 都會將裡面的程式放進任務序列,要在 for 迴圈執行完畢後才執行 console.log(i),此時的 var 已經執行到 5 ,因此在執行 setTimeout 的 callback 時在 console 顯示的值是 5。

  1. 如果想確保輸出結果如下圖,請問要如何修改程式?為什麼?
    兩個方式,但比較建議使用第二種,將 var 改成 let
    第一種,將 setTimeout 拿掉,直接執行 for 迴圈印出 console 內容。
1
2
3
for(var i = 0; i < 5; i++){
console.log(i)
}

第二種,將宣告變數的 var ,改成 let ,以避免變數污染。

1
2
3
4
5
for(let i = 0; i < 5; i++){
setTimeout(function(){
console.log(i);
},1000);
}

Q

請問下方三行程式碼各自在做什麼?

function Person(){}
var person = Person()
var person = new Person()

A

function Person() { },宣告叫 Person 的 function
var person = Person(),宣告 person 的變數,將 Person() 執行後的結果指派給 person
var person = new Person(),宣告 person 的變數,將 new Person() 建立的物件實體指派給 person

Q: this

下列程式碼會在 console 輸出什麼?為什麼?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const myObject = {
foo: 'bar',
func: function () {
const self = this
console.log('outer func: this.foo = ', this.foo)
console.log('outer func: self.foo = ', self.foo);
(function () {
console.log('inner func: this.foo = ', this.foo)
console.log('inner func: self.foo = ', self.foo)
}())
}
}

myObject.func()

A

  1. 輸出結果
1
2
3
4
outer func: this.foo =  bar
outer func: self.foo = bar
inner func: this.foo = undefined
inner func: self.foo = bar
  1. 為什麼
    this 代表『誰呼叫這個函式』,
    而這個『誰』是怎麼來的?依照不同的綁定(binding)的方式,
    outer func: this.foo = bar
    依題目為隱式綁定(implicit binding),this 所指的是 myObject 的物件,因此可以看成 myObject.foo = bar
    outer func: self.foo = bar
    依題目, this 指派給 self 變數,因此 self 也指向 myObject ,可以看成myObject.foo = bar
    inner func: this.foo = undefined
    依題目為預設綁定,與前兩個情況不同的地方在於,this 都可以清楚的指到 myObject 物件,但依題目的情況, this 無法指到物件,因此 JavaScript 會直接綁定在 window 上成為全域物件。
    而 window 沒有 foo 的變數,因此出現 undefined
    inner func: self.foo = bar
    第二個 function 中沒有 self 的變數,就會往外找。
    在第一個 function 中找到 self 的變數,const self = this, this = myObject ,因此可以看成 myObject.foo = bar