【问题标题】:Why do functions seem to be objects?为什么函数看起来是对象?
【发布时间】:2020-07-30 08:40:30
【问题描述】:

我正在尝试学习 JS/ES,但对类和对象有点困惑。

下面的两个代码sn-ps做的完全一样,但是最后一个明显是使用了一个类。但是第一次使用的是什么?我以为一个类在执行时变成了一个对象?

我原以为clock 是一个函数,但它显然可以用作一个对象。这是为什么呢?

当第一种方式更短时,为什么有人会想要使用类方式?

var clock = {
  start() {
    setInterval(() => console.log(Date()), 1000) 
  }
}
clock.start()

class Clock {
  start() {
    setInterval(() => console.log(Date()), 1000)
  }
}
const c = new Clock
c.start()

【问题讨论】:

  • 现在我们在日常场景中谈到“对象”时,通常指的是其他语言中的“记录”或“字典”,在js中我们称之为“普通对象”。那么为什么clock 是一个普通对象呢?因为你是这样声明的。您将 start 声明为函数,而不是 clock
  • 为什么要使用类,因为你可以var c1 = new Clock(), c2 = new Clock() 并节省时间。
  • 请注意,clock 不是函数。它有一个属性start,它是一个函数。但函数是对象,这是一个有用的东西。
  • @sinanspd 是的,但你说得好像这是 JavaScript 特有的问题,其实不是。
  • 当 JS 中没有“类”时,我们习惯于将类模拟为一个函数。类函数充当构造函数,然后我们将通过将属性和方法插入到function_name.prototype 中来添加它们。每当您使用new function_name (); 时,函数本身就会运行(充当构造函数)并且新对象将继承您附加到原型的属性和方法。直接将“类”声明为对象的事情是模仿“静态”类。您不能使用它来生成实例。现在有一些优雅的方法可以在引擎盖下做同样的事情

标签: javascript node.js ecmascript-2017


【解决方案1】:

类是对象的模板。它们描述了一堆对象的行为方式。在 javascript 中,new Clock() 是您获取 class Clock 的新 object 的方式。

对象字面量语法 ({foo: 'bar'}) 是创建通用对象的快捷方式。所以以这种方式创建的对象的Object。此外,以这种方式创建对象并不能让您有办法使 new 对象具有相同的行为(这意味着它们将属于同一类)。

在 C++ 和 Java 等某些语言中,类是该语言的基本构建块。但在 javascript 中,它只是一个概念,基本上(非正式地)意味着任何具有相同指定行为的对象。更正式地说,它是一个在其原型链中具有类原型的对象。什么是原型和原型链?好吧,每个* 对象(即使是使用对象字面量语法制作的对象)都有一个原型作为隐藏的** 属性。在对象中查找属性时,如果在对象本身中没有找到,则在原型中查找该属性。如果该原型对象没有该属性,它会“上链”并检查原型的原型的属性,并尽可能地继续这样做。

当您使用class 语法时,您正在为类创建的每个对象的原型上设置属性。因此,在您的示例中,您将函数 start 分配给每个 Clock 对象的原型。但是如果你将它分配给对象本身,那么它就不会使用原型。

有趣的是,类也是对象(它们可以有属性和方法)。

官方文档见这里:

*:您可以使用Object.create(null) 制作没有原型的对象,但这种情况很少见。这样做的好处是您不会意外访问原型的属性,因为没有。

**:您有时可以使用已弃用的 __poto__ 属性访问原型。首选的新方式是Object.getPrototypeOf(object)

以下是一些您可以探索的示例:

const clockByLiteral = { start(){/* do something */} }
function startClock () { /* do something */}
class Clock {
  start() { /* do something */}
}

const clock1 = new Clock()
const clock2 = new Clock()

function getClassName(object){
  return object.constructor.name
}
console.log('"class" of clockByLiteral:', getClassName(clockByLiteral))
console.log('"class" of clockByLiteral.start:', getClassName(clockByLiteral.start))
console.log('"class" of startClock:', getClassName(startClock))
console.log('"class" of Clock class:', getClassName(Clock))
console.log('"class" of clock1:', getClassName(clock1))
console.log('"class" of clock2:', getClassName(clock2))
console.log('is the constructor of clock1 Clock?:', clock1.constructor === Clock, "(meaning that classes and constructors are the same objects, and are functions also")
console.log('is the prototype of clock1 Clock.prototype?:', Object.getPrototypeOf(clock1) === Clock.prototype)


console.log('is clock1 the same object as clock2?:', clock1 === clock2)
console.log('is clock1.start the same as clock2.start?:', clock1.start === clock2.start)
console.log('is clock1.start the same as Clock.prototype.start?:', clock1.start === Clock.prototype.start)

console.log('is clock1 an "instance" of Clock?:', clock1 instanceof Clock)
console.log('is clock1 an "instance" of Object?:', clock1 instanceof Object)
console.log('is clock1 an "instance" of Function?:', clock1 instanceof Function)
console.log('is Clock an "instance" of Function?:', Clock instanceof Function)
console.log('is startClock an "instance" of Function?:', Clock instanceof Function)
console.log('is startClock an "instance" of Object?:', Clock instanceof Object)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-13
    • 1970-01-01
    • 2022-07-26
    • 2023-03-17
    • 1970-01-01
    • 2014-07-11
    • 1970-01-01
    相关资源
    最近更新 更多