02 - 并不是所有东西都是对象

2020-12-29 15:54:50 浏览数 (1)

挖槽,这年轻人无耻

本文翻译自如下链接:

代码语言:javascript复制
https://dev.to/d4nyll/not-everything-in-javascript-is-an-object

关于 JS 是面向对象编程语言还是函数式编程语言,还是存在很多困惑。确实,JS 可以两种都兼容,但这会导致一个问题,是什么呢?会导致人们会有这样的疑问:在 JS 中,所有的东西都是对象?

今天我们就来解决这个问题

让我们从头开始吧

在 JS 中,有六种原始数据类型:

  • 布尔值 true or false
  • null
  • undefined
  • number 双精度 64 位浮点型数。JS 中没有整数
  • string
  • symbol (ES6 中新增的功能)

除了这六个基本类型,ECMAScript 标准还定义了一个 Object 类型,它用来存储键值对类型。

代码语言:javascript复制
const obj = {
    key: 'value'
}

简而言之,不是原始类型的任何事物都是 Object,并且包括函数和数组。

所有的功能都是对象?

代码语言:javascript复制
// 原始类型
true instanceof Object // false
null instanceof Object // false
undefined instanceof Object // false
0 instanceof Object // false
'bar' instanceof Object // false

// 非原始类型
const foo = function() {}
foo instanceof Object // true

原始类型

基本类型是没有其他附加方法的,所以你永远看不到 undefined.toString()。也正因为如此,原始类型是不可变动的,因为他们没有附加可以使得他们变异的方法。

你可以将原始类型重新分配给变量,但是它将是一个新的值,旧的值不能也不可以更改。

代码语言:javascript复制
const answer = 42
answer.foo = "bar"
console.log(answer.foo) // undefined

基本类型是不可变动的。

此外,与对象不同,原始类型作为值本身存储为对象,后者作为参考存储,这在执行相等性检查时会产生影响。

代码语言:javascript复制
"dog" === "dog" // true
14 === 14 // true
{} === {} // false
[] === [] // false
(function(){}) === (function(){}) // false

基本类型是按值存储,对象是按引用存储

函数

函数是一种特殊的对象,具有一些特殊的属性,例如 constructor 和 call

代码语言:javascript复制
const foo = function(bar) {}
foo.name // foo
foo.length // 1

就像普通对象一样,您可以向对象添加新属性:

代码语言:javascript复制
foo.bar = "baz"
console.log(foo.bar) // baz

这使得函数成为一等公民,因为它可以像传递给其他函数的参数一样传递给其他对象,这就像其他对象一样。

方法

方法是对象的属性,它也恰好是函数。

代码语言:javascript复制
const foo = {}
foo.bar = function(){console.log("baz")}
foo.bar() // 'baz'

构造函数

如果你有几个共享同一实现的对象,则可以将该逻辑放在构造函数中,然后使用该构造函数创建这些对象。

构造函数与其他函数没有什么不同,在 new 关键字之后使用函数时,该函数将用作构造函数。

任何函数都可以当做构造函数。

代码语言:javascript复制
const Foo = function() {}
const bar = new Foo()
console.log(bar); // {}
bar instanceof Foo; // true
bar instanceof Object; // true

构造函数将返回一个对象,你可以在函数体内使用 this 来为对象分配新的属性。因此,如果我们要使许多对象的属性 bar 初始化为 value 'baz',则可以创建一个新的构造函数 Foo 来封装该逻辑。

代码语言:javascript复制
const Foo = function() {
    this.bar = "baz"
}
const qux = new Foo()
console.log(qux); // {bar: 'baz'}
qux instanceof Foo // true
qux instanceof Object // true

你可以使用构造函数创建一个新的对象。

运行诸如 new Foo() 的构造函数,不带 (new)就像普通函数一样执行,函数内部将与执行上下文 this 相对应。因此,如果我们直接 Foo() ,实际上是在 window 对象上调用该函数:

代码语言:javascript复制
Foo() // undefined
window.bar // 'baz'

相反,如你所见,将普通函数作为构造函数运行:

代码语言:javascript复制
const pet = new String('dog')

包装对象

类似 String, Number,Boolean,Function,当 new 关键字结合为这些原始类型创建包装对象。

String 是一个全局函数,当传入参数时会创建原始字符串,它将尝试该参数转换为字符串:

代码语言:javascript复制
String(1337); // '1337'
String(true); // 'true'
string(null); // 'null'
String(undefined); // 'undefined'
String() // ''
String('dog') === 'dog'; // true
typeof  String('pet'); // 'string'

但是你也可以将该String函数作用构造函数:

代码语言:javascript复制
const pet = new String('dog')
typeof pet // 'object'
pet === 'dog' // false

这将创建一个新的对象,该对象便是 string 'dog',具有以下属性:

代码语言:javascript复制
{
    0: 'd',
    1: 'o',
    2: 'g',
    length: 3
}

对象包装通常也称为包装器对象。

自动装箱

有趣的是,原始字符串和对象的构造函数都是函数,更有趣的是.constructor,当我们已经介绍了原始 类型不能有方法时,你可以调用原始字符串。

代码语言:javascript复制
const pet = new String('dog')
pet.constructor === String // true
String('dog').constuctor === String // true

发生力一个称为自动装箱的过程,当你尝试某些原始类型上调用属性或者方法时,js 首先会将其转换为 临时包装对象,然后在不影响原始属性的情况下访问其上的属性、方法。

代码语言:javascript复制
const foo = "bar"
foo.length // 3
foo === "bar" // true

在上面的示例中,要访问property length,JavaScript将自动装箱foo到包装对象中,访问包装对象的length属性,然后将其丢弃。这样做不会影响foofoo仍然是原始字符串)。

这也解释了为什么在尝试将属性分配给原始类型时JavaScript不抱怨的原因,因为赋值是在该临时包装对象上完成的,而不是原始类型本身。

代码语言:javascript复制
const foo = 42;
foo.bar = "baz"; // Assignment done on temporary wrapper object
foo.bar; // undefined

如果您使用没有包装对象的原始类型(例如undefined或)尝试此操作,则报错null

代码语言:javascript复制
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具有自动装箱功能,因此某些基本类型(字符串,数字,布尔值)看起来像对象。

万水千山总是情,点个再看行不行

0 人点赞