SZU_SH


  • 首页

  • 分类

  • 归档

  • 搜索

快速理解 js 模块规范

发表于 2018-03-13 | 分类于 JS | 阅读次数:

随着前端的日益壮大,前端模块化的编程成为了一个非常重要的东西,这可以更好的帮我们构造工程化的前端项目。

说道前端模块规范,我们耳熟能详的就这三种 CommonJS, AMD, CMD

CommonJS

首先说一下CommonJS, node.js 的模块系统就是参照 CommonJS 规范实现的。在CommonJS 中有一个全局性的方法require(path),我们通过方法加载需要的模块。

具体的用法如下:

声明模块:

1
2
3
4
5
6

// test.js

exports.test = function(){
console.log('test')
}

引用模块:

1
2

var test = require('test');

CommonJS 规范中,require() 用来引入外部模块,exports ·对象用于导出当前模块的方法或变量,作为唯一的到出口,module 对象就代表模块本身。

CommonJS 主要用在了服务端,因为服务端加载,读取模块都是在本地加载,如果浏览器中使用,比方说上面的代码,网盘,我们在加载 test 模块的时候必须等待他加载完才能继续执行后面的代码,这在服务端没什么问题,但在浏览器中,我们需要把模块下载下来,才能去执行后面的代码,在等待的过程中,浏览器处于“假死”的状态。

为了能够异步的加载模块,于是 AMD 规范就诞生了。

AMD

最常用的RequireJS就是实现了AMD规范。

require.js加载的模块,采用AMD规范。也就是说,模块必须按照AMD的规定来写。

如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。

1
2
3
4
5
define(['dep1','dep2'],function(
dep1,dep2
){

})

加载模块,与 CommonJS 不同的是需要在require中填入回调函数,第一个参数指明需要加载的模块

1
2
3
4
5
 require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){

    // some code here

  });

CMD

玉伯写的 seajs 就是遵循CMD规范写的

1
2
3
define(function(require,exports,module){

})

他与 AMD 存在着一定的区别。

比方说我们需要在运行代码的时候先后加载两个模块:

AMD

1
2
3
4
5
6
7
8

require(['block1','block2'],function(block1,block2){
block1.start();
block1.end();

block2.start();
block2.end();
})

AMD 虽然实现了异步加载,但是在开始的时候就把所有依赖写出来,可能不太符合我们平时书写的一个逻辑顺序,是否能够像 commonJS那样用的时候require,而且还是支持异步加载后再执行呢。

CMD 就是支持的

CMD

1
2
3
4
5
6
7
8
9
10
11
12

define(function(require, exports, module){
var block1 = require('block1');

block1.start();
block1.end();

var block2 = require('block2');
block2.start();
block2.end();

})

CMD 只有当代码执行到 require的时候才会去异步的加载模块,不会像AMD一开始就把需要的模块都加载好才开始执行。

AMD依赖前置,js可以方便知道依赖模块是谁,立即加载;而CMD就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病CMD的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略。

总结

个人认为,在实际的开发中,大多还是用 AMD 规范的,使用 AMD 规范你能够更清楚的看到 模块之间的依赖关系,能够更符合程序员的思维方式。博主暂时也还没有用过 CMD 规范。

ES6 中的 import webpack 也是将其转化为 AMD 规范的。

发布订阅模式

发表于 2018-03-11 | 分类于 JS | 阅读次数:

目前非常流行的 vue 的底层,其实用的就是发布订阅模式。要学习vue的原理的话,很有必要先学习一下这个设计模式。

发布订阅模式

在观察者模式中一般有两个对象发布者和订阅者,订阅者从发布者那里订阅消息,发布者发布消息通知所有订阅消息的对象。当订阅者订阅发布者的时候,发布者会把订阅者添加到自己的订阅者列表中。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

function Publisher(){
this.observers = [];
}

/**
发布者添加订阅对象
**/
Publisher.prototype.add = function(observer){
this.observers.push(observer);
}


/**
发布者通知订阅者
**/
Publisher.prototype.notify = function(context){
this.observers.forEach((observer)=>{
observer.update(context)
});
}


function Observer(){

}

Observer.prototype.update=function(context){
console.log(context)
}

/**
订阅发布者
**/
Observer.prototype.subscribe=function(publisher){
publisher.add(this)
}

var publisher = new Publisher();
var observer = new Observer();

observer.subscribe(publisher);
publisher.notify('事件发布');

快速理解js继承原理

发表于 2018-03-10 | 分类于 JS | 阅读次数:

JS继承原理

js 的继承是通过原型链来实现的

什么是原型链呢?

首先说一下 prototype 属性,在js中所有的方法都会有一个prototype属性,这个属性就是方法的原型对象

比方说:

1
2
3
4
5
6
7
8
9
10

function Animal(){

}

Animal.prototype.run=function(){
console.log('run')
}

var cat = new Animal()

当我们声明 Animal 这个方法的时候, js引擎会赋予Animal一个原型对象,打印出来可以看到

1
2
3
4
5
6
7
8
9
10
console.log(Animal.prototype)

/**

{
constructor:这个指向 Animal这个方法,
__proto__:这个指向Object.prototype
}

**/

在我们构造实例的时候,cat的__proto__ 会指向 Animal 的 prototype, js引擎 在访问对象的属性的时候会先查看当前对象是否这个属性,如果没有则会查看改对象的 __proto__ 是否有这个属性,访问会沿着原型链访问下去直到找到属性或者 __proto__为null为止。

如何实现继承

这里只讲通用的一种继承的写法

这是父类:

1
2
3
4
5
6
7
function Animal(name){
this.name = name;
}

Animal.prototype.run=function(){
console.log(this.name + ' is run')
}

子类的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Cat(name){

// 这里通过调用父类并把this绑定到父类上
Animal.call(this,name)
}

(()=>{
// 这里其实本质要做的就是让Cat.prototype.__proto__=Animal.prototype
// Cat.prototype =Object.create(Animal.prototype) 相当于做了如下的工作
// var a = {};a.__proto__=Animal.prototype;
// Cat.prototype = a;

Cat.prototype = Object.create(Animal.prototype)
Cat.prototype.constructor = Cat
})()

ES6 的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Class Animal{
constructor(name){
this.name=name
}

run(){
console.log(this.name + ' is run')
}
}

Class Cat extends Animal{
constructor(name){
// ES6要求子类构造函数中必须要调用这个方法,这个方式是用来调用父类的constructor的
super(name)
}
}

其实es6的写法本质上上面的语法糖,使用之后代码对代码的可读性有了明显的提高,但是我们还是需要知道JS继承的原理是怎样的。

1…34
$SH

$SH

灵活跳跃诈笑看天下 乐似仙

33 日志
8 分类
GitHub
© 2021 $SH
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4