摘要:学习冴羽的博客研究 underscore 组织功能函数,形成自己的一个工具函数库
基本结构
一般情况下是把工具函数全部装载到一个对象上,再把这个对象挂载到全局对象上
1 | (function(){ |
获取全局对象
严格模式
严格模式下,this 指向 undefined (this),因而要对其进行修改
1 | var root = (typeof window == 'object' && window.window == window && window) || (typeof global == 'object' && global.global == global && global) |
Web Worker
Web Worker 处在一个自包含的执行环境中,无法访问 Window 对象和 Document 对象,和主线程之间的通信业只能通过异步消息传递机制来实现。
在浏览器中,除了 window 属性,还可以通过 self 属性直接访问到 Winow 对象,Web Worker 中可以访问到 self,好吧 self window window.window self.self window.self self.window 都是指向 window。再次修改
1 | var root = (typeof self == 'object' && self.self == self && self) || (typeof global == 'object' && global.global == global && global) |
node vm
在 node 的 vm 模块 runInContext 方法中无法访问到全局变量,使用 window 和 global 会报错,但是使用 this 可以访问到全局对象:
1 | var vm = require("vm"); |
再次修改:
1 | var root = (typeof window == 'object' && window.window == window && window) || (typeof global == 'object' && global.global == global && global) || this; |
微信小程序
在小程序中没有 window 和 global,又强制使用严格模式,导致 this 为 undefined,只能自己创建一个可以全局引用的空对象:
1 | var root = (typeof window == 'object' && window.window == window && window) || (typeof global == 'object' && global.global == global && global) || this || {}; |
装载全部方法
函数对象
underscore 既支持函数式风格_.fn1('hello');
使用,又支持类似面向对象风格_('hello').fn1()
这样调用。这使得_
像是一个函数,函数也是一种对象,把工具函数都挂载到一个函数上也没问题:
1 | var _ = function() {} |
问题来了:如何实现_('hello')
返回的一个对象仍能调用挂载到 _ 上的 fn1 呢?
underscore 的实现方法:
1 | var _ = function(obj) { |
过程:
1、执行this instanceof _
,this 指向 window,其原型链上不包含 _.prototype, 返回 false。取反后执行new _(obj)
2、执行new _(obj)
,this 指向生成的实例对象,实例对象的原型为 _.prototype,this instanceof _
返回 true,取反后执行this._wrapped = obj
3、最后new默认返回实例对象{_wrapped: [1, 2, 3]}
,并且其原型为 _.prototype
到现在返回的实例对象仍然无法调用 _ 函数身上的方法,因为 _ 函数的方法并没有挂载到 _.prototype 上,下一步将 _ 身上的方法复制到 _.prototype
_.functions
为了将 _ 上的方法复制到原型上,需要先获取 _ 上的所有方法:
1 | _.functions = function(obj) { |
isFunction 参考JavaScript检测类型
_.mixin
获取 _ 上的所有方法后,就可以将所有方法添加到 _ 额原型上:
1 | var ArrayProto = Array.prototype; |
each 参考实现jQuery中的each
拓展自定义方法:
1 | var obj = { |
导出
需要将 _ 在合适的环境中作为模块导出,但是 nodejs 模块的 API 曾经发生过改变:
旧版本:
1 | // add.js |
新版本:
1 | // add.js |
因而要根据 exports 和 module 是否存在来选择不同的导出方式:
1 | if (typeof exports != 'undefined') { // 处于 node 环境下 |
还有一个问题:在 HTML 页面中加入一个 id 为 exports 的元素,比如
1 | <div id="exports"></div> |
就会生成一个 window.exports 全局变量,可以直接在浏览器命令行中打印出该变量。此时typeof exports != 'undefined'
返回值为 true 却处于浏览器环境中,因此还需要排除是 DOM 节点的情况:
1 | if (typeof exports != 'undefined' && !exports.nodeType) { |
源码
1 | (function () { |