{"action":"opened","issue":{"url":"https://api.github.com/repos/yinguangyao/underscore/issues/2","repository_url":"https://api.github.com/repos/yinguangyao/underscore","labels_url":"https://api.github.com/repos/yinguangyao/underscore/issues/2/labels{/name}","comments_url":"https://api.github.com/repos/yinguangyao/underscore/issues/2/comments","events_url":"https://api.github.com/repos/yinguangyao/underscore/issues/2/events","html_url":"https://github.com/yinguangyao/underscore/issues/2","id":305564309,"number":2,"title":"underscore源码剖析之整体架构","user":{"login":"yinguangyao","id":16210045,"avatar_url":"https://avatars0.githubusercontent.com/u/16210045?v=4","gravatar_id":"","url":"https://api.github.com/users/yinguangyao","html_url":"https://github.com/yinguangyao","followers_url":"https://api.github.com/users/yinguangyao/followers","following_url":"https://api.github.com/users/yinguangyao/following{/other_user}","gists_url":"https://api.github.com/users/yinguangyao/gists{/gist_id}","starred_url":"https://api.github.com/users/yinguangyao/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yinguangyao/subscriptions","organizations_url":"https://api.github.com/users/yinguangyao/orgs","repos_url":"https://api.github.com/users/yinguangyao/repos","events_url":"https://api.github.com/users/yinguangyao/events{/privacy}","received_events_url":"https://api.github.com/users/yinguangyao/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-03-15T14:00:45Z","updated_at":"2018-03-15T14:00:45Z","closed_at":null,"author_association":"OWNER","body":"# underscore源码分析之整体架构\r\n\r\n\r\n---\r\n最近打算好好看看underscore源码，一个是因为自己确实水平不够，另一个是underscore源码比较简单，比较易读。\r\n本系列打算对underscore1.8.3中关键函数源码进行分析，希望做到最详细的源码分析。\r\n今天是underscore源码剖析系列第一篇，主要对underscore整体架构和基础函数进行分析。\r\n## 基础模块 ##\r\n首先，我们先来简单的看一下整体的代码：\r\n```\r\n// 这里是一个立即调用函数，使用call绑定了外层的this(全局对象)\r\n(function() {\r\n\r\n  var root = this;\r\n  \r\n  // 保存当前环境中已经存在的_变量（在noConflict中用到）\r\n  var previousUnderscore = root._;\r\n  \r\n  // 用变量保存原生方法的引用，以防止这些方法被重写，也便于压缩\r\n  var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;\r\n\r\n  var\r\n    push             = ArrayProto.push,\r\n    slice            = ArrayProto.slice,\r\n    toString         = ObjProto.toString,\r\n    hasOwnProperty   = ObjProto.hasOwnProperty;\r\n\r\n  var\r\n    nativeIsArray      = Array.isArray,\r\n    nativeKeys         = Object.keys,\r\n    nativeBind         = FuncProto.bind,\r\n    nativeCreate       = Object.create;\r\n\r\n  var Ctor = function(){};\r\n  // 内部实现省略\r\n  var _ = function(obj) {};\r\n  \r\n    // 这里是各种方法的实现（省略）\r\n    \r\n  // 导出underscore方法，如果有exports则用exports导出，如果    没有，则将其设为全局变量\r\n  if (typeof exports !== 'undefined') {\r\n    if (typeof module !== 'undefined' && module.exports) {\r\n      exports = module.exports = _;\r\n    }\r\n    exports._ = _;\r\n  } else {\r\n    root._ = _;\r\n  }\r\n  \r\n  // 版本号\r\n  _.VERSION = '1.8.3';\r\n  \r\n  // 用amd的形式导出\r\n  if (typeof define === 'function' && define.amd) {\r\n    define('underscore', [], function() {\r\n      return _;\r\n    });\r\n  }\r\n}.call(this))\r\n```\r\n## 全局对象 ##\r\n这段代码整体比较简单，不过我看后来的underscore版本有一些小改动，主要是将var root = this;替换为下面这句：\r\n```\r\nvar root = typeof self == 'object' && self.self === self && self || typeof global == 'object' && global.global === global && global || this;\r\n```\r\n这里增加了对self和global的判断，self属性可以返回对窗口自身的引用，等价于window，这里主要是为了兼容web worker，因为web worker中是没有window的，global则是为了兼容node，而且在严格模式下，立即执行函数内部的this是undefined。\r\n\r\n## void(0) ? undefined ##\r\n扫一眼源码，我们会发现在源码中并没有见到undefined的出现，反而是用void(0)或者void 0来代替的，那么这个void到底是什么？为什么不能直接用undefined呢？\r\n关于void的解释，我们可以看这里：[MDN][1]\r\n\r\nvoid 运算符通常只用于获取 undefined的原始值，一般使用void(0)，因为undefined不是保留字，在低版本浏览器或者局部作用域中是可以被当做变量赋值的，这样就会导致我们拿不到正确的undefined值，在很多压缩工具中都是将undefined用void 0来代替掉了。\r\n\r\n其实这里不仅是void 0可以拿到undefined，还有其他很多方法也可以拿到，比如0[\"ygy\"]、Object.\\__undefined\\__、Object.__ygy__，这些原理都是访问一个不存在的属性，所以最后一定会返回undefined\r\n\r\n## noConflict ##\r\n也许有时候我们会碰到这样一种情况，_已经被当做一个变量声明了，我们引入underscore后会覆盖这个变量，但是又不想这个变量被覆盖，还好underscore提供了noConflict这个方法。\r\n```\r\n_.noConflict = function() {\r\n    root._ = previousUnderscore;\r\n    return this;\r\n};\r\nvar underscore = _.noConflict();\r\n```\r\n显而易见，这里正常保留原来的_变量，并返回了underscore这个方法（this就是_方法）\r\n\r\n  [1]: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/void"}}
{"actor":{"display_login":"yinguangyao"}}