翻译:javascript中所有事物并不都是对象
关于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:我在评论区发现了一张很好的图片