AMD CMD CommonJS
AMD是来自于require.js的对模块化的定义
CMD是来自于sea.js的对模块化的定义
Commonjs是来自于Nodejs的对模块化的规范
AMD讲求依赖关系前置,即在定义模块的之前要求其先声明依赖的模块
CMD讲求依赖就近,即随用随声明即可。
前者实际上是为了能异步加载模块所进行的模块化定义,所以前者的用户体验会比较好。
后者放松了AMD的规范,对于开发会更加容易理解,且能够做到按需加载(用户用的时候再进行加载),所以性能会比较好
由于如今webpack等打包工具的出现,前端开发过程中也开始使用commonJS,所以如今require.js和sea.js使用得愈发少了,
Nodejs基于commonJS的规范开发。
阻止默认行为
e.preventDefault();与 window.event.returnValue = false;
多用于阻止a标签跳转,阻止浏览器默认右键,解决Safari的橡皮筋问题
阻止冒泡
e.stopPropagation();与window.event.cancelBubble = true;
用处可多了,父元素托管公用事件,子元素写独特事件的情况下就要取消子元素事件的冒泡行为
React中事件是托管在顶层元素document上以增强性能的,需要用React原生的取消冒泡事件 (e.nativeEvent.stopPropagation())或者stopImmediatePropagation()【阻止任何相同的事件的监听器被调用】取消事件
闭包问题
参考作者博客https://blog.csdn.net/u012312705/article/details/81749777
闭包的主要应用是缓存变量,封装局部变量避免被JS的垃圾回收机制回收,防止变量污染
原型链
所有的引用类型都有_proto_的隐式原型
所有的函数都有prototype的显式原型
prototype其实就是函数的一个实例化对象
_proto_就是他构造函数的prototype也就是其实例化对象
当调用某种方法或属性的时候,首先会查找自身prototype,找不到会查找其_proto_,再找不到继续向上找_proto_,直到来到原型链顶端null。
注意Function的构造器是Object噢。
主要的考察方式是考察new时发生了什么;
先创建一个空的Object对象 var obj = {};
将该对象的构造函数原型(_proto_)与其构造函数的原型(prototype)连接起来(定义都一样不连怎么行)
obj.__proto__ = ClassA.prototype;
然后把自己的this交给新对象(也就是用新的对象调用一次构造函数,让构造函数把this指针给他,从而继承来自实例的方法和属性)【是不是很像super()】ClassA.call(obj);(在这个时候就是传递给他自身this里所包含的属性和方法的时候)
异步(Promise与async/await)
Promise顾名思义就是承诺,承诺能拿到数据,没拿到数据就抛出reject,拿到了就抛出resolve(这么说可能不大对,实际上reject是resolv的变形,即resolve(null,reject))
Promise可以让你逃离回调地狱,因为then的链式调用中,每次都会返回Promise对象,可以让原本难以维护的回调函数得到解放,多用于AJAX得到数据后处理以及数据库查询时(Nodejs)使用
使用then捕获resolve,使用catch捕获reject
后者则是ES7的新语法,声明一个函数为异步函数,他不会阻塞后面代码的运行,同时async实际上返回的是一个Promise对象,在async函数中用return返回一个值会自动调用resolve,用throw会自动调用reject,当然可以用then和catch分别捕获
await只能在async函数中声明,await声明的函数会变为Promise对象(应当在await后面直接声明Promise),直到async函数中的所有await运行完后,才会进行状态的改变
注意一旦有一个await抛出错误,就会导致后面的所有await不执行,这个时候可以用try-catch把所有await包裹起来以捕获错误
原生AJAX
四步五步走
先创建XMLHttpRequest对象 (let ajax = new XMLHttpRequest())
get方法:
使用open设置请求的类型(get),请求的url(url可以带参数),是否异步
ajax.open('get','url');
发送请求(ajax.send())[注意兼容性问题,如果不发送数据,ie会报错,所以这里一般是要发送null或者""]
监听状态
ajax.onreadystatechange = ()=>{
if(ajax.readyState == 4 && ajax.status == 200){
//得到数据ajax.responseText()
}
}
post方法下,需要设置请求头
ajax.setRequestHeader("Content-type","application/x-www-form-urlencoded")(响应对象类型,这里的意思是用这种编码方式封装form),注意在传递file的时候需要换成multipart/form-data(你的form也得加上这个,form的enctype的默认值是前者)
其余的跟get无变化
跨域请求
同源策略:端口,域名,协议相同才能发起数据请求
一般的做法是jsonp,即用script标签包裹json的一种传递方式,因为浏览器对script标签就像对img标签一样,是没有资源限制的,所以你通常可以在服务器为数据包裹上用你自己设置的回调函数,再在js下动态添加script标签从而请求到json数据,回调函数就会运行,从而在回调函数里处理数据。
jsonp不能使用post方式,同时想要监听其是否失败比较麻烦,一般使用超时鉴定。
其次可以使用服务器代理,即服务器帮你完成这次请求的操作。
domain跨域,按照说法只能跨越同一级域名的子域,即兄弟之间的交流,在设置了document.domain之后,可以利用ajax访问其数据,小广告特别喜欢用domain+iframe或者CORS进行跨域拿到广告内容呢
CORS:simple Request下,即不添加自己自定义的请求头,且只使用get,post,head方法的情况下,且Content-Type是application/x-www-urlencoded,multipart/form-data,text/plain三种之一,只需要在服务器响应头中添加Acess-Control-Allow-Origin且使其包含请求的域,即可进行跨域请求。
app.all('/test', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
非simple Request下,即不满足上述情况,则需要向请求的服务器做一次预验证(preflighted),查看对方服务器是否支持请求所在域(Acess-Control-Allow-Origin),是否支持请求的方式(Acess-Control-Allow-Methods),是否支持请求的头(Acess-Control-Allow-Header)【CORS协议默认支持Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,这里是为了使其支持异步AJAX请求】
同时两种请求不同的地方还在于其验证的过程,前者是直接拿到响应再做对ACAO的解析,不满足直接丢弃数据,后者是先做一次预验证,不满足是不会发送真正的数据请求的。
解构赋值
解构赋值在针对伪数组(arguments等)中应用得较多,以前主要是用Array.prototype.slice.call()或者Array.from()来解决,现在可以直接用(...arg)=>{}来接收参数数组了,当然([x,y])=>{}也可以把传入的数组的前两个添加变量名xy。
同样的解构赋值在深拷贝中用得比较多,特别是Redux,由于其保证store的不变性,不对store的变量本身进行修改,所以需要复制一个新的数组,在新数组上进行修改,再返回给原来的数组,直接做替换。以前是用Object.assign({},数组)或者concat的方式来进行数组的克隆,现在可以直接使用[...oldArray] = newArray的方式克隆数组了
深拷贝和浅拷贝的原生实现也是经常被问到的,留待下一章在做详解
const/let
老生长谈,主要是有可能会让你讨论实现手段。
let的实现手段及其原理:https://blog.csdn.net/u012312705/article/details/81750557 作者博客
const并非声明常量,而是声明一个不变的地址指针,并给他取了个名字,这个指针的指向是不能被改变的(所以要求初始化,实际上是要求初始化内存地址指针),但是他指向地址存放的值时可以更改的,更应当称其为常量索引
经典的栗子比如 const a = []; a.push("1");你看看a的值会不会改变,当然会咯,但是你要重复声明a(内存地址独一无二,所以其名字自然也独一无二)或者修改其指针再来一次a = [],这时候就会被报错啦。