简体   繁体   English

我们应该在 JavaScript 的类定义中声明构造函数内部还是外部的方法?

[英]Should we declare methods inside or outside the constructor in a class definition in JavaScript?

I am confused should I declare methods inside or outside the constructor method in a class definition?我很困惑我应该在类定义中声明构造方法内部还是外部的方法?

How does it help to declare to methods inside the constructor function in a class definition?在类定义的构造函数中声明方法有什么帮助?

What is the disadvantage of declaring methods outside the constructor function?在构造函数之外声明方法有什么缺点?

class animal {
    constructor(name, type, color){
        this.name = name;
        this.type = type;
        this.color = color;
        this.sound = () => {console.log(this.name,this.type,this.color)}
    }
    
}

class animal {
    constructor(name, type, color){
        this.name = name;
        this.type = type;
        this.color = color;
    }
    sound = () => {console.log(this.name,this.type,this.color)}
}

Creating objects in either way, results in the similar objects.以任何一种方式创建对象都会产生相似的对象。 What am I missing?我错过了什么?

The definition of functions inside the constructor was mostly used to store and access private variables in calls like this:构造函数内部的函数定义主要用于在调用中存储和访问私有变量,如下所示:

function MyClass() {
  let privateVar = { something: "usefull" };
  this.getPrivateVar = function getPrivateVar() { return privateVar; };
}
new MyClass().getPrivateVar(); // { something: "usefull" }

Just because calls produce a similar result, doesn't mean that they have an equal behavior.仅仅因为调用产生相似的结果,并不意味着它们具有相同的行为。 They can act completly differently depending on how they get called or how they where defined.根据调用方式或定义方式,它们的行为可能完全不同。

This counts especially for arrow functions as they don't have a this binding.这尤其适用于箭头函数,因为它们没有 this 绑定。 They access the closest this providing environment, when defined.当定义时,它们访问最接近的这个提供环境。

class MyClass {}
MyClass.prototype.arrow = () => this;
console.log(new MyClass().arrow() == globalThis); // true

The answers from How does JavaScript .prototype work? JavaScript .prototype 如何工作的答案? explain pretty well how prototype is working and for classes every property and function defined inside it, will be stored on constructor.prototype , with some exceptions like the static and #private properties.很好地解释原型是如何工作的,对于类,其中定义的每个属性和函数都将存储在constructor.prototype上,但有一些例外,例如静态和 #private 属性。

  • Private properties are only available and accessible inside the class and not in an inherited or an extended class and can't be accessed by a classic prototype.私有属性仅在类内部可用和可访问,而不是在继承或扩展类中,并且不能被经典原型访问。
     class MyClass { #private = "example"; doSomethingWithMyPrivate() { return this.#private } } MyClass.prototype.getThePrivate = function() { return this.#private }; new MyClass().doSomethingWithMyPrivate(); // example // Both produce the same SyntaxError: // Private field '#private' must be declared in an enclosing class new MyClass().#private; new MyClass().getThePrivate();
  • Static properties will be placed on the constructor directly.静态属性将直接放在构造函数上。
     class MyClass { static doSomething() { console.log("Okay!") } } MyClass.doSomething(); // Okay! new MyClass().doSomething(); // TypeError: doSomething is not a function

Every instance of a class will access the prototype references or the inheriting objects prototype until any object has a match with the property-name, if not it returns undefined or throw a more specific error depending on the situation.类的每个实例都将访问原型引用或继承对象原型,直到任何对象与属性名称匹配,如果不匹配,则返回未定义或根据情况抛出更具体的错误。

From this answer :这个答案

Javascript has a mechanism when looking up properties on Objects which is called 'prototypal inheritance', here is what is basically does: Javascript 在查找对象的属性时有一种称为“原型继承”的机制,基本上是这样的:

  • First is checked if the property is located on the Object itself.首先检查属性是否位于对象本身上。 If so this property is returned.如果是这样,则返回此属性。
  • If the property is not located on the object itself it will 'climb up the protochain'.如果该属性不在对象本身上,它将“爬上原型链”。 It basically looks at the object referred to by the proto property.它基本上着眼于由 proto 属性引用的对象。 There it checks if the property is available on the object referred to by proto它在那里检查该属性是否在 proto 引用的对象上可用
  • If the property isn't located on the proto object it will climb up the proto chain all the way up to Object object.如果属性不在 proto 对象上,它将沿着 proto 链一直向上爬到 Object 对象。
  • If it cannot find the property nowhere on the object and its prototype chain it will return undefined.如果它在对象及其原型链上找不到属性,它将返回未定义。

So if you define the functions inside the constructor, each instance gets a new function assigned (they won't have the same reference).因此,如果您在构造函数中定义函数,每个实例都会分配一个新函数(它们不会有相同的引用)。 Which means, depending on the class (new animal).sound == (new animal).sound , the statement will return true (your 2nd example) or false (your 1st example) and the prototypal inheritance will take place or not.这意味着,根据类(new animal).sound == (new animal).sound ,该语句将返回true (您的第二个示例)或false (您的第一个示例),并且原型继承将发生或不发生。

 // the old way function Foo() { if (this instanceof Foo) { this.fx = () => {}; } else throw new TypeError(`Constructor Foo cannot be invoked without 'new'`); } Foo.prototype.pfx = function() {}; console.log((new Foo).fx, (new Foo).fx == (new Foo).fx); // arrow ƒ, false console.log((new Foo).pfx, (new Foo).pfx == (new Foo).pfx); // ƒ, true // the class way class Bar { constructor() { // similar too "ƒ Foo() {}" with the addition of super ƒ this.fx = () => {}; } // this is almost like Bar.prototype.pfx = ... pfx() {} } // works like before Bar.prototype.moo = function moo() {}; console.log((new Bar).fx, (new Bar).fx == (new Bar).fx); // arrow ƒ, false console.log((new Bar).pfx, (new Bar).pfx == (new Bar).pfx); // ƒ, true console.log((new Bar).moo, (new Bar).moo == (new Bar).moo); // ƒ, true

If you define properties as mentioned in your example #1, you can "override" other methods and this may cause trouble with private properties as mentioned above.如果您在示例 #1 中定义属性,则可以“覆盖”其他方法,这可能会导致上面提到的私有属性出现问题。

 class Foo { constructor() { // just for the example this.fx = Foo.prototype.fx; } fx() { console.log("I am broken"); }; } class Bar extends Foo { fx() { console.log("I fix something"); } } class Moo extends Bar { constructor() { super(); delete this.fx; } } /* * If instance has no own property called fx the default * behavior will take place: * Test the construtor.prototype if it has one and execute this. * If not, look at the inheriting classes in descending order * (Moo > Bar > Foo > Object) * But bar.fx() => has an own property called fx which takes place */ let bar = new Bar(); bar.fx(); // I am broken // none of the inheriting classes has a toJSON ƒ, // so Object.prototype.toJSON will be called. console.log(bar.toJSON == Object.prototype.toJSON) // true // the Moo constructor deletes the own property after the Foo constructor was evaluated, so Bar.prototype.fx takes place new Moo().fx() // I fix something

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM