Primitive types vs objects 原生型態與 object 比較

Node.js Primitive types vs objects 原生型態與 object 比較

JavaScript 有 2 種資料類型

  • Primitive: 原始 / 基本 ( 非物件 )
  • Object: 物件

Primitive types 原生型態

  • strings
  • numbers (Number and BigInt)
  • booleans (true or false)
  • undefined
  • Symbol values (ES6)
  • null

null 是個特別的原生型態,透過 typeof null 你會得到 Object,但其實他是原生型態

所有東西如果不是 Primitive type 原生型態 的話,都會是 object

let obj = {};
let str = 'KJ'
let und = undefined;
let num = 3;
let nul = null;
let bool = true;
let func = () => {}

// number
console.log(typeof num)
// undefined
console.log(typeof und)
// string
console.log(typeof str)
// boolean
console.log(typeof bool)
// function
console.log(typeof func)

// object
console.log(typeof obj)
// object
console.log(typeof nul)

Object type

  • Object
  • Function
  • Array
  • Set

primitive types 與 Object type 比較

屬性 primitive types Object type
immutable 不可改變 不可改變 可改變
變數傳遞方式 passed by value 傳值 passed by reference 傳址
變數複製方式 copied by value 複製值 copied by reference 複製參照位址
比較方式 compared by value 比較數值 compared by reference 比較位址

變數傳遞方式

Primitive

let name = 'Kay';
let secondName = name;
name = 'Jay';
// Jay
console.log(name);
// Kay
console.log(secondName);

非 Primitive

let employee = {
    name: 'Kay'
};

let secondEmployee = employee;

employee.name = 'Jay';
// { name: 'Jay' }
console.log(employee);
// { name: 'Jay' }
console.log(secondEmployee);
// true
console.log(employee === secondEmployee);
let employee = {
    name: 'Kay'
};

let secondEmployee = {
    name: 'Kay'
};
// false
console.log(employee === secondEmployee);

宣告兩個 objective type 讓 key-value 值完全相同,但是兩者比較後,回傳是 false ,因為 by reference 在比較時,真正比的是兩者的位置,所以 個別的 Array 、Object 永遠不會相同。

比較變數

// false
console.log([] === []);
// false
console.log({} === {});
// false
console.log([1, 2, 3] === [1, 2, 3]);
// false,NaN 永遠不會等於自己,要比對可以用 isNaN()
console.log(NaN === NaN);

變數類型

String 字串

字串比較特別,還有一個特色叫 immutable ,當使用String.prototype methods做任何操作都不會影響到原字串

let s = 'javascript';
s.toUpperCase();
s.slice(-4);
s.concat('es6');
s[3] = 'x';
// javascript
console.log(s)
// a
console.log(s[3])

let newString = s.concat(' es6');
// javascript es6
console.log(newString);

在改變字串後,必須要用變數去接收這個改變的值

Array 陣列

let arr = [
    'Kay',
    'Jay'
];
// 複製陣列
let copyArr = arr;
copyArr[1] = 'KK';

// [ 'Kay', 'KK' ]
console.log(arr);
// [ 'Kay', 'KK' ]
console.log(copyArr);

Object Type 複製

Array 複製

1. 使用 for 迴圈輪詢所有元素

const Employee = ['Kay', 'Jay']

const copyEmployee = []

for (let i = 0; i < Employee.length; i++) {
    copyEmployee[i] = Employee[i]
}

copyEmployee[0] = 'KJ';
// [ 'Kay', 'Jay' ]
console.log(Employee);
// [ 'KJ', 'Jay' ]
console.log(copyEmployee);
// false
console.log(Employee === copyEmployee);

2. 使用 Array.slice() 切割複製元素

const Employee = ['Kay', 'Jay']

// 用 Slice 切割不指定切割位置
const copyEmployee = Employee.slice()

copyEmployee[0] = 'KJ';
// [ 'Kay', 'Jay' ]
console.log(Employee);
// [ 'KJ', 'Jay' ]
console.log(copyEmployee);
// false
console.log(Employee === copyEmployee);

3. 用 Array.concat() 重新連接陣列資料

const Employee = ['Kay', 'Jay']

// 用 concat 重新連接陣列資料
const copyEmployee = [].concat(Employee)

copyEmployee[0] = 'KJ';
// [ 'Kay', 'Jay' ]
console.log(Employee);
// [ 'KJ', 'Jay' ]
console.log(copyEmployee);
// false
console.log(Employee === copyEmployee);

4. 用 …array 重新賦值到新的變數

const Employee = ['Kay', 'Jay']

// 用 ...array 重新賦值到新的變數
const copyEmployee = [...Employee];

copyEmployee[0] = 'KJ';
// [ 'Kay', 'Jay' ]
console.log(Employee);
// [ 'KJ', 'Jay' ]
console.log(copyEmployee);
// false
console.log(Employee === copyEmployee);

5. 用 Array.from() 重新賦值到新陣列資料

const Employee = ['Kay', 'Jay']

// 用 from 重新賦值到新陣列資料
const copyEmployee = Array.from(Employee)

copyEmployee[0] = 'KJ';
// [ 'Kay', 'Jay' ]
console.log(Employee);
// [ 'KJ', 'Jay' ]
console.log(copyEmployee);
// false
console.log(Employee === copyEmployee);

Object 複製

1. 使用 Object.assign() 重新賦值

const Employee = {
    name: 'Kay',
    age: 17
}

// 使用 Object.assign 重新賦值
const copyEmployee = Object.assign({}, Employee, {
    name: 'KJ',
    job: 'engineer'
});

// { name: 'Kay', age: 17 }
console.log(Employee);
// { name: 'KJ', age: 17, job: 'engineer' }
console.log(copyEmployee);
// false
console.log(Employee === copyEmployee);

使用 …objedt 重新賦值到新的變數

const Employee = {
    name: 'Kay',
    age: 17
}

const copyEmployee = {...Employee,  name: 'KJ', job: 'engineer' };

// { name: 'Kay', age: 17 }
console.log(Employee);
// { name: 'KJ', age: 17, job: 'engineer' }
console.log(copyEmployee);
// false
console.log(Employee === copyEmployee);

3. 使用 JSON.stringify 及 JSON.parse 複製物件

若物件有多層巢狀結構的話,可以用這個方式複製,但效能較差

const Employee = {
    name: 'Kay',
    age: 17
}
// 使用 JSON.stringify 及 JSON.parse 複製物件
const copyEmployee = JSON.parse(JSON.stringify(Employee))

copyEmployee.name = 'KJ';
copyEmployee.job = 'engineer';

// { name: 'Kay', age: 17 }
console.log(Employee);
// { name: 'KJ', age: 17, job: 'engineer' }
console.log(copyEmployee);
// false
console.log(Employee === copyEmployee);

判斷變數實際類型

1. typeof

用 typeof 可以檢查資料型態,但在判斷 typeof null 的時候會回傳 object

let EmployeeArray = ['Kay', 'Jay'];
let EmployeeObject = {
    name: 'Kay',
    age: 17
}

let EmployeeFunc = () => {};
let EmployeeString = 'Kay';
let EmployeeUndefined = undefined;
let EmployeeNumber = 3;
let EmployeeBoolean = true;
let EmployeeNull = null;

// object
console.log(typeof EmployeeArray);
// object
console.log(typeof EmployeeObject);
// function
console.log(typeof EmployeeFunc);
// string
console.log(typeof EmployeeString);
// undefined
console.log(typeof EmployeeUndefined);
// number
console.log(typeof EmployeeNumber);
// boolean
console.log(typeof EmployeeBoolean);
// object
console.log(typeof EmployeeNull);

如果對一個未宣告或未賦值的變數做 typeof,會回傳 undefined

所以有些時候可以拿 typeof 來檢查變數是否存在。

2. Object.toString()

使用 Object.prototype.toString.call(),回傳的字串後面就是這個變數的類型

let EmployeeArray = ['Kay', 'Jay'];
let EmployeeObject = {
    name: 'Kay',
    age: 17
}

let EmployeeFunc = () => {};
let EmployeeString = 'Kay';
let EmployeeUndefined = undefined;
let EmployeeNumber = 3;
let EmployeeBoolean = true;
let EmployeeNull = null;

let variableTypeArray = Object.prototype.toString.call(EmployeeArray);
let variableTypeObject = Object.prototype.toString.call(EmployeeObject);
let variableTypeFunc = Object.prototype.toString.call(EmployeeFunc);
let variableTypeString = Object.prototype.toString.call(EmployeeString);
let variableTypeUndefined = Object.prototype.toString.call(EmployeeUndefined);
let variableTypeNumber = Object.prototype.toString.call(EmployeeNumber);
let variableTypeBoolean = Object.prototype.toString.call(EmployeeBoolean);
let variableTypeNull = Object.prototype.toString.call(EmployeeNull);

// [object Array]
console.log(variableTypeArray);
// [object Object]
console.log(variableTypeObject);
// [object Function]
console.log(variableTypeFunc);
// [object String]
console.log(variableTypeString);
// [object Undefined]
console.log(variableTypeUndefined);
// [object Number]
console.log(variableTypeNumber);
// [object Boolean]
console.log(variableTypeBoolean);
// [object Null]
console.log(variableTypeNull);

參考資料