JS 紀錄1 - 物件、變數、型別

JavaScript 基本觀念紀錄

執行時期 - 在準備資料
開發時期 - 開始實作

JavaScript 物件 可以指派給一個 變數 並會在執行時期擁有 型別

  • 物件(Object)
    • 這裡的物件代表存在於記憶體中的資料
    • 僅存於執行時期
  • 變數(Variable)
    • 記憶體指標特性(可以指向任何資料)
    • 只能在開發時期宣告 var , let , const
    • 在執行時期只會用來儲存物件的記憶體位置
  • 型別(Type)
    • 僅存於執行時期,並用來標示物件的種類(類型)
    • 不同型別之間可能會有不同的屬性與方法
1
2
3
4
5
6
7
8
var a 
a = 1
a = ‘a’
a = ‘a’+ a

// 1個變數 var a
// 3個型別 undefind,number,string
// 5個記憶體物件

title

JavaScript 都是物件

  • number
  • string
  • boolean
  • null - (此變數可能曾經有值,可能沒有值) 現在沒有值
  • undefind - 此變數還沒有給值,所以不知道是什麼
  • symbol
  • array
  • object
  • regexp

變數

建立方式

先建立物件 在建立型別型別 用來標示物件的種類

1
var a = “5”

a = 變數 , 5 = 物件 , “” = (字串)型別

1
a.name = “jimmy"

a = 變數物件 , name = 屬性 , jimmy = 物件 , “” = (字串)型別

屬性

1
a.name = {}
  • 可以被刪除
  • 內含記憶體指標特性(可以指向任何資料)
  • 任何一個 JavaScript 只有屬性

變數(也可能是屬性)

1
let a = {}
  • 絕對無法刪除(除非去 prototye 刪除父連結)
  • 內含記憶體指標特性(可以指向任何資料)
  • 沒有型別
  • 在記憶體裡指向別人
  • var , let , const 進行宣告
宣告變數 var let const
作用域 Function scope Block scope Block scope
注意事項 盡量不用 var,會汙染 window(全域變數) 造成一堆變數在 window var 宣告過的變數,不能再使用 let 宣告一次 宣告一個唯讀的變數 (變數無法再指向其他物件)
hosting undefined is not defined is not defined

作用域

  • var ( funcotion socpe )
1
2
3
4
5
6
7
8
9
function  myFunction () {
var myVar = " Nick " ;
if ( true ) {
var myVar = " John " ;
console.log (myVar); // "John"
}
console.log (myVar); // "John"
}
console.log (myVar); // undefined, myVar在函數範圍外部無法被使用
  • let ( block scope )
1
2
3
4
5
6
7
8
9
function  myFunction () {
let myVar = " Nick " ;
if ( true ) {
let myVar = " John " ;
console.log (myVar); // "John"
}
console.log (myVar); // "Nick"
}
console.log (myVar); // undefined, myVar在函數範圍外部無法被使用

注意事項

  • var
    • 污染全域(window)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var a = 1
window[‘a’] = 2
delete window.a // false 是變數也是屬性
console.log(a) // 2

// var 宣告更改屬性值(index)
var a = ‘well’ // ‘well'
a[0] // ‘w'
a[0] = ‘j’ // ‘j'
a[0] // ‘w’
a // ‘well’
delete a // false
window.a // ‘well’ 污染全域(window)
window.a = 'jj' // ‘jj’ window 可重新賦值
a[0] // ‘j'
delete window.a // false 是變數也是屬性
  • let
    • 不會污染全域(window)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var a = 1
window[‘a’] = 2
delete window.a // false 是變數也是屬性
console.log(a) // 2

// 改用 let 宣告不會污染全域(window)
let a = ‘jimmy’ // ‘jimmy’
a[0] // ‘j’
a[0] = ‘m’ // ‘m’
a[0] // ‘j'
a // ‘jimmy’
delete a // false
window.a // undefined 不會污染全域(window)
window.a= ‘mm’ // ‘mm’
window.a[0] // ‘m’
delete window.a // false

hosting

  • 當 function 與變數/常數同名稱而提升時,function 的優先程度高於變數/常數
1
2
3
4
5
6
7
8
a()
function a(){
console.log('hello');
}
// 優先 hosting
function a(){
console.log('he’); // he
}
  • var
    • undefind
1
2
console.log (myVar) // undefined 
var myVar = 2
  • let
    • TDZ
      • 程式碼中某個部份變數的參考動作還不能執行,這是因為該變數尚未被初始化
1
2
console.log (myVar) // ReferenceError: myVar is not defined
let myVar = 2 ;
  • const
    • TDZ
1
2
console.log (myVar) // ReferenceError: myVar is not defined
const myVar = 2 ;

變數指派差異

  • var
    • 一般變數下
      • 可以重新修改變數值
      • 可以重新宣告變數
    • 在 object 和 array 下
      • 可以重新修改 object 和 array 的變數值
      • 可以重新宣告 object 和 array 變數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var myVar =  2 ;
myVar = 6
console.log(myVar) // 6

var myVar = 3
console.log(myVar) // 3

// array
var son = ['John']
son.push('jimmy’)
console.log(son) // ["john", "jimmy”]

son =‘marry'
console.log(son) // marry

// object
let q = {r:0}
q.r = 3
console.log(q) // 3

let q = {f:0}
console.log(q.f) // 0
  • let
    • 一般變數下
      • 可以重新修改變數值
      • 不能重新宣告變數
    • 在 object 和 array 下
      • 可以重新修改 object 和 array 的變數值
      • 不能重新宣告 object 和 array 變數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let myVar =  2 ;
myVar = 6
console.log(myVar) // 6

let myVar = 3 ;
console.log(myVar) // SyntaxError: Identifier 'myVar' has already been declared

// array
let q = {r:0}
q.r = 3
console.log(q.r) // 3

let q = {f:0}
console.log(q.f) // SyntaxError: Identifier 'q' has already been declared

//object
let person = [‘John’]
person.push(‘Jimmy’)
console.log(person) // [‘John’,’Jimmy’]

let person = [‘Peater’]
console.log(person) // SyntaxError: Identifier 'person' has already been declared
  • const
    • 一般變數下
      • 不能重新修改變數值
      • 不能重新宣告變數
    • 在 object 和 array 下
      • 可以重新修改 object 和 array 的變數值
      • 不能重新宣告 object 和 array 變數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const myVar =  2 ;
myVar = 6 // SyntaxError: Identifier 'myVar' has already been declared
const myVar = 3 ; // SyntaxError: Identifier 'myVar' has already been declared

//array
const person = {
name : ' Nick '
};
person.name = ' John '
console.log ( person.name ) // "John"

person = "Sandra”
//跳出錯誤,因為重新指派時是不允許使用const宣告出來的變數的
console.log(person) // TypeError: Assignment to constant variable

//object
const person = [];
person.push ( ' John ' );
console.log (person[ 0 ]) // "John"

person = [ " Nick " ]
console.log(person) // TypeError: Assignment to constant variable

型別

型別分類

  • 原始型別(Primitive Type) - 不允許自由擴增屬性
    • 但卻有屬性 & 方法可以使用,因為有原始型別包裹物件 (primititve type wrapper type) & 父元素的關係
1
'abc'.charAt === String.prototype.charAt // true
運算元 型別
Number (數值) number
String (字串) string
Boolean (布林) boolean
Null (空值) object
Undefind (未定義) undefind
Symbol (符號) function
  • 物件型別(Object Type) - 可以自由擴增屬性
運算元 型別
Array (陣列) object
Object (物件) object

型別判斷

typeof

  • null 是基本型別之一,得到 object,而非 null
  • function 是物件的子型別,但 typeof function() {} 是得到 function ,而非 object
  • NaN 表示是無效的數字,但依舊還是數字,因此結果就是 number,而非「不是數字」(not a number)
    • NaN 與任何數字運算都會得到 NaN,並且 NaN 不大於、不小於也不等於任何數字,包含 NaN 它自己
1
2
3
4
5
6
7
8
9
10
typeof 'Hello World!'   // 'string'
typeof true // 'boolean'
typeof 1234567 // 'number'
typeof null // 'object'
typeof undefined // 'undefined'
typeof { name: 'Jack' } // 'object'
typeof Symbol() // 'symbol'
typeof function() {} // 'function'
typeof [1, 2, 3] // 'object'
typeof NaN // 'number'

instanceof

  • 檢查物件是否為指定的建構子所建立的實體
  • 用來測試一個 object 在原型鏈中是否存在一個構造函數的 prototype 屬性
  • 涉及的構造函數
    • 基礎類型:string、number、boolean、undefined、null、symbol
    • 複雜類型:array,object
    • 其他類型:function、regExp、date
  • 語法
    • [對象] instanceof [構造函數], 回傳 boloon 值
    • 注意左側必須是對象(object),如果不是,直接返回 false
1
2
let obj = new Object()
obj instanceof Object // true

基礎類型

  • 檢測的一定要是對象(object)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 第一個不是 object,是基本類型,返回 false
let num = 1
num instanceof Number // false

// 第二個是封裝成 object,所以 true
num = new Number(1)
num instanceof Number // true

// 檢測目標的 __proto__ 與構造函數的 prototype 相同即返回 true,這是不嚴謹的
let num = 1
num.__proto__ === Number.prototype // true
num instanceof Number // false

// 一定要封裝成 object
num = new Number(1)
num.__proto__ === Number.prototype // true
num instanceof Number // true

num.__proto__ === (new Number(1)).__proto__ // true

複雜類型

  • 直接生成構造函數,所以不會像基本類型一樣兩種情況
1
2
3
4
let arr = [ ]
arr instanceof Array // true
arr instanceof Object // true
Array.isArray(arr) // true

其他類型

  • 除了 function,都一樣是 object
1
2
3
4
5
6
7
8
9
// 這裡要注意,function A() {}相當於 let A; A = function(){}
// a 是 new 出來的,所以是經過構造,因此已經是對象,不再是函數,所以 false

function A() {}
let a = new A()
a instanceof Function // false
a instanceof Object // true
A instanceof Function // true
A instanceof Object // true

型別參數傳遞

  • 參數傳遞
Call by reference Call by value
Object String
Array Number
Function Boolean
RegExp Null
Date Undefined
Math Symbol

Call by value

  • 佔用不同記憶體位置
1
2
3
4
5
6
let age = 100;
let age2 = age;
console.log(age, age2); // 100,100

age = 200;
console.log(age, age2); // 200,100

Call by reference

  • 使用相同記憶體位置
  • 記憶體「路徑位置」會被複製
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let superTeam = ['Otree', 'Penguin', 'Frog', 'Jim'];

let fakeTeam = superTeam;
console.log(fakeTeam, superTeam)
// 兩個變數值相同
// ['Otree', 'Penguin', 'Frog', 'Jim’];
// ['Otree', 'Penguin', 'Frog', 'Jim'];


fakeTeam[3] = 'Chris’;
console.log( fakeTeam, superTeam);
// 值也跟著變了
// ['Otree', 'Penguin', 'Frog', ‘Chris’];
// ['Otree', 'Penguin', 'Frog', ‘Chris'];

Call by Sharing (共享參考)

  • 變數值被呼叫時,會直接把該變數所在的記憶體位置做為參考,傳遞給呼叫它的對象
  • 但當對象被賦予新值時,對象會被指定一個新的記憶體位置,值也跟著變了
1
2
3
4
5
6
7
8
9
10
11
12
13
let superTeam = ['Otree', 'Penguin', 'Frog', 'Jim'];

let fakeTeam = superTeam;
console.log(fakeTeam, superTeam)
// 兩個變數值相同
// ['Otree', 'Penguin', 'Frog', 'Jim’];
// ['Otree', 'Penguin', 'Frog', 'Jim'];

fakeTeam = ['Otree', 'Penguin', 'Frog', 'Chris’];
console.log( fakeTeam, superTeam);
// 值也跟著變了
// ['Otree', 'Penguin', 'Frog', ‘Chris’];
// ['Otree', 'Penguin', 'Frog', 'Jim'];
  • 為何會產生不同傳遞方式的錯覺?
    • 在 JavaScript 中所有的 Call by value 型別皆為不可變動的(Immutable)
    • 永遠只能藉由賦予新值的方式將值傳遞給新的變數

物件

物件取屬性值

  • “ . “
  • […]
    • 可取出 number 屬性的屬性值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 建立物件
var car = {
// 擴增屬性
name:'Tesla',
star:function(){
return 'ok'
},
'001':'log'
}

car.name // “Tesla"
car['name’] // “Tesla"
car.star // f(){return ok}
car['star’] // f(){return ok}
car.001 // Uncaught SyntaxError: Unexpected number
car['001’] // "log"

物件複製

  • 物件複製的內容,儲存在不同的記憶區塊,讓彼此不會影響 ?
  • Object.assign(target, …sources)
    • Shallow Copy
      • 只能處理深度只有一層的物件,沒辦法做到真正的 Deep Copy
      • target - 目標物件
      • sources - 來源目標
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const person = {
name:{
firstname:'wei',
lastname:'ck'
},
age:80
}

// 物件裡面還有一個子物件的話,這個子物件是無法被複製
const person2 = Object.assign({},person)
person2.name.firstname='chi'

console.log(person,person2)
// [object Object] {
age: 80,
name: [object Object] {
firstname: “chi",
lastname: "ck"
}
}
// [object Object] {
age: 80,
name: [object Object] {
firstname: "chi",
lastname: "ck"
}
}
  • JSON.parse(JSON.stringify(obj))
    • Deep Copy
      • JSON.stringify 序列化(轉成字串)
      • JSON.parse 物件化(轉成物件)
    • function、regExp、undefind 過不去
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const person = {
name:{
firstname:'wei',
lastname:'ck'
},
age:80
}

// 透過 JSON 將物件序列化(轉成字串)、再物件化(轉成物件)
const person3 = JSON.parse(JSON.stringify(person))
person3.name.firstname = 'chi'

console.log(person,person3)
// [object Object] {
age: 80,
name: [object Object] {
firstname: "wei",
lastname: "ck"
}
}
// [object Object] {
age: 80,
name: [object Object] {
firstname: "chi",
lastname: "ck"
}
}
  • jQuery - Deep Copy & Shallow Copy
    • extend 可以複製物件,可以將加入參數使用深度複製 (deep copy)
    • merge object 順序會影響到結果
    • shallow copy
      • jQuery.extend( target [, object1 ] [, objectN ] )
    • deep copy
      • jQuery.extend( [(deep)true or false], target, object1 [, objectN ] )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var object1 = {
apple: 0,
banana: { weight: 52, price: 100 },
cherry: 97
};

var object2 = {}
jQuery.extend(object2, object1);
console.log(object2)
//[object Object] {
apple: 0,
banana: [object Object] {
price: 100,
weight: 52
},
cherry: 97
}

console.log(object2.banana === object1.banana) // true

var object3 = {}
jQuery.extend(true,object3, object1);
console.log(object3)
// [object Object] {
apple: 0,
banana: [object Object] {
price: 100,
weight: 52
},
cherry: 97
}

console.log(object3.banana === object1.banana) // false
  • 函式庫 lodash - Deep Copy & Shallow Copy
    • shallow copy
      • _.clone()
    • deep copy
      • _.cloneDeep()
1
2
3
4
5
6
7
var objects = [{ 'a': 1 }, { 'b': 2 }]; 

var shallow = _.clone(objects);
console.log(shallow[0] === objects[0]); // true

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]); // false
0%