(譯) javascript中所有事物並不都是物件

飛翔的大象發表於2019-03-08

關於javascript是物件導向程式設計(OOP)語言還是函式式語言,存在和很多混淆,其實,javascript都可以在兩者中使用。 這會導致人們問:在JavaScript中所有事物是物件嗎?函式是什麼? 這篇post將會作出解釋。

讓我們從頭開始吧

在JavaScript中有六種原始資料型別

  • Booleans - true or false
  • null
  • undefined
  • number - 雙精度的64位的浮點數,JavaScript中沒有整數
  • string
  • symbol (new in ES6)

除了這六種基本型別,ECMAScript標準也定義了一個object型別,它是一個鍵值對儲存

const object = {
  key: "value"
}
複製程式碼

所以,簡單來說,不是基本型別的是事物是一個object,並且包括functionarray

所有的函式都是object

// Primitive types
true instanceof Object; // false
null instanceof Object; // false
undefined instanceof Object; // false
0 instanceof Object; // false
'bar' instanceof Object; // false

// Non-primitive types
const foo = function () {}
foo instanceof Object; // true
複製程式碼

基本型別(Primitive types)

基本型別沒有掛載方法,所以你永遠不會看見undefined.toString()。也是因為這個,基本型別是不可變的,因為沒有附加方法能改變他。

你可以為基本型別重新分配給變數,但它將會是一個新的值,不是原來的值,也不是改變。

const answer = 42
answer.foo = "bar";
answer.foo; // undefined
複製程式碼

基本型別是不可變的

此外,基本型別作為本身的值儲存,不像object作為引用儲存。這會影響相等性檢查。

"dog" === "dog"; // true
14 === 14; // true

{} === {}; // false
[] === []; // false
(function () {}) === (function () {}); // false
複製程式碼

基本型別作為值儲存,object作為引用儲存

函式(function)

函式是一種特殊型別的object,它具有一些特殊的屬性,例如callcontractor

const foo = function (baz) {};
foo.name; // "foo"
foo.length; // 1
複製程式碼

就像普通的物件一樣,你可以為物件增加新的屬性。

foo.bar = "baz";
foo.bar; // "baz"
複製程式碼

這可以是函式成為一等公民(This makes functions a first-class citizen.)。因為他可以像任何其它物件一樣當作引數傳遞給其他引數。

方法(method)

方法是一個物件屬性,也是一個函式。

const foo = {};
foo.bar = function () { console.log("baz"); };
foo.bar(); // "baz"
複製程式碼

函式的建構函式

如果你有多個相同實現的物件,你可以將實現邏輯放在建構函式中,然後使用這個建構函式建立這些物件。

建構函式和普通函式沒有什麼區別,當函式在new關鍵字後使用,它被當作建構函式使用。

任何函式都可以是建構函式

const Foo = function () {};
const bar = new Foo();
bar; // {}
bar instanceof Foo; // true
bar instanceof Object; // true
複製程式碼

一個建構函式會返回一個object,你可以在函式內部使用this增加新的屬性。因此我們想要為多個物件的屬性bar初始化為baz,我們可以建立一個新的建構函式Foo來封裝這個邏輯

const Foo = function () {
  this.bar = "baz";
};
const qux = new Foo();
qux; // { bar: "baz" }
qux instanceof Foo; // true
qux instanceof Object; // true
複製程式碼

你可以使用建構函式來建立一個新的物件

不帶new關鍵字執行建構函式,就像Foo(),將會像普通的函式一樣。函式內部的this將會指向執行上下文。因此如果我們在所有函式的外部呼叫Foo(),它實際上會修改window物件

Foo(); // undefined
window.bar; // "baz"
複製程式碼

相反,將普通的函式作為建構函式執行,你會得到一個新的空物件,正如你看到的那樣。

const pet = new String("dog");
複製程式碼

包裝物件(wrapper object)

由於函式如String,Number,Boolean,Function等而產生混淆,當使用new呼叫時,會為這些基本型別建立包裝器物件。

String是一個全域性函式,它在引數中傳遞時建立一個原始字串;它會嘗試將該引數轉換為字串。

String(1337); // "1337"
String(true); // "true"
String(null); // "null"
String(undefined); // "undefined"
String(); // ""
String("dog") === "dog" // true
typeof String("dog"); // "string"
複製程式碼

但您也可以使用String函式作為建構函式。

const pet = new String("dog")
typeof pet; // "object"
pet === "dog"; // false
複製程式碼

這將建立一個表示字串“dog”的新物件(通常稱為包裝物件),具有以下屬性:

物件包裝器通常也稱為包裝器物件。 Object wrappers are often referred to as wrapper objects, too. Go figure.

自動裝箱(Auto-Boxing)

有趣的是,原始字串和物件的建構函式都是String函式。 更有趣的是你可以在原始字串上呼叫.constructor,當我們已經瞭解了基本型別不能有方法時!

const pet = new String("dog")
pet.constructor === String; // true
String("dog").constructor === String; // true
複製程式碼

發生的事情是一個叫做自動裝箱的過程。 當您嘗試在某些基本型別上呼叫屬性或方法時,JavaScript將首先將其轉換為臨時包裝器物件,並訪問其上的屬性/方法,而不會影響原始物件。

const foo = "bar";
foo.length; // 3
foo === "bar"; // true
複製程式碼

在上面的示例中,要訪問屬性長度,JavaScript將autoboxed foo轉換為包裝器物件,訪問包裝器物件的length屬性,然後將其丟棄。 這樣做不會影響foo(foo仍然是一個原始字串)。

這也解釋了為什麼當您嘗試將屬性分配給基本型別時JavaScript不會報錯,因為賦值是在該臨時包裝器物件上完成的,而不是基本型別本身。

const foo = 42;
foo.bar = "baz"; // Assignment done on temporary wrapper object
foo.bar; // undefined
複製程式碼

如果你嘗試使用沒有包裝器物件的基本型別,例如undefined或null,它會報錯。

const foo = null;
foo.bar = "baz"; // Uncaught TypeError: Cannot set property 'bar' of null
複製程式碼

總結

  1. 並非JavaScript中的所有內容都是物件
  2. JavaScript中有6種基本型別
  3. 所有不是基本型別的東西都是一個物件
  4. 函式只是一種特殊型別的物件
  5. 函式可用於建立新物件
  6. 字串,布林值和數字可以表示為基本型別,但也可以表示為物件
  7. 某些基本型別(字串,數字,布林值)似乎表現得像物件,因為JavaScript特色稱為自動裝箱。

PS:我在評論區發現了一張的圖片

image

相關文章