挖槽,这年轻人无耻
本文翻译自如下链接:
代码语言: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
属性,然后将其丢弃。这样做不会影响foo
(foo
仍然是原始字符串)。
这也解释了为什么在尝试将属性分配给原始类型时JavaScript不抱怨的原因,因为赋值是在该临时包装对象上完成的,而不是原始类型本身。
代码语言: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具有自动装箱功能,因此某些基本类型(字符串,数字,布尔值)看起来像对象。
万水千山总是情,点个再看行不行