Mongoose中document与object的区别示例详解


Posted in Javascript onSeptember 18, 2017

前言

本文主要给大家总结介绍了关于Mongoose中document与object区别的相关内容,分享出来供大家参考学习,其实这个问题其实是mongoose非常常见的问题,经常有很多以前没遇到这个问题的人都会被这个问题弄得怀疑人生。

我们先介绍一些问题的背景。

先看下面一段代码:

router.get('/', function(req, res, next) {
 // res.render('index', { title: 'Express' });
 const model = mongoose.model('realestate');
 const queryCretia = {};
 model.find(queryCretia, (err, docs) => {
 res.render('index', {
 title: 'express',
 docs: docs
 })
 })
});
<!DOCTYPE html>
<html>
 <head>
 <title><%= title %></title>
 <link rel='stylesheet' href='/stylesheets/style.css' />
 </head>
 <body>
 <h1><%= title %></h1>
 <p>Welcome to <%= title %></p>
 <!-- <%= docs %> -->
 <ul>
 <% docs.forEach(function(doc){ %>
 <li><%= doc.type %></li>
 <% }) %>
 </ul>
 </body>
</html>

在第一段代码中,通过model.find我们应该能够获取到根据queryCriteria获取的结果,结果应该是一个对象数组,类似于这样:

[{
 "_id" : ObjectId("59bdeadb2a5c612514ee7970"),
 "title" : "好楼层,中等装修,满5年,上门实拍",
 "type" : "2室1厅",
 "square" : "75.42平",
 "direction" : "朝南",
 "floor" : "中区/6层",
 "unitPrice" : 47732,
 "totalPrice" : 360,
 "location" : null,
 "specialExplain" : "满五",
 "url" : "http://sh.lianjia.com//ershoufang/sh4528035.html",
 "station" : "江杨北路",
 "line" : "3号线",
 "updateTime" : "2017-09-17 11:24:11"
}
{
 "_id" : ObjectId("59bdeadb2a5c612514ee7971"),
 "title" : "南北户型,厨卫全明,高区采光好,装修精美",
 "type" : "2室2厅",
 "square" : "90.92平",
 "direction" : "朝南北",
 "floor" : "高区/6层",
 "unitPrice" : 46194,
 "totalPrice" : 420,
 "location" : null,
 "specialExplain" : "满五",
 "url" : "http://sh.lianjia.com//ershoufang/sh4546221.html",
 "station" : "江杨北路",
 "line" : "3号线",
 "updateTime" : "2017-09-17 11:24:11"
}]

预期index.ejs应该渲染的页面是一个ul渲染的结果,类似于

  • 2室1厅
  • 2室2厅

当然,理想很丰满,现实很骨感。我就是死活渲染不出来doc.type。照理说应该是不可能的,在index.ejs中doc就是一个对象,我为什么不能获取doc的type属性呢?这不合理,太不合理了!

老实说,这个问题我之间也是遇到过,当初我是想修改这个doc的属性,但是死活没有办法修改,当初也是花了很久找到原因。这次我就把这个问题好好地研究一下。

先说结果,以及解决方法把。我比较喜欢剧透。愿意是因为再次返回的doc是属于Document的实例,而不是一个普通的对象。也就是说它和普通的对象是不一样的,它没有普通对象的一些方法,普通对象也没有它身上的一些方法。

解决方案有几种,不过究其根本都是将这种document转化为普通的对象:

方法1:

利用toObject方法

docs.forEach(doc => {
 return doc.toObject();
})

方法2:

利用JSON方法,这是我想到的一个方法,具体深层原因在这就不展开了:

docs = JSON.stringify(docs);
docs = JSON.parse(docs);

方法3:

利用lean方法:

model.find().lean().exec((err, docs) => {
....
})

上述的三种方法应该都能成功将find获取的结果转化为普通的对象。

但是我还想知道到底document和一个普通的对象到底有什么区别,区别在哪里呢?

我们假设find获取的结果是docs,转化为普通对象的结果是docs1。现在我们就看一看这二者的区别。理论上docs和docs1都应该是数组,而它们中元素都应该是一个对象,我们先来看看是不是这样呢?

console.log(Object.prototype.toString.call(docs));
consoele.log(Object.prototype.toString.call(docs[0]));

console.log(Object.prototype.toString.call(docs1));
console.log(Object.prototype.toString.call(docs1[0]))

我们通过上述方法可以获取docs以及docs1的类型以及其中元素的类型,结果是:

[object Array]
[object Object]

[object Array]
[object Object]

和我们预想中的一模一样,那问题不在这,那我们就探究探究docs[0]以及docs1[0]的原型把,看看它的原型到底是什么呢?知道JS的人,应该都知道JS中的原型链。在此,我们就通过__proto__来粗暴地获取对象的原型:

console.dir(doc[0].__proto__);

console.dir(docs[0].__proto__);

结果是:

model {
 db:
 NativeConnection {
  base:
  Mongoose {
  connections: [Array],
  models: [Object],
  modelSchemas: [Object],
  options: [Object],
  plugins: [Array] },
  collections: { realestates: [Object] },
  models: { realestate: [Object] },
  config: { autoIndex: true },
  replica: false,
  hosts: null,
  host: '127.0.0.1',
  port: 27017,
  user: undefined,
  pass: undefined,
  name: 'real_estate_info',
  options:
  { db: [Object],
  auth: {},
  server: [Object],
  replset: [Object],
  mongos: undefined },
  otherDbs: [],
  _readyState: 1,
  _closeCalled: false,
  _hasOpened: true,
  _listening: false,
  db:
  Db {
  domain: null,
  _events: [Object],
  _eventsCount: 6,
  _maxListeners: undefined,
  s: [Object],
  serverConfig: [Getter],
  bufferMaxEntries: [Getter],
  databaseName: [Getter],
  _listening: true },
  _events:
  { connected: [Function],
  error: [Function: bound bound consoleCall],
  disconnected: [Function: bound bound consoleCall],
  reconnected: [Function: bound bound consoleCall] },
  _eventsCount: 4 },
 discriminators: undefined,
 id: [Getter/Setter],
 __v: [Getter/Setter],
 _id: [Getter/Setter],
 schema:
 Schema {
  obj: undefined,
  paths: { _id: [Object], __v: [Object] },
  aliases: {},
  subpaths: {},
  virtuals: { id: [Object] },
  singleNestedPaths: {},
  nested: {},
  inherits: {},
  callQueue: [ [Array], [Array], [Array], [Array], [Array], [Array] ],
  _indexes: [],
  methods: {},
  statics: {},
  tree: { _id: [Object], __v: [Function: Number], id: [Object] },
  query: {},
  childSchemas: [],
  plugins: [ [Object], [Object], [Object], [Object] ],
  s: { hooks: [Object], kareemHooks: [Object] },
  options:
  { retainKeyOrder: false,
  typeKey: 'type',
  id: true,
  noVirtualId: false,
  _id: true,
  noId: false,
  validateBeforeSave: true,
  read: null,
  shardKey: null,
  autoIndex: null,
  minimize: true,
  discriminatorKey: '__t',
  versionKey: '__v',
  capped: false,
  bufferCommands: true,
  strict: true,
  pluralization: true },
  '$globalPluginsApplied': true,
  _requiredpaths: [] },
 collection:
 NativeCollection {
  collection: Collection { s: [Object] },
  opts: { bufferCommands: true, capped: false },
  name: 'realestates',
  collectionName: 'realestates',
  conn:
  NativeConnection {
  base: [Object],
  collections: [Object],
  models: [Object],
  config: [Object],
  replica: false,
  hosts: null,
  host: '127.0.0.1',
  port: 27017,
  user: undefined,
  pass: undefined,
  name: 'real_estate_info',
  options: [Object],
  otherDbs: [],
  _readyState: 1,
  _closeCalled: false,
  _hasOpened: true,
  _listening: false,
  db: [Object],
  _events: [Object],
  _eventsCount: 4 },
  queue: [],
  buffer: false,
  emitter:
  EventEmitter {
  domain: null,
  _events: {},
  _eventsCount: 0,
  _maxListeners: undefined } },
 '$__original_save': { [Function] numAsyncPres: 0 },
 save: { [Function: wrappedPointCut] '$originalFunction': '$__original_save', '$isWrapped': true },
 _pres:
 { '$__original_save': [ [Object], [Object], [Object] ],
  '$__original_remove': [ [Object] ] },
 _posts: { '$__original_save': [], '$__original_remove': [] },
 '$__original_remove': { [Function] numAsyncPres: 1 },
 remove:
 { [Function: wrappedPointCut]
  '$originalFunction': '$__original_remove',
  '$isWrapped': true },
 '$__original_validate': [Function],
 validate:
 { [Function: wrappedPointCut]
  '$originalFunction': '$__original_validate',
  '$isWrapped': true } }

以及

{}

很显然,问题就是在这里,docs[0]和docs[0]的原型并不是一个东西。而js中对象通过.或者是[]访问属性都是调用了Object中的某个方法,但具体什么方法我不太记得。然而docs中的原型或者其原型的原型也是没有这个方法的,因此他就没办法去访问这个属性。

其实docs[0].__proto__.__proto__是Model,docs[0].__proto__.__proto__.__proto__是Document,docs[0].__proto__.__proto__.__proto__.__proto__才是{}。

至此,这个问题引起的一系列的探究也是告一段落了。其实Mongoose还有另外一些奇怪的地方,被人所诟病,在此也不一一细数了。从问题的发现,到写这篇文章大概花了大半天的时间,以前遇到问题就找到解决办法就停止了,但是这一次通过这样深入地去发现,可能就会发掘到更多的东西。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
Javascript 表单之间的数据传递代码
Dec 04 Javascript
javascript getElementsByClassName 和js取地址栏参数
Jan 02 Javascript
js中的push和join方法使用介绍
Oct 08 Javascript
Javascript保存网页为图片借助于html2canvas库实现
Sep 05 Javascript
node.js中的path.resolve方法使用说明
Dec 08 Javascript
AngularJS实现的select二级联动下拉菜单功能示例
Oct 25 Javascript
详解从Vue-router到html5的pushState
Jul 21 Javascript
koa2的中间件功能及应用示例
Mar 05 Javascript
JS 设计模式之:单例模式定义与实现方法浅析
May 06 Javascript
vue实例的选项总结
Jun 09 Javascript
如何配置vue.config.js 处理static文件夹下的静态文件
Jun 19 Javascript
JS获取一个字符串中指定字符串第n次出现的位置
Feb 10 Javascript
新手vue构建单页面应用实例代码
Sep 18 #Javascript
angularjs路由传值$routeParams详解
Sep 05 #Javascript
vue-ajax小封装实例
Sep 18 #Javascript
信息滚动效果的实例讲解
Sep 18 #Javascript
彻底搞懂JavaScript中的apply和call方法(必看)
Sep 18 #Javascript
angularjs实现猜数字大小功能
May 20 #Javascript
angular指令笔记ng-options的使用方法
Sep 18 #Javascript
You might like
很温暖很温暖的Lester Young
2021/03/03 冲泡冲煮
关于BIG5-HKSCS的解决方法
2007/03/20 PHP
PHP __autoload()方法真的影响性能吗?
2012/03/30 PHP
ThinkPHP采用原生query实现关联查询left join实例
2014/12/02 PHP
javascript实现行拖动的方法
2015/05/27 Javascript
JS实现文档加载完成后执行代码
2015/07/09 Javascript
js实现精确到秒的倒计时效果
2016/05/29 Javascript
JS实现弹出居中的模式窗口示例
2016/06/20 Javascript
JavaScript ES5标准中新增的Array方法
2016/06/28 Javascript
Google 地图类型详解及示例代码
2016/08/06 Javascript
Bootstrap显示与隐藏简单实现代码
2017/03/06 Javascript
angular或者js怎么确定选中ul中的哪几个li
2017/08/16 Javascript
vue与bootstrap实现时间选择器的示例代码
2017/08/26 Javascript
解决vue中修改export default中脚本报一大堆错的问题
2018/08/27 Javascript
初学node.js中实现删除用户路由
2019/05/27 Javascript
[19:14]DOTA2 HEROS教学视频教你分分钟做大人-维萨吉
2014/06/24 DOTA
Python协程的用法和例子详解
2017/09/09 Python
python numpy 部分排序 寻找最大的前几个数的方法
2018/06/27 Python
python求最大值最小值方法总结
2019/06/25 Python
python图形绘制奥运五环实例讲解
2019/09/14 Python
Python面向对象原理与基础语法详解
2020/01/02 Python
Pytorch: 自定义网络层实例
2020/01/07 Python
Python爬虫+tkinter界面实现历史天气查询的思路详解
2021/02/22 Python
Canvas绘制浮动球效果的示例
2017/12/29 HTML / CSS
三星加拿大官方网上商店:Samsung CA
2020/12/18 全球购物
医科大学生毕业的自我评价分享
2013/11/12 职场文书
初中生自我评价
2014/02/01 职场文书
学生自我评价范文
2014/02/02 职场文书
行政管理毕业生自荐信
2014/02/24 职场文书
舞蹈毕业生的自我评价
2014/03/05 职场文书
市场营销调查计划书
2014/05/02 职场文书
2014年扫黄打非工作总结
2014/12/03 职场文书
自考生自我评价
2019/06/21 职场文书
有关信念的名言语录集锦
2019/12/06 职场文书
Nginx服务器添加Systemd自定义服务过程解析
2021/03/31 Servers
JavaScript中的宏任务和微任务详情
2021/11/27 Javascript