Mongoose的“静态”方法和“实例”方法

83

我认为这个问题类似于这个问题,但术语不同。来自Mongoose 4 文档

我们还可以定义自己的自定义文档实例方法。

// define a schema
var animalSchema = new Schema({ name: String, type: String });

// assign a function to the "methods" object of our animalSchema
animalSchema.methods.findSimilarTypes = function (cb) {
  return this.model('Animal').find({ type: this.type }, cb);
}

现在我们的所有动物实例都可以使用 findSimilarTypes 方法。

接下来:

将静态方法添加到模型中也很简单。继续使用我们的 animalSchema:

// assign a function to the "statics" object of our animalSchema
animalSchema.statics.findByName = function (name, cb) {
  return this.find({ name: new RegExp(name, 'i') }, cb);
}

var Animal = mongoose.model('Animal', animalSchema);
Animal.findByName('fido', function (err, animals) {
  console.log(animals);
});

似乎使用静态方法后,每个动物实例都可以使用findByName方法。在模式中,staticsmethods对象是什么?它们之间有何区别?为什么我会选择其中之一?

7个回答

115

statics是在模型上定义的方法。methods是在文档(实例)上定义的。

您可以像使用Animal.findByName这样使用静态方法:

Translated text:

statics是在模型上定义的方法。methods是在文档(实例)上定义的。

您可能会使用像Animal.findByName这样的静态方法:

const fido = await Animal.findByName('fido');
// fido => { name: 'fido', type: 'dog' }

你可以使用实例方法,例如fido.findSimilarTypes

const dogs = await fido.findSimilarTypes();
// dogs => [ {name:'fido',type:'dog} , {name:'sheeba',type:'dog'} ]

但是你不会使用Animals.findSimilarTypes(),因为Animals是一个模型,它没有"类型"这个属性。 findSimilarTypes需要this.type,而在Animals模型中这个属性不存在,只有一个文档实例才包含该属性,就像在模型中定义的那样。

同样地,你也不会使用fido.findByName,因为findByName需要搜索所有文档,而fido只是一个文档。

¹好吧,从技术上讲,你可以这样做,因为实例确实可以访问集合(this.constructor或者this.model('Animal')),但是这没有意义(至少在这种情况下)拥有一个实例方法却不使用实例的任何属性。(感谢@AaronDufour指出这一点)


2
@laggingreflex 您的第二个评论是不正确的。您可以拥有fido.findByName,因为fido通过this.model('Animal')可以访问整个集合。但是,如果一个实例方法不使用实例的任何属性,那么这样做就没有太多意义。 - Aaron Dufour
@lagginreflex,你是不是写了mongoosejs.com上使用“static”这个词来免费获取堆栈点的文档?哈哈,实际上它展示了@Startec的示例,即Animal.statics.methodName,但是你作为我迄今为止找到的少数资源之一,却说不要使用“statics”这个词,例如Animals.methodName,但是在文档中它也显示了。感谢您的指导,我也怀疑这一点。 - blamb

4

数据库逻辑应该封装在数据模型内部。Mongoose提供了两种方法来实现这一点,即methods和statics。Methods将一个实例方法添加到文档中,而Statics将静态“类”方法添加到模型本身中。关键字static为模型定义了一个静态方法。静态方法不是在模型实例上调用的,而是在模型本身上调用的。这些通常是一些实用函数,比如用于创建或复制对象的函数。例如:

const bookSchema = mongoose.Schema({
  title: {
    type : String,
    required : [true, 'Book name required']
  },
  publisher : {
    type : String,
    required : [true, 'Publisher name required']
  },
  thumbnail : {
    type : String
  }
  type : {
    type : String
  },
  hasAward : {
    type : Boolean
  }
});

//method
bookSchema.methods.findByType = function (callback) {
  return this.model('Book').find({ type: this.type }, callback);
};

// statics
bookSchema.statics.findBooksWithAward = function (callback) {
  Book.find({ hasAward: true }, callback);
};

const Book = mongoose.model('Book', bookSchema);
export default Book;

更多信息请查看:https://osmangoni.info/posts/separating-methods-schema-statics-mongoose/


3

大家都说,在操作单个文档时使用方法,而在操作整个集合时使用静态方法。但为什么呢?

假设我们有一个模式:

var AnimalSchema = new Schema({
  name: String,
  type: String
});

现在根据文档所述,如果您需要检查数据库中特定文档的相似类型,可以执行以下操作:

if(条件){
//执行代码
}

AnimalSchema.methods.findSimilarType = function findSimilarType (cb) {
    return this.model('Animal').find({ type: this.type }, cb);
};

现在,这里的“this”是指文档本身。因此,这意味着,这个文档不知道它属于哪个模型,因为方法与模型默认没有任何关系,它只适用于特定的文档对象。 但是我们可以使用this.model('anyModelName')使其与模型一起工作。
现在,在寻找类似类型的动物时,为什么要使用方法?
为了找到类似类型的动物,我们必须有一个动物对象,我们将为其找到类似类型的动物。我们有这个动物对象,比如说:
const Lion = await new Animal({name: Lion, type: "dangerous"});

接下来,当我们找到相似类型时,我们需要先有Lion对象,我们必须拥有它。 因此,每当我们需要某个对象/文档的帮助来完成某事时,我们将使用方法。
现在,碰巧我们也需要整个模型来搜索相似类型,尽管它在方法中不是直接可用的(记住this.modelName将返回未定义)。我们可以通过将this.model()设置为我们喜欢的模型(在这种情况下是Animal)来获取它。 关于方法就介绍到这里。
现在,静态方法可以随意使用整个模型。 1) 假设模型具有价格字段,您想计算所有动物的总价格,您将使用静态方法[对于此操作您不需要任何特定的动物对象,因此我们不会使用方法] 2) 假设模型具有皮肤字段,您想查找黄色皮肤的动物,您将使用静态方法。[对于此操作,我们不需要任何特定的动物对象,因此我们不会使用方法]
例如:
 AnimalSchema.statics.findYellowSkin = function findSimilarType (cb) {
        return this.find({ skin: "yellow" }, cb);
    };

记住,在方法中this指的是模型,所以在我们的例子中,this.modelName将返回Animal

但是就像方法一样,在这里我们可以(但不需要)使用以下方式设置模型。

AnimalSchema.methods.findSimilarType = function findSimilarType (cb) {
    return this.model('Animal').find({ skin: "yellow" }, cb);   //just like in methods
};

如您所见,静态方法和实例方法非常相似。

当您需要使用文档并需要执行某些操作时,请使用实例方法。当您需要对整个模型/集合执行操作时,请使用静态方法。


2
对我来说,在“静态”关键字前面添加 Mongoose 甚至在“实例”关键字前面添加任何内容都不意味着添加任何东西。
我相信“静态”的含义和目的在任何地方都是相同的,即使对于一种表示模型以构建类似另一种面向对象编程语言的块的某种驱动程序或外星语言也是如此。同样适用于实例。
从维基百科上可以看到:“面向对象编程(OOP)中的方法是与消息和对象相关联的过程。对象由数据和行为组成。数据和行为组成了一个接口,指定了对象如何被各种消费者[1]使用。”
数据表示为对象的属性,行为表示为对象的方法。例如,Window对象可以有诸如打开和关闭之类的方法,而其状态(在任何给定时间是否已打开或关闭)将是属性。
静态方法是指与类的所有实例相关而不是特定实例的方法。在这方面,它们类似于静态变量。例如,一个用于计算类的每个实例的所有变量值之和的静态方法。例如,如果有一个产品类,它可能会有一个静态方法来计算所有产品的平均价格。
Math.max(double a, double b)
这个静态方法没有拥有对象,也不在实例上运行。它从其参数中接收所有信息。
即使类的实例不存在,也可以调用静态方法。静态方法被称为“静态”,因为它们在编译时基于调用它们的类进行解析,而不像实例方法那样根据对象的运行时类型进行多态解析。

https://en.wikipedia.org/wiki/Method_(computer_programming)


1

静态方法适用于定义它们的整个模型,而实例方法仅适用于集合中的特定文档。

在静态方法上下文中,this返回整个模型,而在实例方法上下文中,this返回文档。

比如说:

const pokemon = new mongoose.Schema({})
pokemon.statics.getAllWithType = function(type){
      // Query the entire model and look for pokemon
      // with the same type
      // this.find({type : type})
}

pokemon.methods.sayName = function(){
      // Say the name of a specific pokemon
      // console.log(this.name)
}


const pokemonModel = mongoose.model('schema', pokemon)
const squirtle = new pokemonModel({name : "squirtle"})

// Get all water type pokemon from the model [static method]
pokemonModel.getAllWithType("water")

// Make squirtle say its name [instance method] 
squirtle.sayName()

0

staticsmethods 差不多,但允许定义直接存在于您的模型上的函数。

statics 属于模型,而 methods 属于实例


0
使用 .statics 来定义静态方法。 使用 .methods 来定义实例方法。
//instance method
bookSchema.methods.findByType = function (callback) {
  return this.model('Book').find({ type: this.type }, callback);
};

// static method
bookSchema.statics.findBooksWithAward = function (callback) {
  Book.find({ hasAward: true }, callback);
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接