这篇文章是因为之前看过一篇文章,总结了一些技能,但是并不详细,之后陆续还会有新的文章。
事件模型
js中有两种事件,DOM0和DOM2.
- DOM0
DOM0事件很简单,就是在dom上绑定事件,代码如下
document.getElementById('click').onclick = function(){
console.log('123')
}
解除绑定很简单,赋值为null即可。
document.getElementById('click').onclick = null
但是这种事件虽然在所有浏览器都能运行,但是有个很明显的缺陷,就是不能绑定多个相同事件,如果绑定了多个相同事件,后面的会覆盖前面的
- DOM2
第二种就是事件捕获和事件冒泡。代码上来说就是注册事件是addEventListener,解除绑定是removeEventListener。阻止冒泡的方法是stopPropagation。
声明提升
其实js中的声明提升很简单,就是指变量声明提升和函数的声明提升。声明提示的意思就是把变量或者函数在编译的时候提升到环境的顶部,并赋值undefined
- 变量声明
变量声明,js会把变量声明分为两个部分,一个是声明操作(var a),一个是赋值操作(a=1),这里的声明提升针对的是声明操作,赋值操作还是在原来的地方等待执行。
- 函数声明
定义函数的方式有两种,函数声明和函数表达式,函数表达式和变量声明同等。
在js编译时,函数声明会把函数声明和整个函数体都提升到环境的顶部,所以可以在函数声明前调用这个函数,如以下代码
foo()
function foo(){
console.log(111)
}
- 声明的顺序
函数声明是优先于变量声明的
1.js编译时,会把所有的函数声明提到顶部,如果有重复的进行覆盖
2.把所有的变量提到顶部,并赋值undefined,如果有重复的进行覆盖
接下来看两个典型的例子
alert(a)
a();
var a=3;
function a(){
alert(10)
}
alert(a)
a=6;
a();
------------分割线------------------
alert(a)
a();
var a=3;
var a=function(){
alert(10)
}
alert(a)
a=6;
a();
第一部分:
1.函数声明优先于变量声明,所以,刚开始,a就是function a(){alert(10)} ,就会看到这个函数。
2.a(),执行函数,就是出现alert(10)
3.执行了var a=3; 所以alert(a)就是显示3
4.由于a不是一个函数了,所以往下在执行到a()的时候, 报错。
第二部分运行结果:
1.underfind
2.报错
在之前说过,预解析是把带有var和function关键字的事先声明,但不会赋值。所以一开始是underfind,然后报错是因为执行到a()的时候,a并不是一个函数。
继承
js继承的几种方法
1.原型链继承,将父类的实例作为子类的原型
function Cat(){}
Cat.prototype = New Animal()
2.构造继承,利用父元素的构造函数来增强子元素的实例,其实就是把父类的实例属性给了子类
function Cat(){
Animal.call(this);
}
3.实例继承,父类的实例增加属性作为子类的实例返回
function Cat(){
var instance = new Animal();
intance.name = 'aaa';
return instance;
}
4.拷贝继承,无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
function Cat(name){
var animal = new Animal();
for(var p in animal){
Cat.prototype[p] = animal[p];
}
Cat.prototype.name = name || 'Tom';
}
5.组合继承,通过调用父类,实现了继承父类的属性和保留传参的优点,父类的实例作为子类的原型,实现了函数的复用
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;//组合继承也是需要修复构造函数指向的。
6.寄生组合继承,通过第三方的一个空类,来继承父类的函数复用,父类的属性的继承通过在子类中调用父类实现。
/**
* 通用方法实现子类继承父类
* @param {function} child 子类构造函数
* @param {function} father 被继承的父类构造函数
*/
function inheritPrototype(child, father) {
var prototype = object(father.prototype); //创建一个指定原型的对象
prototype.constructor = child; //增强对象
child.prototype = prototype; //子类的原型等于该对象
}
function father(name) {
this.faName = 'father';
}
father.prototype.getfaName = function() {
console.log(this.faName);
};
function child(args) {
this.chName = 'child';
father.apply(this,[]);
}
inheritPrototype(child, father); //子类的原型等于new 空函数(), 而new 空函数()出来的对象的原型等于父类的原型
child.prototype.getchName = function() {
console.log(this.chName);
};
console.log( child.prototype.isPrototypeOf(new child()) ); //true
console.log(new child() instanceof child); //true
优点:1.只调用一次父类的构造函数,避免了在子类原型上创建不必要的,多余的属性
2.原型链保持不变
https://blog.csdn.net/xuqingg...
参考https://www.cnblogs.com/humin...
跨域
跨域,是指不能执行其他网站的脚本,是因为浏览器的同源策略(域名,协议,端口均相同)引起的,是浏览器对js实施的安全限制。但是script标签本身就可以访问其它域的资源,不受浏览器同源策略的限制,可以通过在页面动态创建script标签。
限制:
1.cookie,localstorage,indexDB无法读取
2.DOM和js对象无法获取
3.ajax请求发不出去
解决办法:
1.jsonp 但是只限于get请求
1.1 通过动态创建script标签,请求一个带参数的网址实现跨域
var script = document.createElement('script');
script.src = 'http://www.aaa.com/username=aaaa&callback=cb';
document.body.appendChild(script);
function cb(res){}
1.2 jquery的ajax支持jsonp
$.ajax({
url:'http://www.aaa.com',
type:'get',
dataType:'json',
jsonCallback:'cb',
data:{
username:'111'
}
})
2.document.domain+iframe,但是要求主域名相同
3.window.name + iframe 跨域
4.location.hash + iframe 跨域
5.postMessage
1.html
<iframe id="iframe" src="http://www.neal.cn/b.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe');
iframe.onload = function() {
var data = {
name: 'aym'
};
// 向neal传送跨域数据
iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.neal.cn');
};
// 接受domain2返回数据
window.addEventListener('message', function(e) {
alert('data from neal ---> ' + e.data);
}, false);
</script>
2.html(另一端口)
script>
// 接收domain1的数据
window.addEventListener('message', function(e) {
alert('data from nealyang ---> ' + e.data);
var data = JSON.parse(e.data);
if (data) {
data.number = 16;
// 处理后再发回nealyang
window.parent.postMessage(JSON.stringify(data), 'http://www.nealyang.cn');
}
}, false);
</script>
6.跨域资源共享CORS
CORS是目前主流的跨域解决方案。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了ajax的同源策略的限制。
目前所有的浏览器都支持这个功能。IE要求IE10以上,IE8,9需要用XDomainRequest对象来支持这个功能。
CORS需要客户端和服务端同时支持。
整个CORS通信过程,都是浏览器完成的,对于开发者来说,CORS和ajax没有什么区别,发送请求后,浏览器识别到这是跨源请求,就会在头部添加附加信息,有时还会多一次请求。所以实现CORS的关键是服务器,只要服务器实现了CORS接口,就可以跨源通信。
这时候就要说到请求的种类了,分为简单请求和非简单请求。
简单请求
简单请求(请求方式为HEAD,GET,POST,头信息不超过以下字段,Accept,Accept-language,Content-language,Last-Event-ID,Content-type【application/x-www-form-urlencoded、multipart/form-data、text/plain】)。
浏览器直接发出CORS请求,在头信息中会增加origin属性(origin:http://www.baidu.com),这个属性表明请求的来源,服务器根据这个是否同意这次请求。如果origin指定的源不在许可范围内,服务端会返回http回应,浏览器会发现没有Access-Control-Allow-Origin字段,就会抛出异常,到XMLHttpRequest的onerror函数。
如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段
上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-开头
- Access-Control-Allow-Origin :该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求
- Access-Control-Allow-Credentials: 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。
- Access-Control-Expose-Headers:该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。
withCredentials 属性
上面说到,CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。
另一方面,开发者必须在AJAX请求中打开withCredentials属性。
否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。
但是,如果省略withCredentials设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭withCredentials。
需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。
非简单请求
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP头信息。
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。
除了Origin字段,"预检"请求的头信息包括两个特殊字段。
- Access-Control-Request-Method:该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT。
- Access-Control-Request-Headers:该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header
预检请求的回应
服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应.
上面的HTTP回应中,关键的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。
如果浏览器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出报错信息。
服务器回应的其他CORS相关字段如下:
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
- Access-Control-Allow-Methods:该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
- Access-Control-Allow-Headers:如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
- Access-Control-Allow-Credentials: 该字段与简单请求时的含义相同。
- Access-Control-Max-Age: 该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。
浏览器正常请求回应
一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。
7.websocket
8.nginx
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。