xyhthink

JavaScript: 数据类型、面向对象、继承、闭包、插件、作用域、跨域、原型链、模块化、自定义事件、内存泄漏、事件机制、异步装载回调、模板引擎、Nodejs、JSON、ajax等。

会持续定期更新HTML + CSS + JS的面试题,并且分享题目答案,如有理解不对的地方,欢迎评论指正!

1. Cookie安全问题及解决

cookie不安全表现形式

因为Cookie存储在浏览器端(用户本地),一些别有用心的人能够通过浏览器截获cookie(脚本、利用工具抓取等)。

cookie欺骗

别有用心的人不需要知道这个cookie的具体含义,只需要将这个cookie向服务器提交(模拟身份验证),身份验证通过之后,就可以冒充被窃取cookie对应用户来访问网站,甚至获取到用户的隐私信息,对于用户的隐私造成非常严重的危害,这种方式就叫做cookie欺骗。

cookie截获

cookie以纯文本的形式在浏览器和服务器之间传递,在web通信时极容易被非法用户截获和利用。非法用户截获cookie后,在cookie的有效时间内重新发放给服务器,那么这个非法用户就拥有了这个合法用户的所有权限。

Flash的内部代码隐患

Flash中有一个getURL()函数,Flash利用它自动打开指定的页面。那么这个就意味着,你在观看Flash动画时,在Flash的内部可以悄无声息的打开一个极小的不易发现的包含特殊操作的页面,可以是木马,可以向远端输入当前cookie或者用户信息,这是非常危险的,由于这个是Flash内部的操作,所以网站无法禁止,要想避免,尽量打开本地防火墙以及访问正规网站。

如何解决cookie安全性问题

  1. 设置cookie有效期Expire不要过长,合适即可;

  2. 设置HttpOnly属性为true

    • 可以防止js脚本读取cookie信息,有效的防止XSS攻击;
  3. 设置复杂的cookie,加密cookie

    • cookie的key使用uuid,随机生成;

    • cookie的value可以使用复杂组合,比如:用户名+当前时间+cookie有效时间+随机数。

  4. 用户第一次登录时,保存ip+cookie加密后的token

    • 每次请求,都去将当前cookie和ip组合起来加密后的token与保存的token作对比,只有完全对应才能验证成功。
  5. session和cookie同时使用

    • sessionId虽然放在cookie中,但是相对的session更安全,可以将相对重要的信息存入session。
  6. 如果网站支持https,尽可能使用https

    • 如果网站支持https,那么可以为cookie设置Secure属性为true,它的意思是,cookie只能使用https协议发送给服务器,而https比http更加安全。

2. new操作符具体干了什么呢?

  1. 创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
  2. 属性和方法被加入到 this 引用的对象中。
  3. 新创建的对象由 this 所引用,并且最后隐式的返回 this 。
1
2
3
4
5
6
const a = new Base();

var obj = {}; // 创建了一个新的空对象 obj
obj.__proto__ = Base.prototype; // 让这个obj对象的 __proto__指向函数的原型prototype
Base.call(obj); // this指向obj对象
a = obj; // 将obj对象赋给a对象

3. call() 、 apply() 、bind()方法的作用和区别?

call() 、apply()、bind()这三个方法的作用都是:改变函数执行的上下文,也就是改变函数体内部的this的指向,以此来扩充函数赖以运行作用域。

  1. bind与call、apply的区别

    bind()是返回的是执行上下文被改变的函数且不会立即执行,call()、apply()直接执行该函数;

  2. apply与call的区别:两者的参数

    • apply :最多只能有两个参数,第二个参数为数组或者arguments对象。如果传递多个参数,则把参数都写进数组里面,当然,即使只有一个参数,也要写进数组里。
      语法:apply(thisObj,[argArray])

    • call: 其他的参数必须直接传给函数,要一个一个的列出来
      语法:call(thisObj,param1,param2…)

bind封装

1
2
3
4
5
6
7
8
9
10
11
12
function bind(fn, _this) {
// 在调用bind复制函数的时候 , 传入的参数;
// 这个参数是用来固定函数的参数的;
var arg1 = [].slice.call(arguments).slice(2);
return function () {
// 参数来源源自两个地方:
// 1. bind在创建函数时绑定的参数;
// 2. 匿名函数被调用时传入的参数;
var arg2 = arg1.concat([].slice.call(arguments));
fn.apply(_this, arg2);
}
}

4. 事件循环EventLoop

事件循环EventLoop,详细介绍JS中关于事件队列,宏任务微任务。

5. 原生JS的增、删、改、查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 增
createDocumentFragment() //创建一个DOM片段
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点

// 添加、移除、替换、插入
appendChild()
removeChild()
replaceChild()
insertBefore()

// 查
getElementById() // 通过元素Id,唯一性
getElementsByTagName() // 通过标签名称
getElementsByName() // 通过元素的Name属性的值(IE容错能力较强,会得到一个数组,其中包括id等于name值的)
querySelectorAll() // 支持css的选择器格式

6. 实现一个函数clone 可以对Javascript中的五种主要数据类型(Number、string、Object、Array、Boolean)进行复制

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
function clone(obj) {
var o;
switch (typeof obj) {
case "undefined":
break;
case "string":
o = obj + "";
break;
case "number":
o = obj - 0;
break;
case "boolean":
o = obj;
break;
case "object": // object 分为两种情况 对象(Object)或数组(Array)
if (obj === null) {
o = null;
} else {
if (Object.prototype.toString.call(obj).slice(8, -1) === "Array") {
o = [];
for (var i = 0; i < obj.length; i++) {
o.push(clone(obj[i])); // 递归克隆数组
}
} else {
o = {};
for (var k in obj) {
o[k] = clone(obj[k]); // 递归克隆对象
}
}
}
break;
default:
o = obj;
break;
}
return o;
}

7. 去除数组里重复的数字

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
// 哈希表去重
function delectRepeat(arr1) {
var len = arr1.length;
var arr = [];
for(var i = 0; i < len; i++)
if(arr.indexOf(arr1[i]) == -1)
arr.push(arr1[i]);
return arr;
}

// ES6 Set去重
function delectRepeat(arr){
var x = new Set(arr);
return x;
}

// 数组下标判断
// 如果当前数组的第 i 项在当前数组中第一次出现的位置不是 i,那么表示第 i 项是重复的,忽略掉。否则存入结果数组。
function delectRepeat(arr){
var arr1=[];
for (var i = 0; i < arr.length; i++) {
if(arr.indexOf(arr[i])==i){
arr1.push(arr[i]);
}
}
return arr1;
}

// 使用对象保存重复出现次数保存
function delectRepeat(arr){
var obj = {};
for(var i = 0; i < arr.length; i++){
if(!obj[arr[i]])
obj[arr[i]] = 1;
else
obj[arr[i]]++;
}
return obj;
}

8. 如何判断数据类型?

1
2
3
4
5
6
7
8
9
10
11
12
1. 使用typeof操作符。
对一个值使用 typeof 操作符可能返回下列某个字符串,返回的类型都是字符串形式。
undefined:如果这个值未定义
boolean:如果这个值是布尔值
string:如果这个值是字符串
number:如果这个值是数值
object:如果这个值是对象或null
function:如果这个值是函数

***typeof不适合用于判断是否为数组。当使用typeof判断数组和对象的时候,都会返回object
***可以使用isArray()来判断是否为数组。
***也可以使用Object.prototype.toString.call(obj)判断是否为数组

9. 什么是闭包?

  • e.g
1
2
3
4
5
6
7
8
function A(){
function B(){
console.log('Hello Closure!');
}
return B;
}
var C = A();
C();// Hello Closure!

函数A的内部函数B被函数A外的一个变量 c 引用。

把这句话再加工一下就变成了闭包的定义:

当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。

  • Javascript 中的垃圾回收机制

在 Javascript 中,如果一个对象不再被引用,那么这个对象就会被垃圾回收回收,否则这个对象一直会保存在内存中。

在上述例子中,B 定义在 A 中,因此 B 依赖于 A ,而外部变量 C 又引用了 B , 所以A间接的被 C 引用。

也就是说,A 不会被 GC 回收,会一直保存在内存中。上面的例子稍作改进:

1
2
3
4
5
6
7
8
9
10
11
12
function A() {
var count = 0;
function B() {
count ++;
console.log(count);
}
return B;
}
var C = A();
C();// 1
C();// 2
C();// 3

count 是函数A 中的一个变量,它的值在函数B 中被改变,函数 B 每执行一次,count的值就在原来的基础上累加 1 。因此,函数A中的 count 变量会一直保存在内存中。

  • 闭包的用途

当我们需要在模块中定义一些变量,并希望这些变量一直保存在内存中但又不会 “污染” 全局的变量时,就可以用闭包来定义这个模块。

  • 闭包的升级写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(function (document) {
var viewport;
var obj = {
init: function(id) {
viewport = document.querySelector('#' + id);
},
addChild: function(child) {
viewport.appendChild(child);
},
removeChild: function(child) {
viewport.removeChild(child);
}
}
window.jView = obj; // [1]
})(document);

​ 这个组件的作用是:初始化一个容器,然后可以给这个容器添加子容器,也可以移除一个容器。

​ [1] obj 是在函数 f 中定义的一个对象,这个对象中定义了一系列方法, 执行window.jView = obj就是在 window全局对象定义了一个变量 jView,并将这个变量指向 obj 对象,即全局变量 jView 引用了 obj . 而 obj对象中的函数又引用了函数 f 中的变量 viewport ,因此函数 f 中的 viewport 不会被 GC 回收,viewport会一直保存到内存中,所以这种写法满足了闭包的条件。

10. JS的事件委托是什么,原理是什么,有什么优点?

事件委托:也称事件代理(delegation)。

原理:由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。

1
2
3
4
5
6
7
var ul = document.querySelector('ul');

ul.addEventListener('click', function (event) {
if (event.target.tagName.toLowerCase() === 'li') {
// some code
}
});
  • 上面代码中,click事件的监听函数定义在<ul>节点,但是实际上,它处理的是子节点<li>click事件。这样做的好处是,只要定义一个监听函数,就能处理多个子节点的事件,而不用在每个<li>节点上定义监听函数。而且以后再添加子节点,监听函数依然有效。

  • 如果希望事件到某个节点为止,不再传播,可以使用事件对象的stopPropagation方法或stopImmediatePropagation方法。

    • stopPropagation方法只会阻止事件的传播,不会阻止该事件触发<p>节点的其他click事件的监听函数。
    • stopImmediatePropagation方法可以彻底取消这个事件,使得后面绑定的所有click监听函数都不再触发。
  • 事件委托的优点

  1. 提高性能:每一个函数都会占用内存空间,只需添加一个事件处理程序代理所有事件,所占用的内存空间更少。
  2. 动态监听:使用事件委托可以自动绑定动态添加的元素,即新增的节点不需要主动添加也可以一样具有和其他元素一样的事件。

11. DOM0级事件和DOM2级事件的区别?

  • DOM0级事件
1
2
<!-- 一种是在标签内写onclick事件 -->
<input id="myButton" type="button" value="Press Me" οnclick="alert('thanks');" >
1
2
3
4
// 一种是在JS写onlicke=function(){}函数
myButton.onclick = function() {
alert('thanks');
}
  • DOM2级事件

    ​ 原生有两个方法用来添加和移除事件处理程序:addEventListener()removeEventListener()

    它们都有三个参数:

    第一个参数是事件名(如click);

    第二个参数是事件处理程序函数;

    第三个参数如果是true则表示在捕获阶段调用,为false表示在冒泡阶段调用。

    • addEventListener():可以为元素添加多个事件处理程序,触发时会按照添加顺序依次调用
    • removeEventListener():不能移除匿名添加的函数。
1
myButton.addEventListener("click", function(){alert(1)}, false);

只有2级DOM包含3个事件:事件捕获阶段、目标阶段和冒泡阶段

1
2
3
<span>
<a></a>
</span>

​ 点击a后capturing捕获阶段事件传播会从document-> span->a,然后发生在a,最后bubbling冒泡阶段事件传播会从a->span->document

  • 区别
  1. “HTML 的 on- 属性”,违反了 HTML 与 JavaScript 代码相分离的原则,将两者写在一起,不利于代码分工,因此不推荐使用。

  2. “元素节点的事件属性”的缺点在于,同一个事件只能定义一个监听函数,也就是说,如果定义两次onclick属性,后一次定义会覆盖前一次。dom0和dom2可以共存,不互相覆盖,但是dom0之间依然会覆盖。

  3. DOM2级事件监听同一个事件可以添加多个监听函数;能够指定在哪个阶段(捕获阶段还是冒泡阶段)触发监听函数;除了 DOM 节点,其他对象(比如windowXMLHttpRequest等)也有这个接口。

12. 列举几种解决跨域问题的方式,且说明原理

  • 1. JSONP

script标签是不受同源策略影响的,它可以引入来自任何地方的js文件。

在客户端和服务端定义一个函数,当客户端发起一个请求时,服务端返回一段javascript代码,其中调用了在客户端定义的函数,并将相应的数据作为参数传入该函数。

缺点:它只支持GET请求而不支持POST等其它类型的HTTP请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function jsonp_cb(data) {
console.log(data);
}
function ajax(){
var url = "http://xx.com/test.php?jsonp_callback=jsonp_cb";
var script = document.createElement('script');
// 发送请求
script.src = url;
document.head.appendChild(script);
}
ajax();
// 服务端获取到jsonp_callback传递的函数名jsonp_cb,返回一段对该函数调用的js代码
jsonp_cb({
"name": "story"
});
  • 2. 服务器代理

服务器其实是不受同源策略限制的;

浏览器发送ajax请求 => 本机的nginx服务器 => 代理请求,向其他服务器发起请求并返回数据 => 返回的数据由nginx进行转发,返回浏览器;

nginx服务器的nginx.conf配置如下:

1
2
3
4
5
6
7
http {
server {
location = /自己的代理名称 {
proxy_pass 远程服务器域名;
}
}
}

ajax的url配置如下:

1
2
3
let url = "http://服务器地址/代理名称?接口";
// 例如:
let url = "http://localhost/dt?data";
  • 3. CORS

CORS 是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。相比 JSONP 只能发GET请求,CORS 允许任何类型的请求,从而克服了 AJAX 只能同源使用的限制。

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

实现此功能非常简单,只需由服务器发送一个响应标头即可。

1
2
Access-Control-Allow-Origin: * // 允许来自任何域的请求
Access-Control-Allow-Origin: https://www.xyhthink.com/ // 仅允许来自https://www.xyhthink.com/的请求
  • 4. location.hash,又称FIM,Fragment Identitier Messaging

URL有一部分被称为hash,就是#号及其后面的字符,它一般用于浏览器锚点定位,Server端并不关心这部分,应该说HTTP请求过程中不会携带hash,所以这部分的修改不会产生HTTP请求,但是会产生浏览器历史记录。

此方法的原理就是改变URL的hash部分来进行双向通信。每个window通过改变其他 window的location来发送消息,并通过监听自己的URL的变化来接收消息。

这个方式的通信会造成一些不必要的浏览器历史记录,而且有些浏览器不支持onhashchange事件,需要轮询来获知URL的改变,最后,这样做也存在缺点,诸如数据直接暴露在了url中,数据容量和类型都有限等。

  • 5. postMessage

window.postMessage() 方法可以安全地实现跨源通信。window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。

这个功能主要包括接受信息的”message”事件和发送消息的”postMessage”方法。

  • postMessage的使用方法:
1
2
3
4
5
otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow: // 指目标窗口,也就是给哪个window发消息,是 window.frames 属性的成员或者由 window.open 方法创建的窗口
message: // 是要发送的消息,类型为 String、Object (IE8、9 不支持)
targetOrigin: // 是限定消息接收范围,不限制请使用 '*',
transfer: // 可选。是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
  • A页面通过postMessage方法发送消息:
1
2
3
4
5
window.onload = function() {
var ifr = document.getElementById('ifr');
var targetOrigin = "http://www.google.com";
ifr.contentWindow.postMessage('hello world!', targetOrigin);
};
  • B页面通过message事件监听并接受消息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var onmessage = function (event) {
var data = event.data;//消息
var origin = event.origin;//消息来源地址
var source = event.source;//源Window对象
if(origin=="http://www.baidu.com"){
console.log(data);//hello world!
}
};
if (typeof window.addEventListener != 'undefined') {
window.addEventListener('message', onmessage, false);
} else if (typeof window.attachEvent != 'undefined') {
//for ie
window.attachEvent('onmessage', onmessage);
}
  • 6. window.name
  • 7. iframe

13. 谈谈垃圾回收机制的方式及内存管理和内存泄漏

  • 垃圾回收机制方式

垃圾回收机制(GC:Garbage Collection),执行环境负责管理代码执行过程中使用的内存。

垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行。

e.g

1
2
3
4
5
6
7
8
9
function fn1() {
    var obj = {name: 'hanzichi', age: 10};
}
function fn2() {
var obj = {name:'hanzichi', age: 10};
    return obj;
}
var a = fn1();
var b = fn2();

fn1中定义的obj为局部变量,而当调用结束后,出了fn1的环境,那么该块内存会被js引擎中的垃圾回收器自动释放;

在fn2被调用的过程中,返回的对象被全局变量b所指向,所以该块内存并不会被释放。

  • 垃圾回收策略

    • 标记清除:

      当变量进入环境时,将变量标记”进入环境”,当变量离开环境时,标记为:”离开环境”。某一个时刻,垃圾回收器会过滤掉环境中的变量,以及被环境变量引用的变量,剩下的就是被视为准备回收的变量。

      IE、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。此方法较为常用

    • 引用计数:

      引用计数是跟踪记录每个值被引用的次数。

      就是变量的引用次数,被引用一次则加1,当这个引用计数为0时,被视为准备回收的对象。

  • 内存管理

    1. 什么时候触发垃圾回收机制?

      IE6的垃圾回收是根据内存分配量运行的,当环境中的变量,对象,字符串达到一定数量时触发垃圾回收。垃圾回收器一直处于工作状态,严重影响浏览器性能。

      IE7中,垃圾回收器会根据内存分配量与程序占用内存的比例进行动态调整,开始回收工作。

    2. 合理的GC方案:(1)、遍历所有可访问的对象; (2)、回收已不可访问的对象。

    3. GC缺陷:停止响应其他操作;

    4. GC优化策略:(1)、分代回收(Generation GC);(2)、增量GC

  • 内存泄漏

    内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。C#和Java等语言采用了自动垃圾回收方法管理内存,几乎不会发生内存泄露。我们知道,浏览器中也是采用自动垃圾回收方法管理内存,但由于浏览器垃圾回收方法有bug,会产生内存泄露。

  • 内存泄露的几种情况:

    1. 当页面中元素被移除或替换时,若元素绑定的事件仍没被移除,在IE中不会作出恰当处理,此时要先手工移除事件,不然会存在内存泄露。
    1
    2
    3
    4
    5
    6
    // 解决1,使用完手动释放(置为null)
    var btn = document.getElementById("myBtn");
    btn.onclick = function(){
    btn.onclick = null;
    document.getElementById("myDiv").innerHTML = "Processing...";
    }
    1. 由于是函数内定义函数,并且内部函数–事件回调的引用外暴了,形成了闭包。闭包可以维持函数内局部变量,使其得不到释放。

14. js实现继承的方法有哪些?

原型链的继承以及基于class的继承

15. 有哪些方法可以判断一个变量是否是数组?

  1. instanceof
1
2
3
function isArray (obj) {
return obj instanceof Array;
}
  1. isArray
1
2
3
function isArray (obj) {
return Array.isArray(obj);
}
  1. Object.prototype.toString
1
2
3
4
function isArray (obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
// return Object.prototype.toString.call(obj).slice(8, -1) === 'Array';
}

16. let ,const ,var 有什么区别?

  • let

    1. 有块级作用域 ==> 大括号作用域(减少使用匿名函数);
    2. 不允许声明提升
    3. 暂时性死区TDZ(在变量声明时,变量之前所有空间,不允许使用这个变量。)
    4. 在同一作用域中,不允许重复声明
  • const

    1. const 和 let 的作用域是一致的;
    2. const 变量一旦被赋值,就不能再改变了;
    3. 不允许声明提升
    4. 在同一作用域中,**不允许重复声明
  • var

    1. var的作用域是函数作用域;
    2. 存在声明提升,但变量的赋值并没有提前。

17. 箭头函数与普通函数的区别

  1. 箭头函数是匿名函数,不能作为构造函数,不能使用new

    1
    2
    3
    4
    5
    6
    7
    let FunConstructor = () => {
    console.log('lll');
    }
    let fc = new FunConstructor();

    // Uncaught TypeError: FunConstructor is not a constructor
    // at <anonymous>:5:10
  1. 箭头函数不绑定arguments,取而代之用rest参数…解决

    1
    2
    3
    4
    5
    6
    7
    8
    9
    let B = (b)=>{
    console.log(arguments);
    }
    B(2,92,32,32); // Uncaught ReferenceError: arguments is not defined

    let C = (...c) => {
    console.log(c);
    }
    C(3,82,32,11323); // [3, 82, 32, 11323]
  1. 箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var obj = {
    a: 10,
    b: () => {
    console.log(this.a); // undefined
    console.log(this); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
    },
    c: function() {
    console.log(this.a); // 10
    console.log(this); // {a: 10, b: ƒ, c: ƒ}
    }
    }
    obj.b();
    obj.c();
  2. 箭头函数通过 call() 或 apply() 方法调用一个函数时,只传入了一个参数,对 this 并没有影响。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    let obj2 = {
    a: 10,
    b: function(n) {
    let f = (n) => n + this.a;
    return f(n);
    },
    c: function(n) {
    let f = (n) => n + this.a;
    let m = {
    a: 20
    };
    return f.call(m,n);
    }
    };
    console.log(obj2.b(1)); // 11
    console.log(obj2.c(1)); // 11
  3. 箭头函数没有原型属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var a = ()=>{
    return 1;
    }

    function b(){
    return 2;
    }

    console.log(a.prototype); // undefined
    console.log(b.prototype); // {constructor: ƒ}
  4. 箭头函数不能当做Generator函数,不能使用yield关键字

18. Ajax原理

AJAX

19. 什么是模块化开发?

  • 什么是模块化开发?

    1. 模块就是一个有特定功能的文件,我们可以通过加载这些模块得到特定的功能
    2. 模块化开发就是js的功能分离,通过需求引入不同的文件
    3. 模块化开发可以使代码耦合度降低,避免代码多次在页面出现,他最大的作用就是重用
  • 模块开发要遵循的规范

    1. AMD规范也叫异步模块加载规范,在这个规范下模块会异步加载,不影响后面语句的执行,我们可以使用define定义模块,使用require调用模块
    2. CommonJS规范是服务器端模块的规范,node.js就采用了这个规范,每个模块都有一个单独的作用域,模块内部的变量无法被其他模块读取,除非定义为global的对象和属性
    3. CMD规范通用模块定义.CMD是按需加载,一个模块就是一个文件

CSS模块化 – @import

JS模块化

CommonJS规范 — CommonJs社区制定了Modules/1.0规范

使用require方法引入其他模块(js文件),执行的结果即为别的模块(js文件)暴漏出来的API。

模块通过变量module.exports导出API,exports只能是一个对象,暴漏的API须作为此对象的属性。

如果require函数引入的模块中也包含依赖,则依次加载这些依赖。

如果引入模块失败,那么require函数应该报一个异常。

1
2
3
4
5
module.exports={

}
// 引入模块
let obj = require(“./模块名’)

AMD规范 — 革新派,RequireJS

AMD : Asynchronous Module Definition。

“异步模块定义”。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

1
2
3
4
5
6
7
8
9
10
11
12
// 定义模块
define(['模块1',’'模块2'’],function(变量1,变量2){
return {
}
});
// 定义入口文件
require([“模块1”,“模块2”],function(变量1,变量2){
//你的代码
});
// 以上均在不同js文件
// html 页面引入入口文件
<script src="js/require.js" data-main="js/main" ></script>

CMD规范 — 中间派,seajs

es6模块化 — 终极解决方案

1
2
3
Import:导入。在自己当前的模块去使用其它模块

Export:导出。在定义模块时,决定把哪些内容暴露出去:当别的模块去引入当前模块,别人会得到什么东西。

 评论


Power by Yuhangxie , 总访问量为 次 。
载入天数...载入时分秒...
京ICP备19024986号