- 此為翻譯文章
- 原文連結
關於javascript是物件導向程式設計(OOP)語言還是函式式語言,存在和很多混淆,其實,javascript都可以在兩者中使用。 這會導致人們問:在JavaScript中所有事物是物件嗎?函式是什麼? 這篇post將會作出解釋。
讓我們從頭開始吧
在JavaScript中有六種原始資料型別
- Booleans -
true
orfalse
- null
- undefined
- number - 雙精度的64位的浮點數,JavaScript中沒有整數
- string
- symbol (new in ES6)
除了這六種基本型別,ECMAScript標準也定義了一個object
型別,它是一個鍵值對儲存
const object = {
key: "value"
}
複製程式碼
所以,簡單來說,不是基本型別的是事物是一個object
,並且包括function
和array
所有的函式都是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,它具有一些特殊的屬性,例如call
和contractor
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
複製程式碼
總結
- 並非JavaScript中的所有內容都是物件
- JavaScript中有6種基本型別
- 所有不是基本型別的東西都是一個物件
- 函式只是一種特殊型別的物件
- 函式可用於建立新物件
- 字串,布林值和數字可以表示為基本型別,但也可以表示為物件
- 某些基本型別(字串,數字,布林值)似乎表現得像物件,因為JavaScript特色稱為自動裝箱。
PS:我在評論區發現了一張的圖片