官方网站建设意义,网站建设竞标书,中天建设集团有限公司总部在哪里,网络推广网站推广方法哪些情况会导致内存泄漏
以下四种情况会造成内存的泄漏#xff1a;
意外的全局变量#xff1a; 由于使用未声明的变量#xff0c;而意外的创建了一个全局变量#xff0c;而使这个变量一直留在内存中无法被回收。被遗忘的计时器或回调函数#xff1a; 设置了 setInterval…哪些情况会导致内存泄漏
以下四种情况会造成内存的泄漏
意外的全局变量 由于使用未声明的变量而意外的创建了一个全局变量而使这个变量一直留在内存中无法被回收。被遗忘的计时器或回调函数 设置了 setInterval 定时器而忘记取消它如果循环函数有对外部变量的引用的话那么这个变量会被一直留在内存中而无法被回收。脱离 DOM 的引用 获取一个 DOM 元素的引用而后面这个元素被删除由于一直保留了对这个元素的引用所以它也无法被回收。闭包 不合理的使用闭包从而导致某些变量一直被留在内存当中。
常见的CSS布局单位
常用的布局单位包括像素px百分比%emremvw/vh。
1像素px是页面布局的基础一个像素表示终端电脑、手机、平板等屏幕所能显示的最小的区域像素分为两种类型CSS像素和物理像素
CSS像素为web开发者提供在CSS中使用的一个抽象单位物理像素只与设备的硬件密度有关任何设备的物理像素都是固定的。
2百分比%当浏览器的宽度或者高度发生变化时通过百分比单位可以使得浏览器中的组件的宽和高随着浏览器的变化而变化从而实现响应式的效果。一般认为子元素的百分比相对于直接父元素。
3em和rem相对于px更具灵活性它们都是相对长度单位它们之间的区别em相对于父元素rem相对于根元素。
em 文本相对长度单位。相对于当前对象内文本的字体尺寸。如果当前行内文本的字体尺寸未被人为设置则相对于浏览器的默认字体尺寸(默认16px)。(相对父元素的字体大小倍数)。rem rem是CSS3新增的一个相对单位相对于根元素html元素的font-size的倍数。作用利用rem可以实现简单的响应式布局可以利用html元素中字体的大小与屏幕间的比值来设置font-size的值以此实现当屏幕分辨率变化时让元素也随之变化。
4vw/vh是与视图窗口有关的单位vw表示相对于视图窗口的宽度vh表示相对于视图窗口高度除了vw和vh外还有vmin和vmax两个相关的单位。
vw相对于视窗的宽度视窗宽度是100vwvh相对于视窗的高度视窗高度是100vhvminvw和vh中的较小值vmaxvw和vh中的较大值
vw/vh 和百分比很类似两者的区别
百分比%大部分相对于祖先元素也有相对于自身的情况比如border-radius、translate等)vw/vm相对于视窗的尺寸
position的属性有哪些区别是什么
position有以下属性值
属性值概述absolute生成绝对定位的元素相对于static定位以外的一个父元素进行定位。元素的位置通过left、top、right、bottom属性进行规定。relative生成相对定位的元素相对于其原来的位置进行定位。元素的位置通过left、top、right、bottom属性进行规定。fixed生成绝对定位的元素指定元素相对于屏幕视⼝viewport的位置来指定元素位置。元素的位置在屏幕滚动时不会改变⽐如回到顶部的按钮⼀般都是⽤此定位⽅式。static默认值没有定位元素出现在正常的文档流中会忽略 top, bottom, left, right 或者 z-index 声明块级元素从上往下纵向排布⾏级元素从左向右排列。inherit规定从父元素继承position属性的值
前面三者的定位方式如下 relative 元素的定位永远是相对于元素自身位置的和其他元素没关系也不会影响其他元素。 fixed 元素的定位是相对于 window 或者 iframe边界的和其他元素没有关系。但是它具有破坏性会导致其他元素位置的变化。 absolute 元素的定位相对于前两者要复杂许多。如果为 absolute 设置了 top、left浏览器会根据什么去确定它的纵向和横向的偏移量呢答案是浏览器会递归查找该元素的所有父元素如果找到一个设置了position:relative/absolute/fixed的元素就以该元素为基准定位如果没找到就以浏览器边界定位。如下两个图所示
水平垂直居中的实现
利用绝对定位先将元素的左上角通过top:50%和left:50%定位到页面的中心然后再通过translate来调整元素的中心点到页面的中心。该方法需要考虑浏览器兼容问题。
.parent { position: relative;} .child { position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%);}
利用绝对定位设置四个方向的值都为0并将margin设置为auto由于宽高固定因此对应方向实现平分可以实现水平和垂直方向上的居中。该方法适用于盒子有宽高的情况
.parent {position: relative;
}.child {position: absolute;top: 0;bottom: 0;left: 0;right: 0;margin: auto;
}
利用绝对定位先将元素的左上角通过top:50%和left:50%定位到页面的中心然后再通过margin负值来调整元素的中心点到页面的中心。该方法适用于盒子宽高已知的情况
.parent {position: relative;
}.child {position: absolute;top: 50%;left: 50%;margin-top: -50px; /* 自身 height 的一半 */margin-left: -50px; /* 自身 width 的一半 */
}
使用flex布局通过align-items:center和justify-content:center设置容器的垂直和水平方向上为居中对齐然后它的子元素也可以实现垂直和水平的居中。该方法要考虑兼容的问题该方法在移动端用的较多
.parent {display: flex;justify-content:center;align-items:center;
}
说一下 HTML5 drag API
dragstart事件主体是被拖放元素在开始拖放被拖放元素时触发。darg事件主体是被拖放元素在正在拖放被拖放元素时触发。dragenter事件主体是目标元素在被拖放元素进入某元素时触发。dragover事件主体是目标元素在被拖放在某元素内移动时触发。dragleave事件主体是目标元素在被拖放元素移出目标元素是触发。drop事件主体是目标元素在目标元素完全接受被拖放元素时触发。dragend事件主体是被拖放元素在整个拖放操作结束时触发。
设置小于12px的字体
在谷歌下css设置字体大小为12px及以下时显示都是一样大小都是默认12px。
解决办法
使用Webkit的内核的-webkit-text-size-adjust的私有CSS属性来解决只要加了-webkit-text-size-adjust:none;字体大小就不受限制了。但是chrome更新到27版本之后就不可以用了。所以高版本chrome谷歌浏览器已经不再支持-webkit-text-size-adjust样式所以要使用时候慎用。使用css3的transform缩放属性-webkit-transform:scale(0.5); 注意-webkit-transform:scale(0.75);收缩的是整个元素的大小这时候如果是内联元素必须要将内联元素转换成块元素可以使用displayblock/inline-block/…使用图片如果是内容固定不变情况下使用将小于12px文字内容切出做图片这样不影响兼容也不影响美观。
参考 前端进阶面试题详细解答
link和import的区别
两者都是外部引用CSS的方式它们的区别如下
link是XHTML标签除了加载CSS外还可以定义RSS等其他事务import属于CSS范畴只能加载CSS。link引用CSS时在页面载入时同时加载import需要页面网页完全载入以后加载。link是XHTML标签无兼容问题import是在CSS2.1提出的低版本的浏览器不支持。link支持使用Javascript控制DOM去改变样式而import不支持。
对BFC的理解如何创建BFC
先来看两个相关的概念
Box: Box 是 CSS 布局的对象和基本单位⼀个⻚⾯是由很多个 Box 组成的这个Box就是我们所说的盒模型。Formatting context块级上下⽂格式化它是⻚⾯中的⼀块渲染区域并且有⼀套渲染规则它决定了其⼦元素将如何定位以及和其他元素的关系和相互作⽤。
块格式化上下文Block Formatting ContextBFC是Web页面的可视化CSS渲染的一部分是布局过程中生成块级盒子的区域也是浮动元素与其他元素的交互限定区域。
通俗来讲BFC是一个独立的布局环境可以理解为一个容器在这个容器中按照一定规则进行物品摆放并且不会影响其它环境中的物品。如果一个元素符合触发BFC的条件则BFC中的元素布局不受外部影响。
创建BFC的条件
根元素body元素设置浮动float 除 none 以外的值元素设置绝对定位position (absolute、fixed)display 值为inline-block、table-cell、table-caption、flex等overflow 值为hidden、auto、scroll
BFC的特点
垂直方向上自上而下排列和文档流的排列方式一致。在BFC中上下相邻的两个容器的margin会重叠计算BFC的高度时需要计算浮动元素的高度BFC区域不会与浮动的容器发生重叠BFC是独立的容器容器内部元素不会影响外部元素每个元素的左margin值和容器的左border相接触
BFC的作用
解决margin的重叠问题由于BFC是一个独立的区域内部的元素和外部的元素互不影响将两个元素变为两个BFC就解决了margin重叠的问题。解决高度塌陷的问题在对子元素设置浮动后父元素会发生高度塌陷也就是父元素的高度变为0。解决这个问题只需要把父元素变成一个BFC。常用的办法是给父元素设置overflow:hidden。创建自适应两栏布局可以用来创建自适应两栏布局左边的宽度固定右边的宽度自适应。
.left{width: 100px;height: 200px;background: red;float: left;}.right{height: 300px;background: blue;overflow: hidden;}div classleft/div
div classright/div
左侧设置float:left右侧设置overflow: hidden。这样右边就触发了BFCBFC的区域不会与浮动元素发生重叠所以两侧就不会发生重叠实现了自适应两栏布局。
DOM 节点操作
1创建新节点
createDocumentFragment() //创建一个DOM片段
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点2添加、移除、替换、插入
appendChild(node)
removeChild(node)
replaceChild(new,old)
insertBefore(new,old)3查找
getElementById();
getElementsByName();
getElementsByTagName();
getElementsByClassName();
querySelector();
querySelectorAll();4属性操作
getAttribute(key);
setAttribute(key, value);
hasAttribute(key);
removeAttribute(key);CSS动画和过渡
animation / keyframes
animation-name: 动画名称对应keyframesanimation-duration: 间隔animation-timing-function: 曲线animation-delay: 延迟animation-iteration-count: 次数 infinite: 循环动画 animation-direction: 方向 alternate: 反向播放 animation-fill-mode: 静止模式 forwards: 停止时保留最后一帧backwards: 停止时回到第一帧both: 同时运用 forwards / backwards 常用钩子: animationend 动画属性: 尽量使用动画属性进行动画能拥有较好的性能表现 translatescalerotateskewopacitycolor
transform
位移属性 translate( x , y )旋转属性 rotate()缩放属性 scale()倾斜属性 skew()
transition
transition-property过渡的属性的名称。transition-duration定义过渡效果花费的时间,默认是 0。transition-timing-function:linear(匀速) ease(慢速开始然后变快然后慢速结束)规定过渡效果的时间曲线最常用的是这两个。transition-delay规定过渡效果何时开始。默认是 0 般情况下我们都是写一起的比如transition width 2s ease 1s 关键帧动画animation 一个关键帧动画最少包含两部分animation 属性及属性值动画的名称和运行方式运行时间等。keyframes规定动画的具体实现过程 animation 属性可以拆分为
animation-name 规定keyframes 动画的名称。animation-duration 规定动画完成一个周期所花费的秒或毫秒。默认是 0。animation-timing-function 规定动画的速度曲线。默认是 “ease”常用的还有linear同transtion 。animation-delay 规定动画何时开始。默认是 0。animation-iteration-count 规定动画被播放的次数。默认是 1但我们一般用infinite一直播放 而keyframes的使用方法可以是from-to等同于0%和100%也可以是从0%-100%之间任意个的分层设置。我们通过下面一个稍微复杂点的demo来看一下基本上用到了上面说到的大部分知识 eg:keyframes mymove{from {top:0px;}to {top:200px;}}/* 等同于 */keyframes mymove
{0% {top:0px;}25% {top:200px;}50% {top:100px;}75% {top:200px;}100% {top:0px;}
}用css3动画使一个图片旋转
#loader {display: block;position: relative;-webkit-animation: spin 2s linear infinite;animation: spin 2s linear infinite;}-webkit-keyframes spin {0% {-webkit-transform: rotate(0deg);-ms-transform: rotate(0deg);transform: rotate(0deg);}100% {-webkit-transform: rotate(360deg);-ms-transform: rotate(360deg);transform: rotate(360deg);}}keyframes spin {0% {-webkit-transform: rotate(0deg);-ms-transform: rotate(0deg);transform: rotate(0deg);}100% {-webkit-transform: rotate(360deg);-ms-transform: rotate(360deg);transform: rotate(360deg);}}template预编译是什么
对于 Vue 组件来说模板编译只会在组件实例化的时候编译一次生成渲染函数之后在也不会进行编译。因此编译对组件的 runtime 是一种性能损耗。 而模板编译的目的仅仅是将template转化为render function这个过程正好可以在项目构建的过程中完成这样可以让实际组件在 runtime 时直接跳过模板渲染进而提升性能这个在项目构建的编译template的过程就是预编译。 viewport
meta nameviewport contentwidthdevice-width,initial-scale1.0,minimum-scale1.0,maximum-scale1.0,user-scalableno /// width 设置viewport宽度为一个正整数或字符串‘device-width’// device-width 设备宽度// height 设置viewport高度一般设置了宽度会自动解析出高度可以不用设置// initial-scale 默认缩放比例初始缩放比例为一个数字可以带小数// minimum-scale 允许用户最小缩放比例为一个数字可以带小数// maximum-scale 允许用户最大缩放比例为一个数字可以带小数// user-scalable 是否允许手动缩放延伸提问 怎样处理 移动端 1px 被 渲染成 2px问题
局部处理
meta标签中的 viewport属性 initial-scale 设置为 1rem按照设计稿标准走外加利用transfrome 的scale(0.5) 缩小一倍即可
全局处理
mate标签中的 viewport属性 initial-scale 设置为 0.5rem 按照设计稿标准走即可
深浅拷贝 1. 浅拷贝的原理和实现 自己创建一个新的对象来接受你要重新复制或引用的对象值。如果对象属性是基本的数据类型复制的就是基本类型的值给新对象但如果属性是引用数据类型复制的就是内存中的地址如果其中一个对象改变了这个内存中的地址肯定会影响到另一个对象 方法一object.assign object.assign是 ES6 中 object 的一个方法该方法可以用于 JS 对象的合并等多个用途其中一个用途就是可以进行浅拷贝。该方法的第一个参数是拷贝的目标对象后面的参数是拷贝的来源对象也可以是多个来源。 object.assign 的语法为Object.assign(target, ...sources)object.assign 的示例代码如下
let target {};
let source { a: { b: 1 } };
Object.assign(target, source);
console.log(target); // { a: { b: 1 } };但是使用 object.assign 方法有几点需要注意
它不会拷贝对象的继承属性它不会拷贝对象的不可枚举的属性可以拷贝 Symbol 类型的属性。
let obj1 { a:{ b:1 }, sym:Symbol(1)};
Object.defineProperty(obj1, innumerable ,{value:不可枚举属性,enumerable:false
});
let obj2 {};
Object.assign(obj2,obj1)
obj1.a.b 2;
console.log(obj1,obj1);
console.log(obj2,obj2);从上面的样例代码中可以看到利用 object.assign 也可以拷贝 Symbol 类型的对象但是如果到了对象的第二层属性 obj1.a.b 这里的时候前者值的改变也会影响后者的第二层属性的值说明其中依旧存在着访问共同堆内存的问题也就是说这种方法还不能进一步复制而只是完成了浅拷贝的功能 方法二扩展运算符方式
我们也可以利用 JS 的扩展运算符在构造对象的同时完成浅拷贝的功能。扩展运算符的语法为let cloneObj { ...obj };
/* 对象的拷贝 */
let obj {a:1,b:{c:1}}
let obj2 {...obj}
obj.a 2
console.log(obj) //{a:2,b:{c:1}} console.log(obj2); //{a:1,b:{c:1}}
obj.b.c 2
console.log(obj) //{a:2,b:{c:2}} console.log(obj2); //{a:1,b:{c:2}}
/* 数组的拷贝 */
let arr [1, 2, 3];
let newArr [...arr]; //跟arr.slice()是一样的效果扩展运算符 和 object.assign 有同样的缺陷也就是实现的浅拷贝的功能差不多但是如果属性都是基本类型的值使用扩展运算符进行浅拷贝会更加方便 方法三concat 拷贝数组 数组的 concat 方法其实也是浅拷贝所以连接一个含有引用类型的数组时需要注意修改原数组中的元素的属性因为它会影响拷贝之后连接的数组。不过 concat 只能用于数组的浅拷贝使用场景比较局限。代码如下所示。 let arr [1, 2, 3];
let newArr arr.concat();
newArr[1] 100;
console.log(arr); // [ 1, 2, 3 ]
console.log(newArr); // [ 1, 100, 3 ]方法四slice 拷贝数组 slice 方法也比较有局限性因为它仅仅针对数组类型。slice方法会返回一个新的数组对象这一对象由该方法的前两个参数来决定原数组截取的开始和结束时间是不会影响和改变原始数组的。 slice 的语法为arr.slice(begin, end);let arr [1, 2, {val: 4}];
let newArr arr.slice();
newArr[2].val 1000;
console.log(arr); //[ 1, 2, { val: 1000 } ]从上面的代码中可以看出这就是浅拷贝的限制所在了——它只能拷贝一层对象。如果存在对象的嵌套那么浅拷贝将无能为力。因此深拷贝就是为了解决这个问题而生的它能解决多层对象嵌套问题彻底实现拷贝 手工实现一个浅拷贝
根据以上对浅拷贝的理解如果让你自己实现一个浅拷贝大致的思路分为两点
对基础类型做一个最基本的一个拷贝对引用类型开辟一个新的存储并且拷贝一层对象属性。
const shallowClone (target) {if (typeof target object target ! null) {const cloneTarget Array.isArray(target) ? []: {};for (let prop in target) {if (target.hasOwnProperty(prop)) {cloneTarget[prop] target[prop];}}return cloneTarget;} else {return target;}
}利用类型判断针对引用类型的对象进行 for 循环遍历对象属性赋值给目标对象的属性基本就可以手工实现一个浅拷贝的代码了 2. 深拷贝的原理和实现
浅拷贝只是创建了一个新的对象复制了原有对象的基本类型的值而引用数据类型只拷贝了一层属性再深层的还是无法进行拷贝。深拷贝则不同对于复杂引用数据类型其在堆内存中完全开辟了一块内存地址并将原有的对象完全复制过来存放。
这两个对象是相互独立、不受影响的彻底实现了内存上的分离。总的来说深拷贝的原理可以总结如下 将一个对象从内存中完整地拷贝出来一份给目标对象并从堆内存中开辟一个全新的空间存放新对象且新对象的修改并不会改变原对象二者实现真正的分离。 方法一乞丐版JSON.stringify JSON.stringify() 是目前开发过程中最简单的深拷贝方法其实就是把一个对象序列化成为 JSON 的字符串并将对象里面的内容转换成字符串最后再用 JSON.parse() 的方法将 JSON 字符串生成一个新的对象 let a {age: 1,jobs: {first: FE}
}
let b JSON.parse(JSON.stringify(a))
a.jobs.first native
console.log(b.jobs.first) // FE但是该方法也是有局限性的
会忽略 undefined会忽略 symbol不能序列化函数无法拷贝不可枚举的属性无法拷贝对象的原型链拷贝 RegExp 引用类型会变成空对象拷贝 Date 引用类型会变成字符串对象中含有 NaN、Infinity 以及 -InfinityJSON 序列化的结果会变成 null不能解决循环引用的对象即对象成环 (obj[key] obj)。
function Obj() { this.func function () { alert(1) }; this.obj {a:1};this.arr [1,2,3];this.und undefined; this.reg /123/; this.date new Date(0); this.NaN NaN;this.infinity Infinity;this.sym Symbol(1);
}
let obj1 new Obj();
Object.defineProperty(obj1,innumerable,{ enumerable:false,value:innumerable
});
console.log(obj1,obj1);
let str JSON.stringify(obj1);
let obj2 JSON.parse(str);
console.log(obj2,obj2);使用 JSON.stringify 方法实现深拷贝对象虽然到目前为止还有很多无法实现的功能但是这种方法足以满足日常的开发需求并且是最简单和快捷的。而对于其他的也要实现深拷贝的比较麻烦的属性对应的数据类型JSON.stringify 暂时还是无法满足的那么就需要下面的几种方法了 方法二基础版手写递归实现 下面是一个实现 deepClone 函数封装的例子通过 for in 遍历传入参数的属性值如果值是引用类型则再次递归调用该函数如果是基础数据类型就直接复制 let obj1 {a:{b:1}
}
function deepClone(obj) { let cloneObj {}for(let key in obj) { //遍历if(typeof obj[key] object) { cloneObj[key] deepClone(obj[key]) //是对象就再次调用该函数递归} else {cloneObj[key] obj[key] //基本类型的话直接复制值}}return cloneObj
}
let obj2 deepClone(obj1);
obj1.a.b 2;
console.log(obj2); // {a:{b:1}}虽然利用递归能实现一个深拷贝但是同上面的 JSON.stringify 一样还是有一些问题没有完全解决例如
这个深拷贝函数并不能复制不可枚举的属性以及 Symbol 类型这种方法只是针对普通的引用类型的值做递归复制而对于 Array、Date、RegExp、Error、Function 这样的引用类型并不能正确地拷贝对象的属性里面成环即循环引用没有解决。
这种基础版本的写法也比较简单可以应对大部分的应用情况。但是你在面试的过程中如果只能写出这样的一个有缺陷的深拷贝方法有可能不会通过。
所以为了“拯救”这些缺陷下面我带你一起看看改进的版本以便于你可以在面试种呈现出更好的深拷贝方法赢得面试官的青睐。
方法三改进版改进后递归实现 针对上面几个待解决问题我先通过四点相关的理论告诉你分别应该怎么做。 针对能够遍历对象的不可枚举属性以及 Symbol 类型我们可以使用 Reflect.ownKeys 方法当参数为 Date、RegExp 类型则直接生成一个新的实例返回利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性以及对应的特性顺便结合 Object.create 方法创建一个新对象并继承传入原对象的原型链利用 WeakMap 类型作为 Hash 表因为 WeakMap 是弱引用类型可以有效防止内存泄漏你可以关注一下 Map 和 weakMap 的关键区别这里要用 weakMap作为检测循环引用很有帮助如果存在循环则引用直接返回 WeakMap 存储的值
如果你在考虑到循环引用的问题之后还能用 WeakMap 来很好地解决并且向面试官解释这样做的目的那么你所展示的代码以及你对问题思考的全面性在面试官眼中应该算是合格的了
实现深拷贝
const isComplexDataType obj (typeof obj object || typeof obj function) (obj ! null)const deepClone function (obj, hash new WeakMap()) {if (obj.constructor Date) {return new Date(obj) // 日期对象直接返回一个新的日期对象}if (obj.constructor RegExp){return new RegExp(obj) //正则对象直接返回一个新的正则对象}//如果循环引用了就用 weakMap 来解决if (hash.has(obj)) {return hash.get(obj)}let allDesc Object.getOwnPropertyDescriptors(obj)//遍历传入参数所有键的特性let cloneObj Object.create(Object.getPrototypeOf(obj), allDesc)// 把cloneObj原型复制到obj上hash.set(obj, cloneObj)for (let key of Reflect.ownKeys(obj)) { cloneObj[key] (isComplexDataType(obj[key]) typeof obj[key] ! function) ? deepClone(obj[key], hash) : obj[key]}return cloneObj
}// 下面是验证代码
let obj {num: 0,str: ,boolean: true,unf: undefined,nul: null,obj: { name: 我是一个对象, id: 1 },arr: [0, 1, 2],func: function () { console.log(我是一个函数) },date: new Date(0),reg: new RegExp(/我是一个正则/ig),[Symbol(1)]: 1,
};
Object.defineProperty(obj, innumerable, {enumerable: false, value: 不可枚举属性 }
);
obj Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop obj // 设置loop成循环引用的属性
let cloneObj deepClone(obj)
cloneObj.arr.push(4)
console.log(obj, obj)
console.log(cloneObj, cloneObj)我们看一下结果cloneObj 在 obj 的基础上进行了一次深拷贝cloneObj 里的 arr 数组进行了修改并未影响到 obj.arr 的变化如下图所示 TCP粘包是怎么回事如何处理?
默认情况下, TCP 连接会启⽤延迟传送算法 (Nagle 算法), 在数据发送之前缓存他们. 如果短时间有多个数据发送, 会缓冲到⼀起作⼀次发送 (缓冲⼤⼩⻅ socket.bufferSize ), 这样可以减少 IO 消耗提⾼性能.
如果是传输⽂件的话, 那么根本不⽤处理粘包的问题, 来⼀个包拼⼀个包就好了。但是如果是多条消息, 或者是别的⽤途的数据那么就需要处理粘包.
下面看⼀个例⼦, 连续调⽤两次 send 分别发送两段数据 data1 和 data2, 在接收端有以下⼏种常⻅的情况: A. 先接收到 data1, 然后接收到 data2 . B. 先接收到 data1 的部分数据, 然后接收到 data1 余下的部分以及 data2 的全部. C. 先接收到了 data1 的全部数据和 data2 的部分数据, 然后接收到了 data2 的余下的数据. D. ⼀次性接收到了 data1 和 data2 的全部数据.
其中的 BCD 就是我们常⻅的粘包的情况. ⽽对于处理粘包的问题, 常⻅的解决⽅案有:
多次发送之前间隔⼀个等待时间只需要等上⼀段时间再进⾏下⼀次 send 就好, 适⽤于交互频率特别低的场景. 缺点也很明显, 对于⽐较频繁的场景⽽⾔传输效率实在太低不过⼏乎不⽤做什么处理.关闭 Nagle 算法关闭 Nagle 算法, 在 Node.js 中你可以通过 socket.setNoDelay() ⽅法来关闭 Nagle 算法, 让每⼀次 send 都不缓冲直接发送。该⽅法⽐较适⽤于每次发送的数据都⽐较⼤ (但不是⽂件那么⼤), 并且频率不是特别⾼的场景。如果是每次发送的数据量⽐较⼩, 并且频率特别⾼的, 关闭 Nagle 纯属⾃废武功。另外, 该⽅法不适⽤于⽹络较差的情况, 因为 Nagle 算法是在服务端进⾏的包合并情况, 但是如果短时间内客户端的⽹络情况不好, 或者应⽤层由于某些原因不能及时将 TCP 的数据 recv, 就会造成多个包在客户端缓冲从⽽粘包的情况。 (如果是在稳定的机房内部通信那么这个概率是⽐较⼩可以选择忽略的)进⾏封包/拆包 封包/拆包是⽬前业内常⻅的解决⽅案了。即给每个数据包在发送之前, 于其前/后放⼀些有特征的数据, 然后收到数据的时 候根据特征数据分割出来各个数据包。
Proxy代理 proxy在目标对象的外层搭建了一层拦截外界对目标对象的某些操作必须通过这层拦截 var proxy new Proxy(target, handler);new Proxy()表示生成一个Proxy实例target参数表示所要拦截的目标对象handler参数也是一个对象用来定制拦截行为 var target {name: poetries};var logHandler {get: function(target, key) {console.log(${key} 被读取);return target[key];},set: function(target, key, value) {console.log(${key} 被设置为 ${value});target[key] value;}}var targetWithLog new Proxy(target, logHandler);targetWithLog.name; // 控制台输出name 被读取targetWithLog.name others; // 控制台输出name 被设置为 othersconsole.log(target.name); // 控制台输出: otherstargetWithLog 读取属性的值时实际上执行的是 logHandler.get 在控制台输出信息并且读取被代理对象 target 的属性。在 targetWithLog 设置属性值时实际上执行的是 logHandler.set 在控制台输出信息并且设置被代理对象 target 的属性的值
// 由于拦截函数总是返回35所以访问任何属性都得到35
var proxy new Proxy({}, {get: function(target, property) {return 35;}
});proxy.time // 35
proxy.name // 35
proxy.title // 35Proxy 实例也可以作为其他对象的原型对象
var proxy new Proxy({}, {get: function(target, property) {return 35;}
});let obj Object.create(proxy);
obj.time // 35proxy对象是obj对象的原型obj对象本身并没有time属性所以根据原型链会在proxy对象上读取该属性导致被拦截 Proxy的作用 对于代理模式 Proxy 的作用主要体现在三个方面 拦截和监视外部对对象的访问降低函数或类的复杂度在复杂操作前对操作进行校验或对所需资源进行管理
Proxy所能代理的范围–handler 实际上 handler 本身就是ES6所新设计的一个对象.它的作用就是用来 自定义代理对象的各种可代理操作 。它本身一共有13中方法,每种方法都可以代理一种操作.其13种方法如下 // 在读取代理对象的原型时触发该操作比如在执行 Object.getPrototypeOf(proxy) 时。
handler.getPrototypeOf()// 在设置代理对象的原型时触发该操作比如在执行 Object.setPrototypeOf(proxy, null) 时。
handler.setPrototypeOf()// 在判断一个代理对象是否是可扩展时触发该操作比如在执行 Object.isExtensible(proxy) 时。
handler.isExtensible()// 在让一个代理对象不可扩展时触发该操作比如在执行 Object.preventExtensions(proxy) 时。
handler.preventExtensions()// 在获取代理对象某个属性的属性描述时触发该操作比如在执行 Object.getOwnPropertyDescriptor(proxy, foo) 时。
handler.getOwnPropertyDescriptor()// 在定义代理对象某个属性时的属性描述时触发该操作比如在执行 Object.defineProperty(proxy, foo, {}) 时。
andler.defineProperty()// 在判断代理对象是否拥有某个属性时触发该操作比如在执行 foo in proxy 时。
handler.has()// 在读取代理对象的某个属性时触发该操作比如在执行 proxy.foo 时。
handler.get()// 在给代理对象的某个属性赋值时触发该操作比如在执行 proxy.foo 1 时。
handler.set()// 在删除代理对象的某个属性时触发该操作比如在执行 delete proxy.foo 时。
handler.deleteProperty()// 在获取代理对象的所有属性键时触发该操作比如在执行 Object.getOwnPropertyNames(proxy) 时。
handler.ownKeys()// 在调用一个目标对象为函数的代理对象时触发该操作比如在执行 proxy() 时。
handler.apply()// 在给一个目标对象为构造函数的代理对象构造实例时触发该操作比如在执行new proxy() 时。
handler.construct()为何Proxy不能被Polyfill
如class可以用function模拟promise可以用callback模拟但是proxy不能用Object.defineProperty模拟 目前谷歌的polyfill只能实现部分的功能如get、set https://github.com/GoogleChrome/proxy-polyfill // commonJS require
const proxyPolyfill require(proxy-polyfill/src/proxy)();// Your environment may also support transparent rewriting of commonJS to ES6:
import ProxyPolyfillBuilder from proxy-polyfill/src/proxy;
const proxyPolyfill ProxyPolyfillBuilder();// Then use...
const myProxy new proxyPolyfill(...);HTTP状态码
状态码的类别
类别原因描述1xxInformational(信息性状态码)接受的请求正在处理2xxSuccess(成功状态码)请求正常处理完毕3xxRedirection(重定向状态码)需要进行附加操作一完成请求4xxClient Error (客户端错误状态码)服务器无法处理请求5xxServer Error(服务器错误状态码)服务器处理请求出错
1. 2XX (Success 成功状态码)
状态码2XX表示请求被正常处理了。
1200 OK
200 OK表示客户端发来的请求被服务器端正常处理了。
2204 No Content
该状态码表示客户端发送的请求已经在服务器端正常处理了但是没有返回的内容响应报文中不包含实体的主体部分。一般在只需要从客户端往服务器端发送信息而服务器端不需要往客户端发送内容时使用。
3206 Partial Content
该状态码表示客户端进行了范围请求而服务器端执行了这部分的 GET 请求。响应报文中包含由 Content-Range 指定范围的实体内容。
2. 3XX (Redirection 重定向状态码)
3XX 响应结果表明浏览器需要执行某些特殊的处理以正确处理请求。
1301 Moved Permanently
永久重定向。 该状态码表示请求的资源已经被分配了新的 URI以后应使用资源指定的 URI。新的 URI 会在 HTTP 响应头中的 Location 首部字段指定。若用户已经把原来的URI保存为书签此时会按照 Location 中新的URI重新保存该书签。同时搜索引擎在抓取新内容的同时也将旧的网址替换为重定向之后的网址。
使用场景
当我们想换个域名旧的域名不再使用时用户访问旧域名时用301就重定向到新的域名。其实也是告诉搜索引擎收录的域名需要对新的域名进行收录。在搜索引擎的搜索结果中出现了不带www的域名而带www的域名却没有收录这个时候可以用301重定向来告诉搜索引擎我们目标的域名是哪一个。
2302 Found
临时重定向。 该状态码表示请求的资源被分配到了新的 URI希望用户本次能使用新的 URI 访问资源。和 301 Moved Permanently 状态码相似但是 302 代表的资源不是被永久重定向只是临时性质的。也就是说已移动的资源对应的 URI 将来还有可能发生改变。若用户把 URI 保存成书签但不会像 301 状态码出现时那样去更新书签而是仍旧保留返回 302 状态码的页面对应的 URI。同时搜索引擎会抓取新的内容而保留旧的网址。因为服务器返回302代码搜索引擎认为新的网址只是暂时的。
使用场景
当我们在做活动时登录到首页自动重定向进入活动页面。未登陆的用户访问用户中心重定向到登录页面。访问404页面重新定向到首页。
3303 See Other
该状态码表示由于请求对应的资源存在着另一个 URI应使用 GET 方法定向获取请求的资源。 303 状态码和 302 Found 状态码有着相似的功能但是 303 状态码明确表示客户端应当采用 GET 方法获取资源。
303 状态码通常作为 PUT 或 POST 操作的返回结果它表示重定向链接指向的不是新上传的资源而是另外一个页面比如消息确认页面或上传进度页面。而请求重定向页面的方法要总是使用 GET。
注意
当 301、302、303 响应状态码返回时几乎所有的浏览器都会把 POST 改成GET并删除请求报文内的主体之后请求会再次自动发送。301、302 标准是禁止将 POST 方法变成 GET方法的但实际大家都会这么做。
4304 Not Modified
浏览器缓存相关。 该状态码表示客户端发送附带条件的请求时服务器端允许请求访问资源但未满足条件的情况。304 状态码返回时不包含任何响应的主体部分。304 虽然被划分在 3XX 类别中但是和重定向没有关系。
带条件的请求Http 条件请求使用 Get方法 请求请求报文中包含if-match、if-none-match、if-modified-since、if-unmodified-since、if-range中任意首部。
状态码304并不是一种错误而是告诉客户端有缓存直接使用缓存中的数据。返回页面的只有头部信息是没有内容部分的这样在一定程度上提高了网页的性能。
5307 Temporary Redirect
307表示临时重定向。 该状态码与 302 Found 有着相同含义尽管 302 标准禁止 POST 变成 GET但是实际使用时还是这样做了。
307 会遵守浏览器标准不会从 POST 变成 GET。但是对于处理请求的行为时不同浏览器还是会出现不同的情况。规范要求浏览器继续向 Location 的地址 POST 内容。规范要求浏览器继续向 Location 的地址 POST 内容。
3. 4XX (Client Error 客户端错误状态码)
4XX 的响应结果表明客户端是发生错误的原因所在。
1400 Bad Request
该状态码表示请求报文中存在语法错误。当错误发生时需修改请求的内容后再次发送请求。另外浏览器会像 200 OK 一样对待该状态码。
2401 Unauthorized
该状态码表示发送的请求需要有通过 HTTP 认证(BASIC 认证、DIGEST 认证)的认证信息。若之前已进行过一次请求则表示用户认证失败
返回含有 401 的响应必须包含一个适用于被请求资源的 WWW-Authenticate 首部用以质询(challenge)用户信息。当浏览器初次接收到 401 响应会弹出认证用的对话窗口。
以下情况会出现401
401.1 - 登录失败。401.2 - 服务器配置导致登录失败。401.3 - 由于 ACL 对资源的限制而未获得授权。401.4 - 筛选器授权失败。401.5 - ISAPI/CGI 应用程序授权失败。401.7 - 访问被 Web 服务器上的 URL 授权策略拒绝。这个错误代码为 IIS 6.0 所专用。
3403 Forbidden
该状态码表明请求资源的访问被服务器拒绝了服务器端没有必要给出详细理由但是可以在响应报文实体的主体中进行说明。进入该状态后不能再继续进行验证。该访问是永久禁止的并且与应用逻辑密切相关。
IIS 定义了许多不同的 403 错误它们指明更为具体的错误原因
403.1 - 执行访问被禁止。403.2 - 读访问被禁止。403.3 - 写访问被禁止。403.4 - 要求 SSL。403.5 - 要求 SSL 128。403.6 - IP 地址被拒绝。403.7 - 要求客户端证书。403.8 - 站点访问被拒绝。403.9 - 用户数过多。403.10 - 配置无效。403.11 - 密码更改。403.12 - 拒绝访问映射表。403.13 - 客户端证书被吊销。403.14 - 拒绝目录列表。403.15 - 超出客户端访问许可。403.16 - 客户端证书不受信任或无效。403.17 - 客户端证书已过期或尚未生效403.18 - 在当前的应用程序池中不能执行所请求的 URL。这个错误代码为 IIS 6.0 所专用。403.19 - 不能为这个应用程序池中的客户端执行 CGI。这个错误代码为 IIS 6.0 所专用。403.20 - Passport 登录失败。这个错误代码为 IIS 6.0 所专用。
4404 Not Found
该状态码表明服务器上无法找到请求的资源。除此之外也可以在服务器端拒绝请求且不想说明理由时使用。 以下情况会出现404
404.0 -无 – 没有找到文件或目录。404.1 - 无法在所请求的端口上访问 Web 站点。404.2 - Web 服务扩展锁定策略阻止本请求。404.3 - MIME 映射策略阻止本请求。
5405 Method Not Allowed
该状态码表示客户端请求的方法虽然能被服务器识别但是服务器禁止使用该方法。GET 和 HEAD 方法服务器应该总是允许客户端进行访问。客户端可以通过 OPTIONS 方法预检来查看服务器允许的访问方法, 如下
Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
4. 5XX (Server Error 服务器错误状态码)
5XX 的响应结果表明服务器本身发生错误.
1500 Internal Server Error
该状态码表明服务器端在执行请求时发生了错误。也有可能是 Web 应用存在的 bug 或某些临时的故障。
2502 Bad Gateway
该状态码表明扮演网关或代理角色的服务器从上游服务器中接收到的响应是无效的。注意502 错误通常不是客户端能够修复的而是需要由途经的 Web 服务器或者代理服务器对其进行修复。以下情况会出现502
502.1 - CGI 通用网关接口应用程序超时。502.2 - CGI 通用网关接口应用程序出错。
3503 Service Unavailable
该状态码表明服务器暂时处于超负载或正在进行停机维护现在无法处理请求。如果事先得知解除以上状况需要的时间最好写入 RetryAfter 首部字段再返回给客户端。
使用场景
服务器停机维护时主动用503响应请求nginx 设置限速超过限速会返回503。
4504 Gateway Timeout
该状态码表示网关或者代理的服务器无法在规定的时间内获得想要的响应。他是HTTP 1.1中新加入的。
使用场景代码执行时间超时或者发生了死循环。
5. 总结
12XX 成功
200 OK表示从客户端发来的请求在服务器端被正确处理204 No content表示请求成功但响应报文不含实体的主体部分205 Reset Content表示请求成功但响应报文不含实体的主体部分但是与 204 响应不同在于要求请求方重置内容206 Partial Content进行范围请求
23XX 重定向
301 moved permanently永久性重定向表示资源已被分配了新的 URL302 found临时性重定向表示资源临时被分配了新的 URL303 see other表示资源存在着另一个 URL应使用 GET 方法获取资源304 not modified表示服务器允许访问资源但因发生请求未满足条件的情况307 temporary redirect临时重定向和302含义类似但是期望客户端保持请求方法不变向新的地址发出请求
34XX 客户端错误
400 bad request请求报文存在语法错误401 unauthorized表示发送的请求需要有通过 HTTP 认证的认证信息403 forbidden表示对请求资源的访问被服务器拒绝404 not found表示在服务器上没有找到请求的资源
45XX 服务器错误
500 internal sever error表示服务器端在执行请求时发生了错误501 Not Implemented表示服务器不支持当前请求所需要的某个功能503 service unavailable表明服务器暂时处于超负载或正在停机维护无法处理请求
执行上下文 当执行 JS 代码时会产生三种执行上下文 全局执行上下文函数执行上下文eval 执行上下文 每个执行上下文中都有三个重要的属性 变量对象VO包含变量、函数声明和函数的形参该属性只能在全局上下文中访问作用域链JS 采用词法作用域也就是说变量的作用域是在定义时就决定了this
var a 10
function foo(i) {var b 20
}
foo()对于上述代码执行栈中有两个上下文全局上下文和函数 foo 上下文。 stack [globalContext,fooContext
]对于全局上下文来说VO大概是这样的 globalContext.VO globe
globalContext.VO {a: undefined,foo: Function,
}对于函数 foo 来说VO 不能访问只能访问到活动对象AO fooContext.VO foo.AO
fooContext.AO {i: undefined,b: undefined,arguments:
}
// arguments 是函数独有的对象(箭头函数没有)
// 该对象是一个伪数组有 length 属性且可以通过下标访问元素
// 该对象中的 callee 属性代表函数本身
// caller 属性代表函数的调用者对于作用域链可以把它理解成包含自身变量对象和上级变量对象的列表通过 [[Scope]]属性查找上级变量 fooContext.[[Scope]] [globalContext.VO
]
fooContext.Scope fooContext.[[Scope]] fooContext.VO
fooContext.Scope [fooContext.VO,globalContext.VO
]接下来让我们看一个老生常谈的例子var b() // call b
console.log(a) // undefinedvar a Hello worldfunction b() {console.log(call b)
}想必以上的输出大家肯定都已经明白了这是因为函数和变量提升的原因。通常提升的解释是说将声明的代码移动到了顶部这其实没有什么错误便于大家理解。但是更准确的解释应该是在生成执行上下文时会有两个阶段。第一个阶段是创建的阶段具体步骤是创建 VOJS 解释器会找出需要提升的变量和函数并且给他们提前在内存中开辟好空间函数的话会将整个函数存入内存中变量只声明并且赋值为 undefined所以在第二个阶段也就是代码执行阶段我们可以直接提前使用。 在提升的过程中相同的函数会覆盖上一个函数并且函数优先于变量提升
b() // call b secondfunction b() {console.log(call b fist)
}
function b() {console.log(call b second)
}
var b Hello worldvar会产生很多错误所以在 ES6中引入了 let。let不能在声明前使用但是这并不是常说的 let 不会提升let 提升了声明但没有赋值因为临时死区导致了并不能在声明前使用。 对于非匿名的立即执行函数需要注意以下一点
var foo 1
(function foo() {foo 10console.log(foo)
}()) // - ƒ foo() { foo 10 ; console.log(foo) }因为当 JS 解释器在遇到非匿名的立即执行函数时会创建一个辅助的特定对象然后将函数名称作为这个对象的属性因此函数内部才可以访问到 foo但是这个值又是只读的所以对它的赋值并不生效所以打印的结果还是这个函数并且外部的值也没有发生更改。 specialObject {};Scope specialObject Scope;foo new FunctionExpression;
foo.[[Scope]] Scope;
specialObject.foo foo; // {DontDelete}, {ReadOnly}delete Scope[0]; // remove specialObject from the front of scope chain总结 执行上下文可以简单理解为一个对象: 它包含三个部分:
变量对象(VO)作用域链(词法作用域)this指向
它的类型:
全局执行上下文函数执行上下文eval执行上下文
代码执行过程:
创建 全局上下文 (global EC)全局执行上下文 (caller) 逐行 自上而下 执行。遇到函数时函数执行上下文 (callee) 被push到执行栈顶层函数执行上下文被激活成为 active EC, 开始执行函数中的代码caller 被挂起函数执行完后callee 被pop移除出执行栈控制权交还全局上下文 (caller)继续执行
介绍一下 Tree Shaking
对tree-shaking的了解
作用
它表示在打包的时候会去除一些无用的代码
原理
ES6的模块引入是静态分析的所以在编译时能正确判断到底加载了哪些模块分析程序流判断哪些变量未被使用、引用进而删除此代码
特点
在生产模式下它是默认开启的但是由于经过babel编译全部模块被封装成IIFE它存在副作用无法被tree-shaking掉可以在package.json中配置sideEffects来指定哪些文件是有副作用的。它有两种值一个是布尔类型如果是false则表示所有文件都没有副作用如果是一个数组的话数组里的文件路径表示改文件有副作用rollup和webpack中对tree-shaking的层度不同例如对babel转译后的class如果babel的转译是宽松模式下的话(也就是loose为true)webpack依旧会认为它有副作用不会tree-shaking掉而rollup会。这是因为rollup有程序流分析的功能可以更好的判断代码是否真正会产生副作用。
原理
ES6 Module 引入进行静态分析故而编译的时候正确判断到底加载了那些模块静态分析程序流判断那些模块和变量未被使用或者引用进而删除对应代码 依赖于import/export 通过导入所有的包后再进行条件获取。如下
import foo from foo;
import bar from bar;if(condition) {// foo.xxxx
} else {// bar.xxx
}ES6的import语法完美可以使用tree shaking因为可以在代码不运行的情况下就能分析出不需要的代码 CommonJS的动态特性模块意味着tree shaking不适用 。因为它是不可能确定哪些模块实际运行之前是需要的或者是不需要的。在ES6中进入了完全静态的导入语法import。这也意味着下面的导入是不可行的
// 不可行ES6 的import是完全静态的
if(condition) {myDynamicModule require(foo);
} else {myDynamicModule require(bar);
}模块化 js 中现在比较成熟的有四种模块加载方案 第一种是 CommonJS 方案它通过 require 来引入模块通过 module.exports 定义模块的输出接口。这种模块加载方案是服务器端的解决方案它是以同步的方式来引入模块的因为在服务端文件都存储在本地磁盘所以读取非常快所以以同步的方式加载没有问题。但如果是在浏览器端由于模块的加载是使用网络请求因此使用异步加载的方式更加合适。第二种是 AMD 方案这种方案采用异步加载的方式来加载模块模块的加载不影响后面语句的执行所有依赖这个模块的语句都定义在一个回调函数里等到加载完成后再执行回调函数。require.js 实现了 AMD 规范第三种是 CMD 方案这种方案和 AMD 方案都是为了解决异步模块加载的问题sea.js 实现了 CMD 规范。它和require.js的区别在于模块定义时对依赖的处理不同和对依赖模块的执行时机的处理不同。第四种方案是 ES6 提出的方案使用 import 和 export 的形式来导入导出模块 在有 Babel 的情况下我们可以直接使用 ES6的模块化 // file a.js
export function a() {}
export function b() {}
// file b.js
export default function() {}import {a, b} from ./a.js
import XXX from ./b.jsCommonJS CommonJs 是 Node 独有的规范浏览器中使用就需要用到 Browserify解析了。 // a.js
module.exports {a: 1
}
// or
exports.a 1// b.js
var module require(./a.js)
module.a // - log 1在上述代码中module.exports 和 exports 很容易混淆让我们来看看大致内部实现 var module require(./a.js)
module.a
// 这里其实就是包装了一层立即执行函数这样就不会污染全局变量了
// 重要的是 module 这里module 是 Node 独有的一个变量
module.exports {a: 1
}
// 基本实现
var module {exports: {} // exports 就是个空对象
}
// 这个是为什么 exports 和 module.exports 用法相似的原因
var exports module.exports
var load function (module) {// 导出的东西var a 1module.exports areturn module.exports
};再来说说 module.exports 和exports用法其实是相似的但是不能对 exports 直接赋值不会有任何效果。 对于 CommonJS 和 ES6 中的模块化的两者区别是 前者支持动态导入也就是 require(${path}/xx.js)后者目前不支持但是已有提案,前者是同步导入因为用于服务端文件都在本地同步导入即使卡住主线程影响也不大。而后者是异步导入因为用于浏览器需要下载文件如果也采用同步导入会对渲染有很大影响前者在导出时都是值拷贝就算导出的值变了导入的值也不会改变所以如果想更新值必须重新导入一次。但是后者采用实时绑定的方式导入导出的值都指向同一个内存地址所以导入值会跟随导出值变化后者会编译成 require/exports 来执行的
AMD AMD 是由 RequireJS 提出的 AMD 和 CMD 规范的区别
第一个方面是在模块定义时对依赖的处理不同。AMD推崇依赖前置在定义模块的时候就要声明其依赖的模块。而 CMD 推崇就近依赖只有在用到某个模块的时候再去 require。第二个方面是对依赖模块的执行时机处理不同。首先 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 requirejs 在推广过程中对模块定义的规范化产出提前执行推崇依赖前置CMD seajs 在推广过程中对模块定义的规范化产出延迟执行推崇依赖就近CommonJs 模块输出的是一个值的 copy运行时加载加载的是一个对象module.exports 属性该对象只有在脚本运行完才会生成ES6 Module 模块输出的是一个值的引用编译时输出接口ES6模块不是对象它对外接口只是一种静态定义在代码静态解析阶段就会生成。
谈谈对模块化开发的理解
我对模块的理解是一个模块是实现一个特定功能的一组方法。在最开始的时候js 只实现一些简单的功能所以并没有模块的概念但随着程序越来越复杂代码的模块化开发变得越来越重要。由于函数具有独立作用域的特点最原始的写法是使用函数来作为模块几个函数作为一个模块但是这种方式容易造成全局变量的污染并且模块间没有联系。后面提出了对象写法通过将函数作为一个对象的方法来实现这样解决了直接使用函数作为模块的一些缺点但是这种办法会暴露所有的所有的模块成员外部代码可以修改内部属性的值。现在最常用的是立即执行函数的写法通过利用闭包来实现模块私有作用域的建立同时不会对全局作用域造成污染。
盒模型 content元素内容 padding内边距 border边框 margin外边距 延伸box-sizing
content-box默认值总宽度 margin border padding widthborder-box盒子宽度包含 padding 和 border总宽度 margin widthinherit从父元素继承 box-sizing 属性