传统的JS函数是对象的一部分吗?

huangapple go评论69阅读模式
英文:

Are the conventional JS Functions part of an object or not?

问题

当我打印对象时,我没有得到传统风格的函数,只有箭头风格的函数。传统函数是创建的对象的隐藏部分,还是它们不属于类对象?

class Person {
    pName = "Hasnain";
    pAge = 22;

    displayNormalFunc1()
    {
        return this;
    }
    displayNormalFunc2()
    {
        return this;
    }
    displayArrowFunc1 = ()=>
    {
        return this;
    }
    displayArrowFunc2 = ()=>
    {
        return this;
    }
}

objp = new Person();
console.log(objp);
英文:

When I printed the object I did't get the conventional style functions but only the arrow style functions. Are the conventional functions hidden part of the created object or they do not belong to the class object?

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

class Person {
    pName = &quot;Hasnain&quot;;
    pAge = 22;

    displayNormalFunc1()
    {
        return this;
    }
    displayNormalFunc2()
    {
        return this;

    }
    displayArrowFunc1 = ()=&gt;
    {
        return this;
    }
    displayArrowFunc2 = ()=&gt;
    {
        return this;
    }
}

objp = new Person();
console.log(objp)

<!-- end snippet -->

答案1

得分: 1

displayArrowFunc1, displayArrowFunc2 像一个变量,它们在每次 Person 初始化时都会被初始化。

displayNormalFunc1, displayNormalFunc2 是属于 prototype 的方法,这是原型编程语言的设计原则。它们在声明 Person 时只会被创建一次,然后直接写入 Person.prototype。虽然可以通过一些 JavaScript API 访问它们,但这不在本问题的讨论范围内。

更多信息:

示例代码:

const p1 = new Person()
const p2 = new Person()

console.log(p1.displayNormalFunc1 === p2.displayNormalFunc1) // true
console.log(p1.displayArrowFunc1 === p2.displayArrowFunc1) // false

以及

class Foo {
  pLog() {
    console.log('p a')
  }
  log = () => console.log('a')
}

const bar = new Foo()

bar.pLog() // 'p a'
Foo.prototype.pLog = () => console.log('p b')
bar.pLog() // 'p b'

bar.log() // 'a'
Foo.prototype.log = () => console.log('b')
bar.log() // 'a'
英文:

displayArrowFunc1, displayArrowFunc2 is like a variable it will be initialized every time Person is initialized
displayNormalFunc1, displayNormalFunc2 are methods that belong to prototype which is a design principle of prototypical programming languages ​​they are created only once when declaring Person and directly written to your Person.prototype it is possible to access them through some javscript APIs but it is not in this question

More info:

See example:

const p1 = new Person()
const p2 = new Person()

console.log(p1.displayNormalFunc1 === p2.displayNormalFunc1) // true
consele.log(p1.displayArrowFunc1 === p2.displayArrowFunc1) // false

and

class Foo {
  pLog() {
    console.log(&#39;p a&#39;)
  }
  log = () =&gt; console.log(&#39;a&#39;)
}

const bar = new Foo()

bar.pLog() // &#39;p a&#39;
Foo.prototype.pLog = () =&gt; console.log(&#39;p b&#39;)
bar.pLog() // &#39;p b&#39;

bar.log() // &#39;a&#39;
Foo.prototype.log = () =&gt; console.log(&#39;b&#39;)
bar.log() // &#39;a&#39;

答案2

得分: 0

这实际上与函数是否是箭头函数无关。我可以以相同的方式重新定义displayArrowFunc属性,但将其定义为"常规"函数表达式,结果是相同的:

class Person {
    pName = "Hasnain";
    pAge = 22;

    displayNormalFunc1()
    {
        return this;
    }
    displayNormalFunc2()
    {
        return this;
    }
    displayArrowFunc1 = function()
    {
        return this;
    }
    displayArrowFunc2 = function()
    {
        return this;
    }
}

objp = new Person();
console.log(objp)

// 请查看答案中的注释
console.log(Person.prototype);
console.log(Person.__proto__);
console.log(Person.__proto__ === Function.prototype);

结果实际上取决于JavaScript对象和"类"的一些不同方面。我将尽量不深入讨论这些问题,但会简要总结一些关键事实,并提供相关链接。

JavaScript 为对象(而不是"类")提供了对象的"继承"版本,这与Java和C#等语言中的继承方式有很大不同。基本上,每个对象都链接到一个"原型对象",用于查找属性(包括函数属性或"方法"),这些属性在原始对象上不存在。

当您使用console.log输出一个对象时,您只会看到那些"直接存在"于该对象上的属性,而不会看到存在于其原型或原型的原型等上的属性。尽管访问原型链中的这些属性仍然有效。

这就是您观察到的根本原因,原始示例中的"箭头样式函数"(以及我修改的示例中的非箭头函数)都是类实例objp的直接属性,但其他函数不是。

"普通函数"不是因为这是JavaScript中"类"的工作方式。正如我尝试通过引号的使用来暗示的,JS实际上并没有"类",它们只是常规的JavaScript函数的语法糖。JS有一个功能,即任何函数都可以使用new运算符进行调用,然后构造一个新对象,无论函数体本身实际上做了什么。在ES6引入class关键字之前,这是人们在JS中使用"类"的方式。

要将"方法"添加到这样的"类"中,可以这样做:

function SomeClass(a) {
  this.a = a;
}

SomeClass.prototype.someMethod = function() {
  // 在这里执行某些操作
}

请注意,该方法实际上是对象SomeClass.prototype的属性,然后(由于JS内部的工作方式)它成为通过const someInstance = new SomeClass(2);构造的任何实例的"原型对象"(如上所述的意义)。

这正是您的"类"代码被转换成的内容 - 它只是一种语法糖。这就是为什么displayNormalFunc等未记录 - 它们不在实际实例对象上,而在其原型上。

至于为什么记录了displayArrowFunc1等内容,那是因为您在类内部以不同的方式定义了这些内容 - 这是一种比"类"本身更新的JS特性。这些在类主体内部使用someProperty = something的方式称为class fields。请注意我在链接文档中引用的这句话:

公共实例字段存在于类的每个创建实例中。

总之,这就是为什么它们被记录的原因 - 因为它们存在于实例上,而不是其原型上。这不仅适用于像pNamepAge这样的"常规"值,还适用于您以这种方式定义的函数/方法 - 在JavaScript中,函数只是像其他任何值一样。这也是为什么我说这与您是否将这些函数表达式定义为箭头函数无关 - 它与您用于将它们添加到类中的语法有关。

简而言之,在类主体内部的someProperty = someValue将属性直接放在每个构造的实例上,即使someValue是一个函数。而"标准"方法定义是一种特殊的语法,它最终添加到所有这些实例的原型上 - 因此,当实例被记录时,它们不会出现。

英文:

This actually is nothing to do with whether the functions are arrow functions or not. I can rewrite your example with the displayArrowFunc properties defined in the same way but as "regular" function expressions, and the result is the same:

<!-- begin snippet: js hide: false console: true babel: null -->

<!-- language: lang-js -->

class Person {
    pName = &quot;Hasnain&quot;;
    pAge = 22;

    displayNormalFunc1()
    {
        return this;
    }
    displayNormalFunc2()
    {
        return this;

    }
    displayArrowFunc1 = function()
    {
        return this;
    }
    displayArrowFunc2 = function()
    {
        return this;
    }
}

objp = new Person();
console.log(objp)

// see comments on answer
console.log(Person.prototype);
console.log(Person.__proto__);
console.log(Person.__proto__ === Function.prototype);

<!-- end snippet -->

The result is actually down to a few different things about Javascript objects and "classes". I'll try not to get too deep into this but give a quick summary of the key facts, with links.

Javascript has a version of "inheritance" for objects - for objects directly, not "classes" - which is quite different from that found in languages like Java and C#. Basically, each object is linked to a "prototype object" which is used to look up properties (including function properties, or "methods") that don't exist on the original object.

When you console.log an object, you'll only be shown the properties that "directly exist" on that object - not those that exist on its prototype, or its prototype's prototype and so on. Even though accessing those properties that are in the prototype chain will still work.

That's the root of what you observe - it turns out the "arrow style functions" in your original example (as well as the non-arrow ones in my modified example) are direct properties of the class instance objp, but the other ones are not.

The "normal functions" are not because this is how Javascript "classes" work. As I've tried to imply by the use of quotation marks, JS doesn't really have "clases" - they're syntactic sugar for a regular Javascript function. JS has the feature that any function can be called using the new operator, which will then construct a new object, whatever the function body itself actually does. This used to be the way, before ES6 introduced the class keyword (in around 2014/5), that people used "classes" in JS.

And the way to add "methods" to such a "class" would be like this:

function SomeClass(a) {
  this.a = a;
}

SomeClass.prototype.someMethod = function() {
  // do something here
}

Notice how the method is actually a property of the object SomeClass.prototype - which then (due to how JS works internally) becomes the "prototype object" (in the sense mentioned above) of any instance you construct via const someInstance = new SomeClass(2);.

And that's exactly what your "class" code gets transformed into - it's just a syntactic sugar. This is why displayNormalFunc and so on aren't logged - they're not on the actual instance object, but on its prototype.

As for why displayArrowFunc1 and friends are logged, that's because you've defined these in a different way inside your class - a way that is a more recent JS feature than "classes" themselves. These, where you put someProperty = something inside the class body, are known as class fields. Notice this sentence in the docs I linked to:

> Public instance fields exist on every created instance of a class.

So in short, that's why they are logged - because they're on the instance, not its prorotype. This applies not only to "regular" values like your pName and pAge, but also the functions/methods you defined this way - functions in Javascript are just values like any other. And this is why as I said it's nothing to do with whether you defined those function expressions as arrow functions or not - it's the syntax you use to add them to the class.

In short, someProperty = someValue inside the class body puts the property directly on each constructed instance, including when someValue is a function. Whereas "standard" method definitions are a special syntax and they end up added to the prototype of all such instances - therefore they don't appear when an instance is logged.

huangapple
  • 本文由 发表于 2023年2月19日 19:39:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/75499863.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定