js 模块化的方法

Module

在模块化规范形成之前,JS 开发者使用 Module 设计模式来解决 JS 全局作用域的污染问题。Module 模式最初被定义为一种在传统软件工程中为类提供私有和共有封装的方法。在 JavaScript,Module 模式使用匿名函数自调用(闭包)来封装,通过自定义暴露行为来区分私有成员和共有成员。

let myModule = (function(window){
    // 私有
    let moduleName = 'module';
    // 公有
    function setModuleName(name){
        moduleName = name;
    }
    // 公有
    function getModuleName(name){
        return moduleName;
    }
    //  暴露行为
    return { setModuleName, getModuleName}
})(window);

上面例子是 Module 模式的一种写法,它通过闭包的特性打开一个新的作用域,缓解了全局作用域命名冲突和安全性的问题。但是开发者并不能用它来组织和拆分代码,于是乎便出现了以此为基石的模块化规范。

CommonJS

CommonJS 主要用在 Node 开发上,每个文件就是一个模块,每个文件都有自己的一个作用域。通过 module.exports 暴露 public 成员。例如:

// 文件名: x.js
let x = 1;
function add(){
    x += 1;
    return x;
}
module.exports.x = x;
module.exports.add = add;
let xm = require( './x.js');
console.log(xm.x); // 1
console.log(xm.add()); // 2
console.log(xm.x) // 1

require 加载是同步的,这限制了 CommonJS 在客户端的使用,因为通过 HTTP 同步加载 CommonJS 是非常耗时的。

ES6 模块化

ES6 的模块化已经不是规范了,而是 JS 语言的特性。随着 ES6 的推出,AMD 和 CMD 也随之成为历史。ES6 与模块化规范相比,有两大特点:

  • 模块化规范输出的是一个值的拷贝,ES6 模块输出的是值的引用;
  • 模块化规范是运行时加载, ES6 模块是编译时输出接口;

讲一讲 websocket

js 实现继承的各种方式,各有什么优缺点

1. 借助 call

function Parent1(){
    this.name = 'parent1';
}

function Child1() {
    Parent1.call(this);
    this.type = 'child1';
}

console.log(new Child1);

借助 call 的缺点:这样写子类虽然能够拿到父类的属性值,但是问题是父类原型对象中一旦存在方法子类无法继承。

2. 借助原型链

function Parent2(){
    this.name = 'Parent2';
    this.play = [1, 2, 3];
}

function Child2() {
    this.type = 'Child2';
}

Child2.prototype = new Parent2();
console.log(new Child2());

// 看似没有问题,父类的方法和属性都能够访问,但实际上有一个潜在的不足。
// 举个例子:
var s1 = new Child2();
var s2 = new Child2();
s1.play.push(4);
console.log(s1.play);  // [1,2,3,4]
console.log(s2.play);  // [1,2,3,4]

将前两种组合

function Parent3(){
    this.name = 'Parent3';
    this.play = [1,2,3]; 
}

function Child3(){
    Parent3.call(this);
    this.type = 'child3';
}

Child3.prototype = new Parent3();
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play);  // [1,2,3,4]
console.log(s4.play);  // [1,2,3]

新增了一个问题:那就是 Parent3 的构造函数会多执行一次(Child3.prototype = new Parent3();)。

组合继承的优化1

function Parent4(){
    this.name = "Parent4";
    this.play = [1,2,3];
}

function Child4(){
    Parent4.call(this);
    this.type = 'Child4';
}

// 这里让父类原型对象直接给到子类,父类构造函数只执行一次
// 而且父类属性和方法均能访问。
Child4.prototype = Parent4.prototype;

var s3 = new Child4();
var s4 = new Child4();

console.log(s3);
// 此时子类实例的构造函数是 Parent4,显然是不对的,应该是 Child4

组合继承的的优化2

function Parent5(){
    this.name = "Parent5";
    this.play = [1,2,3];
}

function Child5(){
    Parent5.call(this);
    this.type = 'Child5';
}

Child5.prototype = Object.create(Parent5.prototype);
child5.prototype.constructor = Child5;

这是最推荐的一种方式,接近完美的继承,它的名字也叫做寄生组合继承。

js 的异步请求方式

高阶组件

flux

http1.1 和 http1.0 的区别

主要区别体现在:

  • 缓存处理:在 1.0 中主要使用 header 里的 If-Modified-Since,Expires 来作为缓存判断的标准,而 HTTP1.1 则引入了更多的缓存控制策略例如 Entity tag,If-Modified-Since,If-None-Match 等更多可供选择的缓存头来控制缓存策略;
  • 长连接:HTTP 1.1 支持长连接和请求的流水线处理,在一个 TCP 连接上可以传送多个 HTTP 请求和响应,减少了建立和关闭连接的消耗和延迟,在 1.1 中默认开启了 Connection: keep-alive,一定程度上弥补了 1.0 每次请求都要创建连接的缺点;
  • Host 头处理:在 1.0 中认为每台服务器都绑定一个唯一的 IP 地址,因此,请求消息中的 URL 并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机,并且它们共享一个 IP 地址。 HTTP 1.1 的请求消息和响应消息都应支持 Host 头域,且请求消息中如果没有 Host 头域会报告一个错误*(400 Bad Request);
  • 带宽优化及网络连接的使用: 在 1.0 中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能。1.1 中在请求头中引入了 range 头域,它允许只请求资源的某个部分,即返回码是 206(Partial Content), 这样就方便了开发者自由地选择以便于充分利用带宽和连接。
  • 错误通知的管理:1.1 中新增了 24 个错误状态响应码,如 409 (Conflict)表示请求的资源与资源的当前状态发生冲突;410 (Gone)表示服务器上的某个资源被永久性地删除

https 与 http 的区别

https 协议需要到 CA 申请证书,一般免费证书很少,需要缴费;
HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,HTTPS 运行在 SSL/TLS 之上,SSL/TLS 运行在 TCP 之上,所有的传输内容都是经过加密的;
HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443;
HTTPS 可以有效的防止运营商劫持,解决了防劫持的一大问题;