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关闭当前页面(窗口)不带任何提示
Mar 26 Javascript
setinterval()与clearInterval()JS函数的调用方法
Jan 21 Javascript
javascript实现详细时间提醒信息效果的方法
Mar 11 Javascript
JS动态修改表格cellPadding和cellSpacing的方法
Mar 31 Javascript
js实现适用于素材网站的黑色多级菜单导航条效果
Aug 24 Javascript
javascript HTML5 canvas实现打砖块游戏
Jun 18 Javascript
JS实现图文并茂的tab选项卡效果示例【附demo源码下载】
Sep 21 Javascript
jsp 网站引入外部css或者js失效问题解决
Oct 31 Javascript
JS定时检测任务任务完成后执行下一步的解决办法
Dec 22 Javascript
Cropper.js 实现裁剪图片并上传(PC端)
Aug 20 Javascript
微信小程序实现底部弹出框
Nov 18 Javascript
JS指定音频audio在某个时间点进行播放
Nov 28 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
php传值赋值和传地址赋值用法实例分析
2015/06/20 PHP
php单链表实现代码分享
2016/07/04 PHP
jQuery EasyUI API 中文文档 - Parser 解析器
2011/09/29 Javascript
asp.net刷新本页面的六种方法总结
2014/01/07 Javascript
详解Javascript 装载和执行
2014/11/17 Javascript
JavaScript判断IE版本型号
2015/07/27 Javascript
jquery事件绑定解绑机制源码解析
2016/09/19 Javascript
详解使用nodeJs安装Vue-cli
2017/05/17 NodeJs
深入理解Angularjs中$http.post与$.post
2017/05/19 Javascript
Nodejs异步回调之异常处理实例分析
2018/06/22 NodeJs
JavaScript 中 JSON.parse 函数 和 JSON.stringify 函数
2018/12/05 Javascript
axios 实现post请求时把对象obj数据转为formdata
2019/10/31 Javascript
Vue常用传值方式、父传子、子传父及非父子实例分析
2020/02/24 Javascript
[12:29]《一刀刀一天》之DOTA全时刻19:蝙蝠骑士田伯光再度不举
2014/06/10 DOTA
[51:00]Secret vs VGJ.S 2018国际邀请赛淘汰赛BO3 第一场 8.24
2018/08/25 DOTA
跟老齐学Python之再深点,更懂list
2014/09/20 Python
Python中方法链的使用方法
2016/02/23 Python
python win32 简单操作方法
2017/05/25 Python
对Python中的@classmethod用法详解
2018/04/21 Python
django 解决manage.py migrate无效的问题
2018/05/27 Python
Python定义函数功能与用法实例详解
2019/04/08 Python
Pandas中DataFrame的分组/分割/合并的实现
2019/07/16 Python
PyQT5 emit 和 connect的用法详解
2019/12/13 Python
基于MSELoss()与CrossEntropyLoss()的区别详解
2020/01/02 Python
python sitk.show()与imageJ结合使用常见的问题
2020/04/20 Python
python批量提取图片信息并保存的实现
2021/02/05 Python
细说CSS3中的选择符
2008/10/17 HTML / CSS
纯CSS3制作漂亮带动画效果的主机价格表
2015/04/25 HTML / CSS
美国老牌主机服务商:iPage
2016/07/22 全球购物
斯凯奇澳大利亚官网:SKECHERS澳大利亚
2018/03/31 全球购物
薇姿法国官网:Vichy法国
2021/01/28 全球购物
中英文自我评价语句
2013/12/20 职场文书
集体婚礼证婚词
2014/01/13 职场文书
装修施工安全责任书
2014/07/24 职场文书
2016拓展训练心得体会范文
2016/01/12 职场文书
MySQL实现用逗号进行拼接、以逗号进行分割
2022/12/24 MySQL