服务器代理转发时,该如何处理 cookie?

详细资料可以参考:
《深入浅出 Nginx》

我的理解是 cookie 是服务器提供的一种用于维护会话状态信息的数据,通过服务器发送到浏览器,浏览器保存在本地,当下一次有同源的请求时,将保存的 cookie 值添加到请求头部,发送给服务端。这可以用来实现记录用户登录状态等功能。cookie 一般可以存储 4k 大小的数据,并且只能够被同源的网页所共享访问。

服务器端可以使用 Set-Cookie 的响应头部来配置 cookie 信息。一条cookie 包括了9个属性值 name、value、expires、domain、path、secure、HttpOnly、SameSite、Priority。其中 name 和 value 分别是 cookie 的名字和值。expires 指定了 cookie 失效的时间,domain 是域名、path是路径,domain 和 path 一起限制了 cookie 能够被哪些 url 访问。secure 规定了 cookie 只能在确保安全的情况下传输,HttpOnly 规定了这个 cookie 只能被服务器访问,不能使用 js 脚本访问。SameSite 属性用来限制第三方 cookie,可以有效防止 CSRF 攻击,从而减少安全风险。Priority 是 chrome 的提案,定义了三种优先级,当 cookie 数量超出时低优先级的 cookie 会被优先清除。

在发生 xhr 的跨域请求的时候,即使是同源下的 cookie,也不会被自动添加到请求头部,除非显示地规定。

详细资料可以参考:
《HTTP cookies》
《聊一聊 cookie》

模块化开发怎么做?

我对模块的理解是,一个模块是实现一个特定功能的一组方法。在最开始的时候,js 只实现一些简单的功能,所以并没有模块的概念
,但随着程序越来越复杂,代码的模块化开发变得越来越重要。

由于函数具有独立作用域的特点,最原始的写法是使用函数来作为模块,几个函数作为一个模块,但是这种方式容易造成全局变量的污
染,并且模块间没有联系。

后面提出了对象写法,通过将函数作为一个对象的方法来实现,这样解决了直接使用函数作为模块的一些缺点,但是这种办法会暴露所
有的所有的模块成员,外部代码可以修改内部属性的值。

现在最常用的是立即执行函数的写法,通过利用闭包来实现模块私有作用域的建立,同时不会对全局作用域造成污染。

详细资料可以参考:
《浅谈模块化开发》
《Javascript 模块化编程(一):模块的写法》
《前端模块化:CommonJS,AMD,CMD,ES6》
《Module 的语法》

js 的几种模块规范?

js 中现在比较成熟的有四种模块加载方案。

第一种是 CommonJS 方案,它通过 require 来引入模块,通过 module.exports 定义模块的输出接口。这种模块加载方案是
服务器端的解决方案,它是以同步的方式来引入模块的,因为在服务端文件都存储在本地磁盘,所以读取非常快,所以以同步的方式
加载没有问题。但如果是在浏览器端,由于模块的加载是使用网络请求,因此使用异步加载的方式更加合适。

第二种是 AMD 方案,这种方案采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行,所有依赖这个模块的语句都定
义在一个回调函数里,等到加载完成后再执行回调函数。require.js 实现了 AMD 规范。

第三种是 CMD 方案,这种方案和 AMD 方案都是为了解决异步模块加载的问题,sea.js 实现了 CMD 规范。它和 require.js
的区别在于模块定义时对依赖的处理不同和对依赖模块的执行时机的处理不同。参考60

第四种方案是 ES6 提出的方案,使用 import 和 export 的形式来导入导出模块。这种方案和上面三种方案都不同。参考 61。

AMD 和 CMD 规范的区别?

它们之间的主要区别有两个方面。

(1)第一个方面是在模块定义时对依赖的处理不同。AMD 推崇依赖前置,在定义模块的时候就要声明其依赖的模块。而 CMD 推崇
就近依赖,只有在用到某个模块的时候再去 require。

(2)第二个方面是对依赖模块的执行时机处理不同。首先 AMD 和 CMD 对于模块的加载方式都是异步加载,不过它们的区别在于
模块的执行时机,AMD 在依赖模块加载完成后就直接执行依赖模块,依赖模块的执行顺序和我们书写的顺序不一定一致。而 CMD
在依赖模块加载完成后并不执行,只是下载而已,等到所有的依赖模块都加载好后,进入回调函数逻辑,遇到 require 语句
的时候才执行对应的模块,这样模块的执行顺序就和我们书写的顺序保持一致了。

// CMD
define(function(require, exports, module) {
var a = require("./a");
a.doSomething();
// 此处略去 100 行
var b = require("./b"); // 依赖可以就近书写
b.doSomething();
// ...
});

// AMD 默认推荐
define(["./a", "./b"], function(a, b) {
// 依赖必须一开始就写好
a.doSomething();
// 此处略去 100 行
b.doSomething();
// ...
});

详细资料可以参考:
《前端模块化,AMD 与 CMD 的区别》

ES6 模块与 CommonJS 模块、AMD、CMD 的差异。

  • 1.CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令 import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。

  • 2.CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。CommonJS 模块就是对象,即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。

requireJS 的核心原理是什么?(如何动态加载的?如何避免多次加载的?如何 缓存的?)

require.js 的核心原理是通过动态创建 script 脚本来异步引入模块,然后对每个脚本的 load 事件进行监听,如果每个脚本都加载完成了,再调用回调函数。

详细资料可以参考:
《requireJS 的用法和原理分析》
《requireJS 的核心原理是什么?》
《从 RequireJs 源码剖析脚本加载原理》
《requireJS 原理分析》

JS 模块加载器的轮子怎么造,也就是如何实现一个模块加载器?

详细资料可以参考:
《JS 模块加载器加载原理是怎么样的?》

ECMAScript6 怎么写 class,为什么会出现 class 这种东西?

在我看来 ES6 新添加的 class 只是为了补充 js 中缺少的一些面向对象语言的特性,但本质上来说它只是一种语法糖,不是一个新的东西,其背后还是原型继承的思想。通过加入 class 可以有利于我们更好的组织代码。

在 class 中添加的方法,其实是添加在类的原型上的。

详细资料可以参考:
《ECMAScript 6 实现了 class,对 JavaScript 前端开发有什么意义?》
《Class 的基本语法》

documen.write 和 innerHTML 的区别?

document.write 的内容会代替整个文档内容,会重写整个页面。

innerHTML 的内容只是替代指定元素的内容,只会重写页面中的部分内容。

详细资料可以参考:
《简述 document.write 和 innerHTML 的区别。》