1. 如何区分运行JS的环境 ?
先来回顾几个知识点:
- 可运行JS的环境有哪些,分别有些什么特性?
- CommonJS 规范 VS ES6Module 规范
可运行JS的环境
这里说的区分环境是指:区分在哪里运行的JS代码
目前可运行JS的环境主要有:浏览器、webview、node 以及 webpack。
在浏览器内运行JS的特性
- 有 window 对象
- 支持 ES6Module 规范,不支持 CommonJS 规范
常见的浏览器内核有哪些?
浏览器 | 内核 | JavaScript 引擎 |
---|---|---|
Chrome | Blink(28~)| Webkit(Chrome 27) | V8 |
FireFox | Gecko | SpiderMonkey |
Safari | Webkit | JavaScriptCore |
Edge | EdgeHTML | Chakra(for JavaScript) |
IE | Trident | Chakra(for JScript) |
在 node 内运行JS的特性
-
没有 window 对象
-
支持 CommonJS 规范,不支持 ES6Module 规范
我们平常所说的 node ,并不是一门编程语言,
而是指在服务器端安装 node 环境,
使用 JS 写一些后台逻辑,再交由node运行。
在 webview 内运行JS的特性
这种运行环境,主要应用在 Hybrid 混合 App 开发中,使用 Native App 的架子,在内里嵌套 Web App ( HTML5 页面) 。在这种项目中的 JS 就是在 webview 中运行的,实质上就是使用 webkit 渲染引擎来解析运行 JS,其特性与浏览器相同。
在 webpack 内运行JS的特性
-
存在 window 对象
-
它即支持 CommonJS 规范 ,也支持 ES6Module 规范
webpack 它是基于 node 打包 JS 代码,最后把打包好的 JS 代码交由浏览器运行。它即支持 CommonJS 规范 ,也支持 ES6Module 规范。还支持两种规范之间的转换。
简单区分 CommonJS 规范和 ES6Module 规范
CommonJS规范
- 导入用:require
- 导出用:module.exports
ES6Module
- 导出:export & export default
- 导入:import
jQuery 中是如何区分环境的?
- jQuery 官网地址
- 选用 jQuery 版本
jQuery JavaScript Library v3.6.0
首先,我们来折叠 jQuery 代码,从最外层看 jQuery 。
我们不难看出 jQuery 本质上是一个自执行函数。
它在调用自己的匿名函数之前,为匿名函数传递了2个实参。分别由匿名函数中的 global, factory
两个形参接收。
确定环境中是否存在 window 对象
先来看看传递给 global
的第一个实参。
typeof window !== "undefined" ? window : this
复制代码
这句代码的目的是,使用 typeof 检测是否存在 window 对象
使用 typeof 检测 一个未被声明定义的变量时,是不会报错的,只会返回一个 “undefined”。
window 对象在浏览器、webview 和 webpack 中,是全局对象,而在 node 中,没有 window 对象。
typeof window !== "undefined"
说明运行环境中存在 window 对象,那么就把 window 对象传递给形参 global
。反之,就是不存在 window 对象,就把 this 传递给形参 global
,用来表示当前模块。
我们也可以得出的结论是:可以运行JS代码,而又没有 window 对象,就是在 node 环境下运行的。
核心方法 factory
再来看看传递给匿名函数第二个形参 factory
的实参,是一个函数。一个写得很多很长的函数。
这个函数是 JQuery 的核心方法, 为了方便简称它为 factory 方法,这个方法有 window, noGlobal 两个形参。
匿名函数执行
匿名函数接收到传递的两个实参收,开始执行。
看看下面这张图:
匿名函数的顶端第一句是:
"use strict"; // 意思是启用“严格模式”
复制代码
接下来是一个 if…else… 条件判断,如图:
不难看出,这是要在满足一定的条件下,才能执行 factory 方法。
那么,究竟是些什么条件呢?
看 if 条件
if (typeof module === "object" && typeof module.exports === "object") ...
复制代码
这句的意思是:既要存在 module 对象,也要存在 module.exports 才满足条件。
我们先假设满足 if 条件判断,进入执行 if 语句代码块。
是个给 module.exports 赋值的语句,将三目运算的结果赋给 module.exports。
三目运算的条件是 global.document ,由前面知 gloabl 可能是 window 对象也可能是 this。而 document 只有 window 对象中才有哦。
如满足这个条件,进一步确定是有 window 对象的环境。便执行 factory 方法,且将其执行的结果赋给 module.exports 。执行 factory 方法时,传递了2个实参,第一个是 global (也就是window对象) ,第二个是个布尔值 true。
如不满足三目运算的判断条件,则将一个函数赋给 module.exports。
这个函数的具体代码如下:
function (w) {
if (!w.document) {
throw new Error("jQuery requires a window with a document");
}
return factory(w);
};
复制代码
我们先假设不满足 if 条件判断,进入执行 else 语句代码块。执行 factory 方法,并将 global 即 window 对象传递给 factory 方法的第一个形参。
哈哈,有点糊,有点迷糊,简单捋一捋思路。
if 判断, 有无 module 和 module.exports 两个对象,
是为了确定当前环境是否支持 CommonJS规范。
支持 CommonJS 规范才存在这个两个对象,不支持则没有。
支持 CommonJS规范的前提下,再进行三目运算。
通过判断 global 对象中是否有 document ,
来判断 global 是不是指向 window。
进而区分运行环境是在 node 还是在 webpack 。
=》支持 CommonJS规范,有window 对象的是 webpack。
执行 factory 方法,factory 执行返回的是 jQuery 对象,赋给了 module.exports ,
也就是用 module.exports 暴露 jQuery 对象。
=》支持 CommonJS规范,没有window 对象的是 node。
此时,将一个函数赋给 module.exports,
node 环境下调用这个函数,将会抛出错误。
复制代码