什么是闭包?闭包有什么好处?为什么要用它?使用闭包要注意什么?

闭包:

闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和参数传递到外部。该变量和参数不会被垃圾回收机制所回收

好处 :

1)希望一个变量长期驻扎在内存之中
2)避免全局变量的污染
3)私有成员的存在
注意:参数和变量不会被垃圾回收机制回收,使用闭包后变量就在内存中,所以也不宜使用太多的闭包。可能会造成内存泄漏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//li节点的onclick事件都能正确的弹出当前被点击的li索引

<ul id="testUL">
<li> index = 0</li>
<li> index = 1</li>
<li> index = 2</li>
<li> index = 3</li>
</ul>
<script type="text/javascript">
var nodes = document.getElementsByTagName("li");
for(i = 0;i<nodes.length;i++){
nodes[i].onclick = function(){
console.log(i+1);//不用闭包的话,值每次都是4
}(i);
}
</script>

执行 say667()后,say667()闭包内部变量会存在,而闭包内部函数的内部变量不会存在,使得 Javascript 的垃圾回收机制 GC 不会收回 say667()所占用的资源,因为 say667()的内部函数的执行需要依赖 say667()中的变量

1
2
3
4
5
6
7
8
9
10
11
function say667() {
// Local variable that ends up within closure
var num = 666;
var sayAlert = function() {
alert(num);
}
num++;
return sayAlert;
}
var sayAlert = say667();
sayAlert()//执行结果应该弹出的667
闭包的原理和应用

闭包就是能够读取其他函数内部变量的函数。
当回调发生时,闭包能记住它原来所在的执行上下文
它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在 IE 中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。

闭包的用途:

(1)匿名自执行函数
如果变量不加上 var 关键字,则会默认添加到全局对象属性上去,这样可能造成别的函数误用这些变量,造成全局对象过于庞大,影响访问速度。此外,也会有的函数只需执行一次,内部的变量无需维护

1
2
3
4
5
6
var datamodel={
table:[],
tree:{}
};
(function(dm){
})(datamodel);

创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此在执行完后很快就会被释放,关键是这种机制不会污染全局对象。
(2)有一个很耗时的函数对象,每次调用都会花费很长时间,就需要把计算的值存储起来,当调用的时候,首先在缓存中查找,找不到,则进行计算。(闭包不会释放外部的引用,从而使函数内部的值可以保留)
(3)实现封装,person 之外的地方无法访问其内部变量的值,通过闭包的形式访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var person=function(){
var name=”default”;
return{
getName:function(){
return name;
},
setName:function (newName){
name=newName;
}
}
}();
print(person.name); // 直接访问,结果为undefined
print(person.getName()); //default
person.setName(“MIKE”);
print(person.getName()); //MIKE

(4)实现面向对象中的对象

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
function Person(){
var name = "default";
return {
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
};
var john =new Person();
print(john.getName()); //default
john.setName("john");
print(john.getName()); //john
varjack =new Person();
print(jack.getName()); //default
jack.setName("jack");
print(jack.getName()); //jack
//john和jack都可以称为是Person这个类的实例,因为这两个实例对name这个成员的访问是独立的,互不影响的。

function lazy_sum(arr) {
var sum = function () {
return arr.reduce(function (x, y) {
return x + y; //在函数lazy_sum中又定义了函数sum,
}); //并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,
} //当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中
return sum; //注意sum是一个函数
}
varf = lazy_sum([1, 2, 3, 4, 5]); // 调用lazy_sum返回的不是求和结果,而是求和函数
f(); //15 调用函数f时,才是真正的计算结果

函数在其定义内部引用了局部变量 arr,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
function count() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push(function () {
return i * i;
});
}
return arr;
}
var results = count();
var f1 = results[0]; //16
var f2 = results[1]; //16
var f3 = results[2]; //16

返回的函数引用了变量 i,但它并非立刻执行。等到 3 个函数都返回时,它们所引用的变量 i 已经变成了 4,因此最终结果为 16。
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,然后立即调用该循环变量,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function count() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push(
(function (n) {
return function () {
return n * n;
}
})(i) //这是一个匿名函数
);
}
return arr;
}
varresults = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
f1();// 1
f2();// 4
f3();// 9

闭包的可以封装一个私有变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function  create_counter(initial) {
var x = initial || 0;
return {
inc: function () {
x += 1;
return x;
}
}
}
varc1 = create_counter();
c1.inc();// 1
c1.inc();// 2
c1.inc();// 3
varc2 = create_counter(10);
c2.inc();// 11
c2.inc();// 12
c2.inc();// 13

在返回的对象中,实现了一个闭包,该闭包携带了局部变量 x,并且,从外部代码根本无法访问到变量 x。
使用闭包写一个斐波那契数列求值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function fibonacii(n){
var sum=0,pre=0,next=1;
return function(n){
if(n==0){
return 0;
}else if(n==1){
return 1;
}
for(var i=2;i<=n;i++){
sum=pre+next;
pre=next;
next=sum;
}
return sum;
}
}
for(var i=0;i<n;i++){//显示斐波那契数列前n项
console.log(fibonacii(i)(i));
}
使用生成器实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function* fibonacci(){
let a=0;
let b=1;
yield a;
yield b;
while(true){
let next=a+b;
a=b;
b=next;
yield next;
}
}
let gen=fibonacci();
for(var i=0;i<n;i++){
console.log(gen.next().value);
}

iframe 有那些优缺点?

优点:

1)解决加载缓慢的第三方内容如图标和广告等的加载问题
2)Security sandbox
3)并行加载脚本

缺点:

1)在网页中使用框架结构最大的弊病是搜索引擎的检索程序无法解读这种页面,不利于 SEO;
2)iframe 会阻塞主页面的 Onload 事件;
3)iframe 和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。即使内容为空,加载也需要时间。
4)框架结构有时会让人感到迷惑,页面很混乱,没有语义。 使用 iframe 之前需要考虑这两个缺点。如果需要使用 iframe,最好是通过 javascript 动态给 iframe 添加 src 属性值,这样可以绕开以上两个问题。

JavaScript 原型,原型链 ? 有什么特点?

每个对象都会在其内部初始化一个属性,就是 prototype(原型),当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去 prototype 里找这个属性,这个 prototype 又会有自己的 prototype,于是就这样一直找下去,也就是我们平时所说的原型链的概念。
关系:instance.constructor.prototype = instance.proto(对象的proto属性和创建这个对象的构造函数的 prototype 属性是一个东西)

特点:
JavaScript 对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。而且创建子类型的实例时不能向超类型的构造函数中传递参数。
当我们需要一个属性的时,Javascript 引擎会先看当前对象中是否有这个属性, 如果没有的话,就会查找他的 Prototype 对象是否有这个属性,如此递推下去,一直检索到 Object 内建对象。

1
2
3
4
5
6
7
8
9
10
function Func(){}
Func.prototype.name = "Sean";
Func.prototype.getInfo = function() {
return this.name;
}
var person = new Func();//现在可以参考var person = Object.create(oldObject);
console.log(person.getInfo());//它拥有了Func的属性和方法
//"Sean"
console.log(Func.prototype);
// Func { name="Sean", getInfo=function()}
Javascript 作用链域?

全局函数无法查看局部函数的内部细节,但局部函数可以查看其上层的函数细节,直至全局细节。
当需要从局部函数查找某一属性或方法时,如果当前作用域没有找到,就会上溯到上层作用域查找,直至全局函数,这种组织形式就是作用域链。

javascript 是面向对象的,怎么体现 javascript 的继承关系?

实现继承主要是依靠原型链来实现的
1)属性

1
2
父级构造函数.apply(this,arguments);
SuperType.apply(this,arguments); 实现父级构造函数的操作

2)方法

1
2
3
4
person1.constructor == Person
Person.prototype.constructor == Person
子级构造函数.prototype=new 父级构造(); SubType.prototype=new SuperType();
子级构造函数.prototype.constructor=子级构造; SubType.prototype.constructor=SubType;

javascript 继承的 6 种方法

原型链继承
借用构造函数继承
组合继承(原型+借用构造)
原型式继承
寄生式继承
寄生组合式继承

原型 prototype 机制或 apply 和 call 方法去实现较简单,建议使用构造函数与原型混合方式。

1
2
3
4
5
6
7
8
9
10
function Parent(){
this.name = 'wang';
}
function Child(){
this.age = 28;
}
Child.prototype = new Parent();//继承了Parent,通过原型
var demo = new Child();
alert(demo.age);
alert(demo.name);//得到被继承的属性

10、javascript 创建对象的几种方式?

工厂模式
构造函数模式
原型模式
混合构造函数和原型模式
动态原型模式
寄生构造函数模式
稳妥构造函数模式

javascript 创建对象简单的说,无非就是使用内置对象或各种自定义对象,当然还可以用 JSON;但写法有很多种,也能混合使用。
1)、对象字面量的方式

1
2
3
4
5
6
person={
firstname:"Mark",
lastname:"Yun",
age:25,
eyecolor:"black"
};

2)、用 function 来模拟无参的构造函数

1
2
3
4
5
6
7
8
function Person(){}
var person=new Person();//定义一个function,如果使用new"实例化",该function可以看作是一个Class
person.name="Mark";
person.age="25";
person.work=function(){
alert(person.name+" hello...");
}
person.work();

3)、用 function 来模拟参构造函数来实现(用 this 关键字定义构造的上下文属性)

1
2
3
4
5
6
7
8
9
10
function Pet(name,age,hobby){
this.name=name;//this作用域:当前对象
this.age=age;
this.hobby=hobby;
this.eat=function(){
alert("我叫"+this.name+",我喜欢"+this.hobby+",是个程序员");
}
}
var maidou =new Pet("麦兜",25,"coding");//实例化、创建对象
maidou.eat();//调用eat方法

4)、用工厂方式来创建(内置对象)

1
2
3
4
5
6
7
var wcDog =new Object();
wcDog.name="旺财";
wcDog.age=3;
wcDog.work=function(){
alert("我是"+wcDog.name+",汪汪汪......");
}
wcDog.work();

5)、用原型方式来创建

1
2
3
4
5
6
7
8
function Dog(){
}
Dog.prototype.name="旺财";
Dog.prototype.eat=function(){
alert(this.name+"是个吃货");
}
var wangcai =new Dog();
wangcai.eat();

6)、用混合方式来创建

1
2
3
4
5
6
7
8
9
function Car(name,price){
this.name=name;
this.price=price;
}
Car.prototype.sell=function(){
alert("我是"+this.name+",我现在卖"+this.price+"万元");
}
var camry =new Car("凯美瑞",27);
camry.sell();

11、call 方法,apply 方法

call 方法:
语法:call(thisObj,Object)
定义:调用一个对象的一个方法,以另一个对象替换当前对象。
说明:call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。
apply 方法:
语法:apply(thisObj,[argArray])
定义:应用某一对象的一个方法,用另一个对象替换当前对象。
说明:如果 argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。如果没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用作 thisObj, 并且无法被传递任何参数。

1
2
3
4
5
6
7
function add(a,b){
alert(a+b);
}
function sub(a,b){
alert(a-b);
}
add.call(sub,3,1);

例子中用 add 来替换 sub,add.call(sub,3,1) == add(3,1) ,所以运行结果为:alert(4); 注意:js 中的函数其实是对象,函数名是对 Function 对象的引用。

说几条写 JavaScript 的基本规范?

1).不要在同一行声明多个变量。
2).请使用 ===/!==来比较 true/false 或者数值
3).使用对象字面量替代 new Array 这种形式
4).不要使用全局函数。
5).Switch 语句必须带有 default 分支
6).函数不应该有时候有返回值,有时候没有返回值。
7).For 循环必须使用大括号
8).If 语句必须使用大括号
9).for-in 循环中的变量应该使用 var 关键字明确限定作用域,从而避免作用域污染。

在 js 中通过 typeof 能弹出的数据类型有哪些

number,string,boolean,function,object,undefined 记住 typeof null 是’object’

js 数据类型和区分:
基本数据类型:String,boolean,Number,Undefined, Null
引用数据类型:Object(Array,Date,RegExp,Function)
Object 是 JavaScript 中所有对象的父对象 数据封装类对象:Object、Array、Boolean、Number 和 String 其他对象:Function、Arguments、Math、Date、RegExp、Error 区分基本数据类型:typeof;Undefined 只能用 typeof 检测 typeof a==’undefined’ typeof null 是’object’
typeof 返回的类型都是字符串形式,可以判断 function 的类型
区分引用数据类型:instanceof
区分各数据类型: Object.prototype.toString.call()

js 数组中提供了以下几个方法可以让我们很方便实现堆栈:
shift:从数组中把第一个元素删除,并返回这个元素的值。
unshift: 在数组的开头添加一个或更多元素,并返回新的长度
push:在数组的中末尾添加元素,并返回新的长度
pop:从数组中把最后一个元素删除,并返回这个元素的值。

谈谈 This 对象的理解。

this 总是指向函数的直接调用者(而非间接调用者);
如果有 new 关键字,this 指向 new 出来的那个对象;
在事件中,this 指向触发这个事件的对象,特殊的是,IE 中的 attachEvent 中的 this 总是指向全局对象 Window;

eval 是做什么的?

它的功能是把对应的字符串解析成 JS 代码并运行;
应该避免使用 eval,不安全,非常耗性能(2 次,一次解析成 js 语句,一次执行)。
由 JSON 字符串转换为 JSON 对象的时候可以用

1
eval,var obj =eval('('+ str +')');

什么是 window 对象? 什么是 document 对象?

window 对象代表浏览器中打开的一个窗口。document 对象代表整个 html 文档。实际上,document 对象是 window 对象的对象属性。

null,undefined 的区别?

null:示一个对象被定义了,值为“空值”;
undefined:表示不存在这个值。
typeof undefine//“undefined”
undefined :是一个表示”无”的原始值或者说表示”缺少值”,就是此处应该有一个值,但是还没有定义。当尝试读取时会返回 undefined;例如变量被声明了,但没有赋值时,就等于 undefined。
typeof null//“object”
null : 是一个对象(空对象, 没有任何属性和方法);
例如作为函数的参数,表示该函数的参数不是对象;
注意:
在验证 null 时,一定要使用 === ,因为 == 无法区别 null 和  undefined

写一个通用的事件侦听器函数。

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
markyun.Event = {
// 页面加载完成后
readyEvent : function(fn) {
if (fn==null) {
fn=document;
}
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = fn;
} else {
window.onload = function() {
oldonload();
fn();
};
}
},
// 视能力分别使用dom0||dom2||IE方式 来绑定事件
// 参数: 操作的元素,事件名称 ,事件处理程序
addEvent : function(element, type, handler) {
if (element.addEventListener) {
//事件类型、需要执行的函数、是否捕捉
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent('on' + type, function() {
handler.call(element);
});
} else {
element['on' + type] = handler;
}
},
// 移除事件
removeEvent : function(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if (element.datachEvent) {
element.detachEvent('on' + type, handler);
} else {
element['on' + type] = null;
}
},
// 阻止事件 (主要是事件冒泡,因为IE不支持事件捕获)
stopPropagation : function(ev) {
if (ev.stopPropagation) {
ev.stopPropagation();
} else {
ev.cancelBubble = true;
}
},
// 取消事件的默认行为
preventDefault : function(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
// 获取事件目标
getTarget : function(event) {
return event.target || event.srcElement;
},
// 获取event对象的引用,取到事件的所有信息,确保随时能使用event;
getEvent : function(e) {
var ev = e || window.event;
if (!ev) {
var c = this.getEvent.caller;
while (c) {
ev = c.arguments[0];
if (ev && Event == ev.constructor) {
break;
}
c = c.caller;
}
}
return ev;
}
};

[“1”, “2”, “3”].map(parseInt) 答案是多少?

1
[1, NaN, NaN]

parseInt() 函数能解析一个字符串,并返回一个整数,需要两个参数 (val, radix),其中 radix 表示要解析的数字的基数。【该值介于 2 ~ 36 之间,并且字符串中的数字不能大于 radix 才能正确返回数字结果值;但此处 map 传了 3 个 (element, index, array),我们重写 parseInt 函数测试一下是否符合上面的规则。

1
2
3
4
5
function parseInt(str, radix) {
return str+'-'+radix;
};
var a=["1", "2", "3"];
a.map(parseInt); // ["1-0", "2-1", "3-2"] 不能大于radix

因为二进制里面,没有数字 3,导致出现超范围的 radix 赋值和不合法的进制解析,才会返回 NaN。所以[“1”, “2”, “3”].map(parseInt) 答案也就是:[1, NaN, NaN]

事件是?IE 与火狐的事件机制有什么区别? 如何阻止冒泡?

1)、我们在网页中的某个操作(有的操作对应多个事件)。例如:当我们点击一个按钮就会产生一个事件。是可以被 JavaScript 侦测到的行为。
2)、事件处理机制:IE 是事件冒泡、Firefox 同时支持两种事件模型,也就是:捕获型事件和冒泡型事件;
3)、ev.stopPropagation();(旧 ie 的方法 ev.cancelBubble = true;)

如何判断一个对象是否属于某个类?

使用 instanceof (待完善)

1
2
3
if(a instanceof Person){
alert('yes');
}

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

1)、声明一个中间对象;
2)、将该中间对象的原型指向构造函数的原型;
3)、调用该构造函数,将构造函数的上下文对象 this,指向该中间对象;
4)、返回该中间对象,即返回实例对象。

1
2
3
var obj  = {};
obj.__proto__ = fn.prototype;
fn.call(obj);//如果构造函数明确指定了返回对象时,那么new的执行结果就是该返回对象,否则就是返回的实例对象

Javascript 中,有一个函数,执行时对象查找时,永远不会去查找原型,这个函数是?

hasOwnProperty
javaScript 中 hasOwnProperty 函数方法是返回一个布尔值,指出一个对象是否具有指定名称的属性。此方法无法检查该对象的原型链中是否具有该属性;该属性必须是对象本身的一个成员。
使用方法:
object.hasOwnProperty(proName),其中参数 object 是必选项。一个对象的实例。proName 是必选项。一个属性名称的字符串值。如果 object 具有指定名称的属性,那么 JavaScript 中 hasOwnProperty 函数方法返回 true,反之则返回 false。

JSON 的了解?

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。是键值对,是一个对象。
它是基于 JavaScript 的一个子集。数据格式简单, 易于读写, 占用带宽小
如:{“age”:”12”, “name”:”back”}
JSON 字符串转换为 JSON 对象:

1
2
3
var obj =eval('('+ str +')');
var obj = str.parseJSON();
var obj = JSON.parse(str);

JSON 对象转换为 JSON 字符串:

1
2
var last=obj.toJSONString();
var last=JSON.stringify(obj);

js 延迟加载的方式有哪些?

defer 和 async、动态创建 DOM 方式(用得最多)、按需异步载入 js

Ajax 是什么?ajax 原理是什么?ajax 的交互模型?同步和异步的区别?如何解决跨域问题?

ajax 的全称:Asynchronous Javascript And XML。
异步传输+js+xml。
所谓异步,在这里简单地解释就是:向服务器发送请求的时候,我们不必等待结果,而是可以同时做其他的事情,等到有了结果它自己会根据设定进行后续操作,与此同时,页面是不会发生整页刷新的,提高了用户体验。
Ajax 就是通过 JavaScript 创建 XMLHttpRequest 对象,再由 JavaScript 调用 XMLHttpRequest 对象的方法完成异步通信;然后,再由 JavaScript 通过 DOM 的属性和方法,完成页面的不完全刷新。
由事件触发,创建一个 XMLHttpRequest 对象,把 HTTP 方法(Get/Post)和目标 URL 以及请求返回后的回调函数设置到 XMLHttpRequest 对象,通过 XMLHttpRequest 向服务器发送请求,请求发送后继续响应用户的界面交互,只有等到请求真正从服务器返回的时候才调用 callback()函数,对响应数据进行处理。

原理:

AJAX 技术的核心是 XMLHttpRequest 对象(XHR),AJAX 与数据格式无关,这种即使是无需刷新技术就可以从服务器取得数据,不一定是 XML 数据。
要完整实现一个 AJAX 异步调用和局部刷新,通常需要以下几个步骤:
(1)创建 XMLHttpRequest 对象,也就是创建一个异步调用对象。

1
2
var xmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");//IE
var xmlHttpRequest = new XMLHttpRequest();

(2)创建一个新的 HTTP 请求,并指定该 HTTP 请求的方法、URL 及验证信息。说明 XMLHttpRequest 对象要从哪里获取数据。

1
XMLHttpRequest.open(method,URL,flag,name,password)//method:get、post、head、put、delete

(3)设置响应 HTTP 请求状态变化的函数。
XMLHttpRequest 对象可以响应 readystatechange 事件,该事件在 XMLHttpRequest 对象状态改变时(readyState 属性只要发生改变就会触发该函数)激发。因此,可以通过该事件调用一个函数,并在该函数中判断 XMLHttpRequest 对象的 readyState 属性值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//设置当XMLHttpRequest对象状态改变时调用的函数,注意函数名后面不要添加小括号
xmlHttpRequest.onreadystatechange = getData;
//定义函数
function getData(){
//判断XMLHttpRequest对象的readyState属性值是否为4,如果为4表示异步调用完成
if(xmlHttpRequest.readyState == 4){
//设置获取数据的语句
if(xmlHttpRequest.status >= 200 && xmlHttpRequest.status <300|| xmlHttpRequest.status == 304){
//使用以下语句将返回结果以字符串形式输出
document.write(xmlHttpRequest.responseText);
//或者使用以下语句将返回结果以XML形式输出
//docunment.write(xmlHttpRequest.responseXML);
}
}
}

(4)发送 HTTP 请求。

1
MLHttpRequest.send(data)

其中 data 是作为请求主体发送的数据,如果请求的数据不需要参数,则必须传入 null。
(5)获取异步调用返回的数据。
如果 XMLHttpRequest 对象的 readyState 属性值等于 4,表示异步调用过程完毕,使用 responseText 属性(作为响应主体)或 responseXml 属性来获取数据。但是,异步调用过程完毕,并不代表异步调用成功了,如果要判断异步调用是否成功,还要判断 XMLHttpRequest 对象的 status 属性值,只有该属性值为 200,才表示异步调用成功。
(6)使用 JavaScript 和 DOM 实现局部刷新。
注意:在调用 open()函数之前制定 onreadystatechange 事件处理程序才能确保跨浏览器兼容性。没有使用 event 对象直接使用 XHR 对象确定下一步该怎么做是较为可靠的一种方式。

整个 XMLHttpRequest 对象的生命周期应该包含如下阶段:

创建-初始化请求-发送请求-接收数据-解析数据-完成
readyState 共有五个状态,分别为 01234,但一般我们只关注 4 这个状态就好。但对于其各个状态的含义可以了解下,具体如下:
   0 - (未初始化)还没有调用 open()方法
   1 - (启动)已调用 open()方法,尚未调用 send()方法
   2 - (发送)已经调用 send(),但尚未接收到响应
   3 - (接收)已经接收到部分相应数据(已经接收到 HTTP 响应头部信息,但是消息体部分还没有完全接收到)
   4 - (完成)已经接收到全部响应数据,可以在客户端调用了
(0)未初始化
此阶段确认 XMLHttpRequest 对象是否创建,并为调用 open()方法进行未初始化作好准备。值为 0 表示对象已经存在,否则浏览器会报错--对象不存在。
(1)启动
此阶段对 XMLHttpRequest 对象进行初始化,即调用 open()方法,根据参数(method,url,true)完成对象状态的设置。并且 XMLHttpRequest 对象已经准备好将一个请求发送到服务器端。
(2)发送
已经通过 send 方法把一个请求发送到服务器端,但是还没有收到一个响应
(3)接收
此阶段解析接收到的服务器端响应数据。即根据服务器端响应头部返回的 MIME 类型把数据转换成能通过 responseBody、responseText 或 responseXML 属性存取的格式,为在客户端调用作好准备。状态 3 表示正在解析数据。
(4)完成
此阶段确认全部数据都已经解析为客户端可用的格式,解析已经完成。值为 4 表示数据解析完毕,可以通过 XMLHttpRequest 对象的相应属性取得数据。

请求返回前可调用 abort()方法终止请求。
Comet 是 Ajax 的进一步发展,让服务器几乎能够实时的向客户端发送数据,实现 Comet 主要有长轮询和 HTTP 流,所有浏览器都支持长轮询,而只有部分浏览器原生支持 HTTP 流,SSE 是一种实现 Comet 交互的浏览器 API,既支持长轮询,也支持 HTTP 流。

优势:

<1>.无刷新更新数据。
AJAX 最大优点就是能在不刷新整个页面的前提下与服务器通信维护数据。这使得 Web 应用程序更为迅捷地响应用户交互,并避免了在网络上发送那些没有改变的信息,减少用户等待时间,带来非常好的用户体验。

<2>.异步与服务器通信。
AJAX 使用异步方式与服务器通信,不需要打断用户的操作,具有更加迅速的响应能力。优化了 Browser 和 Server 之间的沟通,减少不必要的数据传输、时间及降低网络上数据流量。

<3>.前端和后端负载平衡。
AJAX 可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理,减轻服务器和带宽的负担,节约空间和宽带租用成本。并且减轻服务器的负担,AJAX 的原则是“按需取数据”,可以最大程度的减少冗余请求和响应对服务器造成的负担,提升站点性能。

<4>.基于标准被广泛支持。

<5>.界面与应用分离。
Ajax 使 WEB 中的界面与应用分离(也可以说是数据与呈现分离),有利于分工合作、减少非技术人员对页面的修改造成的 WEB 应用程序错误、提高效率、也更加适用于现在的发布系统。

Ajax 的最大的特点是什么:

Ajax 可以实现动态不刷新(局部刷新)
readyState 属性 状态 有 5 个可取值: 0=未初始化 ,1=启动 2=发送,3=接收,4=完成

Ajax 同步和异步的区别:

1)同步:提交请求 -> 等待服务器处理 -> 处理完毕返回,这个期间客户端浏览器不能干任何事
2)异步:请求通过事件触发 -> 服务器处理(这是浏览器仍然可以作其他事情)-> 处理完毕
ajax.open 方法中,第 3 个参数是设同步或者异步

ajax 的缺点:

1)ajax 不支持浏览器 back 按钮。
back 和 history 存在的根本就是 url 的改变,ajax 并没有改变 url,用户单击后退按钮访问历史记录时,通过创建或使用一个隐藏的 IFRAME 来重现页面上的变更。(例如,当用户在 Google Maps 中单击后退时,它在一个隐藏的 IFRAME 中进行搜索,然后将搜索结果反映到 Ajax 元素上,以便将应用程序状态恢复到当时的状态。)但是它所带来的开发成本是非常高的,和 ajax 框架所要求的快速开发是相背离的。这是 ajax 所带来的一个非常严重的问题。
2)安全问题 AJAX 暴露了与服务器交互的细节。
ajax 技术就如同对企业数据建立了一个直接通道。这使得开发者在不经意间会暴露比以前更多的数据和服务器逻辑。ajax 的逻辑可以对客户端的安全扫描技术隐藏起来,允许黑客从远端服务器上建立新的攻击。还有 ajax 也难以避免一些已知的安全弱点,诸如跨站点脚本攻击、SQL 注入攻击和基于 credentials 的安全漏洞等。
3)对搜索引擎的支持比较弱。
搜索引擎在抓取页面的时候会屏蔽所有的 JavaScript 代码,而基于 ajax 技术的 web 站点所用的到的很重要的技术就是 js 代码,这样一来 ajax 载入的内容相对于搜索引擎就是透明的不利于各大搜索引擎的搜索。
4)破坏了程序的异常机制。
5)不容易调试。
6)违背 URL 和资源定位的初衷。
7)AJAX 不能很好支持移动设备。
8)客户端过肥,太多客户端代码造成开发上的成本。编写复杂、容易出错;冗余代码比较多,破坏了 Web 的原有标准。
跨域: jsonp、 iframe、window.name、window.postMessage、服务器上设置代理页面

AJAX 注意点及适用和不适用场景
注意点

Ajax 开发时,网络延迟——即用户发出请求到服务器发出响应之间的间隔——需要慎重考虑。不给予用户明确的回应,没有恰当的预读数据,或者对 XMLHttpRequest 的不恰当处理,都会使用户感到延迟,这是用户不希望看到的,也是他们无法理解的。通常的解决方案是,使用一个可视化的组件来告诉用户系统正在进行后台操作并且正在读取数据和内容。

Ajax 适用场景

<1>.表单驱动的交互

<2>.深层次的树的导航

<3>.快速的用户与用户间的交流响应

<4>.类似投票 yes/no 等无关痛痒的场景

<5>.对数据进行过滤和操纵相关数据的场景

<6>.普通的文本输入提示和自动完成的场景。

Ajax 不适用场景

<1>.部分简单的表单

<2>.搜索

<3>.基本的导航

<4>.替换大量的文本

<5>.对呈现的操纵。

Ajax 解决浏览器缓存问题?

确保 ajax 或连接不走缓存路径:
1)、在 ajax 发送请求前加上 anyAjaxObj.setRequestHeader(“If-Modified-Since”,”0”)。
2)、在 ajax 发送请求前加上 anyAjaxObj.setRequestHeader(“Cache-Control”,”no-cache”)。
3)、在 URL 后面加上一个随机数: “fresh=” + Math.random();。
4)、在 URL 后面加上时间搓:”nowtime=” + new Date().getTime();。
5)、如果是使用 jQuery,直接这样就可以了 \$.ajaxSetup({cache:false})。这样页面的所有 ajax 都会执行这条语句就是不需要保存缓存记录。

Flash、Ajax 各自的优缺点,在使用中如何取舍?

Flash 适合处理多媒体、矢量图形、访问机器;对 CSS、处理文本上不足,不容易被搜索。
Ajax 对 CSS、文本支持很好,支持搜索;多媒体、矢量图形、机器访问不足。
共同点:与服务器的无刷新传递消息、用户离线和在线状态、操作 DOM。

简述同步和异步的区别

同步是阻塞模式,异步是非阻塞模式。
同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;
异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率
同步的概念应该是来自于 OS 中关于同步的概念:不同进程为协同完成某项工作而在先后次序上调整(通过阻塞,唤醒等方式).同步强调的是顺序性.谁先谁后.异步则不存在这种顺序性.
同步:浏览器访问服务器请求,用户看得到页面刷新,重新发请求,等请求完,页面刷新,新内容出现,用户看到新内容,进行下一步操作。
异步:浏览器访问服务器请求,用户正常操作,浏览器后端进行请求。等请求完,页面不刷新,新内容也会出现,用户看到新内容。

如何解决跨域问题?

jsonp、document.domain + iframe(只有在主域相同的时候才能使用该方法) iframe、window.name、web-cocket、postMessage(HTML5 中的 XMLHttpRequest Level 2 中的 API)、服务器上设置代理页面,动态创建 script,CORS

模块化开发怎么做?

模块化开发指的是在解决某一个复杂问题或者一系列问题时,依照一种分类的思维把问题进行系统性的分解以之解决。模块化是一种处理复杂系统分解为代码结构更合理,可维护性更高的可管理的模方式。对于软件行业:系统被分解为一组高内聚,低耦合的模块。
(1)定义封装的模块
(2)定义新模块对其他模块的依赖
(3)可对其他模块的引入支持
在 JavaScript 中出现了一些非传统模块开发方式的规范 CommonJS 的模块规范,AMD(Asynchronous Module Definition),CMD(Common Module Definition)等 AMD 是异步模块定义,所有的模块将被异步加载,模块加载不影响后边语句运行。
立即执行函数,不暴露私有成员

1
2
3
4
5
6
7
8
9
10
11
12
13
var module1 = (function(){
var _count = 0;
var m1 = function(){
 //...
};
var m2 = function(){
 //...
};
return {
 m1 : m1,
 m2 : m2
};
})();

AMD(Modules/Asynchronous-Definition)、CMD(Common Module Definition)规范区别?

Asynchronous Module Definition,异步模块定义,所有的模块将被异步加载,模块加载不影响后面语句运行。所有依赖某些模块的语句均放置在回调函数中。

区别: 1.对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.
2.CMD 推崇依赖就近(在语句中使用到这个模块的内容时再书写),AMD 推崇依赖前置(在编写代码开始就写好依赖)。看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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()
// ...
})

requireJS 的核心原理是什么?(如何动态加载的?如何避免多次加载的?如何缓存的?)

(1)实现 js 文件的异步加载,避免网页失去响应;
(2)管理模块之间的依赖性,便于代码的编写和维护。
Requirejs 基于 AMD 规范,每个模块用 define 定义,如果这个模块还依赖其他模块,那么 define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。主模块依次加载 require 的模块,使用 require.config()方法,我们可以对模块的加载行为进行自定义。require.config()就写在主模块(main.js)的头部。参数就是一个对象,这个对象的 paths 属性指定各个模块的加载路径。shim 属性,专门用来配置不兼容的模块。具体来说,每个模块要定义(1)exports 值(输出的变量名),表明这个模块外部调用时的名称;(2)deps 数组,表明该模块的依赖性。然后监测 script 的 onload 事件,判断所有模块加载成功,执行 require 的 callback, 如果只带一个参数且不是数组,就是加载成功后 return 模块,会记录加载成功的个数以及各个模块的顺序。

对 CommonJs 和 AMD,CMD 的理解:
都是为了使 js 代码模块化的规范,以前的时候如果一个 js 模块调用另一个模块,需要在 html 中进行 link,而且必须有严格的引入顺序,但是这样又有可能造成阻塞,使页面失去响应。
CommonJS 规定一个文件是一个模块,每个模块内部,module 变量代表当前模块。这个变量是一个对象,它的 exports 属性(即 module.exports)是对外的接口。加载某个模块,其实是加载该模块的 module.exports 属性。内置的 require(路径以/开头是绝对路径,以./开头是相对本文件所在位置的路径,不以“./“或”/“开头,则表示加载的是一个默认提供的核心模块(位于 Node 的系统安装目录中),或者一个位于各级 node_modules 目录的已安装模块(全局安装或局部安装))命令用于加载模块文件。当使用 require 调用该模块时,就获得了 exports 对象。

每个模块内部,都有一个 module 对象,代表当前模块。它有以下属性。
● module.id 模块的识别符,通常是带有绝对路径的模块文件名。
● module.filename 模块的文件名,带有绝对路径。
● module.loaded 返回一个布尔值,表示模块是否已经完成加载。
● module.parent 返回一个对象,表示调用该模块的模块。
● module.children 返回一个数组,表示该模块要用到的其他模块。
● module.exports 表示模块对外输出的值。

CommonJs 是同步加载 JS 脚本,Node.js 使用了这一规范。这一规范和我们之前的做法比较类似,是同步加载 JS 脚本。这么做在服务端毫无问题,因为文件都存在磁盘上,然而浏览器的特性决定了 JS 脚本需要异步加载,否则就会失去响应,因此 CommonJS 规范无法直接在浏览器中使用。

CommonJS 模块的特点如下:
● 所有代码都运行在模块作用域,不会污染全局作用域。
● 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
● 模块加载的顺序,按照其在代码中出现的顺序。

在 Node 中使用 exports=module.exports,可以对 exports 进行返回值的设定,但是不能对 exports 和 module.exports 进行赋值,而且也不能使用 exports 输出,只能使用 module.exports 输出 module.exports = function (x){ console.log(x);};。当使用 require 调用自身模块时就会执行自身的 module.exports
CommonJS 模块的加载机制是,输入的是被输出的值的拷贝。
AMD 规范:前置加载,所有前置模块异步加载结束后,才进行调用 callback。require.js 实现了这个规范。
缓存:所有缓存的模块保存在 require.cache 之中,如果想删除模块的缓存,可以像下面这样写。

1
2
3
4
5
6
// 删除指定模块的缓存
delete require.cache[moduleName];
// 删除所有模块的缓存
Object.keys(require.cache).forEach(function(key) {
delete require.cache[key];
})

注意,缓存是根据绝对路径识别模块的,如果同样的模块名,但是保存在不同的路径,require 命令还是会重新加载该模块。
require.main:
require 方法有一个 main 属性,可以用来判断模块是直接执行,还是被调用执行。直接执行的时候(node module.js),require.main 属性指向模块本身。require.main === module// true。调用执行的时候(通过 require 加载该脚本执行),上面的表达式返回 false。

require 的内部处理流程:
require 命令是 CommonJS 规范之中,用来加载其他模块的命令。它其实不是一个全局命令,而是指向当前模块的 module.require 命令,而后者又调用 Node 的内部命令 Module._load。

1
2
3
4
5
6
7
8
9
Module._load = function(request, parent, isMain) {
// 1. 检查 Module._cache,是否缓存之中有指定模块
// 2. 如果缓存之中没有,就创建一个新的Module实例
// 3. 将它保存到缓存
// 4. 使用 module.load() 加载指定的模块文件,
// 读取文件内容之后,使用 module.compile() 执行文件代码
// 5. 如果加载/解析过程报错,就从缓存删除该模块
// 6. 返回该模块的 module.exports
};

上面的第 4 步,采用 module.compile()执行指定模块的脚本,逻辑如下。

1
2
3
4
5
6
Module.prototype._compile = function(content, filename) {
// 1. 生成一个require函数,指向module.require
// 2. 加载其他辅助方法到require
// 3. 将文件内容放到一个函数之中,该函数可调用 require
// 4. 执行该函数
};

上面的第 1 步和第 2 步,require 函数及其辅助方法主要如下。
● require(): 加载外部模块
● require.resolve():将模块名解析到一个绝对路径
● require.main:指向主模块
● require.cache:指向所有缓存的模块
● require.extensions:根据文件的后缀名,调用不同的执行函数
一旦 require 函数准备完毕,整个所要加载的脚本内容,就被放到一个新的函数之中,这样可以避免污染全局环境。该函数的参数包括 require、module、exports,以及其他一些参数。

1
2
3
(function (exports, require, module, __filename, __dirname) {
// YOUR CODE INJECTED HERE!
});

Module._compile 方法是同步执行的,所以 Module._load 要等它执行完成,才会向用户返回 module.exports 的值。

CMD 规范:就近加载,在需要用到依赖的时候才申明,可同步可异步,Sea.js 实现了这个规范,Sea.js 遇到依赖后只会去下载 JS 文件,并不会执行,而是等到所有被依赖的 JS 脚本都下载完以后,才从头开始执行主逻辑。因此被依赖模块的执行顺序和书写顺序完全一致。

异步加载 JS 的方式有哪些?

(1) defer,只支持 IE
(2) async:
(3) 动态创建 script,插入到 DOM 中,加载完毕后 callBack

DOM 操作——怎样添加、移除、移动、复制、创建和查找节点?

(1)创建新节点

1
2
3
createDocumentFragment()//创建一个DOM片段
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点

(2)添加、移除、替换、插入

1
2
3
4
appendChild()
removeChild()
replaceChild()
insertBefore() //在已有的子节点前插入一个新的子节点

(3)查找

1
2
3
getElementsByTagName()   //通过标签名称
getElementsByName() //通过元素的Name属性的值(IE容错能力较强,会得到一个数组,其中包括id等于name值的)
getElementById() //通过元素Id,唯一性

JS 怎么实现一个类。怎么实例化这个类

可以使用命名空间,js 中的一个类就是一个对象,也可以说就是一个模块,

Jquery 与 jQuery UI 有啥区别?

jQuery 是一个 js 库,主要提供的功能是选择器,属性修改和事件绑定等等。
jQuery UI 则是在 jQuery 的基础上,利用 jQuery 的扩展性,设计的插件。
提供了一些常用的界面元素,诸如对话框、拖动行为、改变大小行为等等

针对 jQuery 的优化方法?

1)基于 Class 的选择性的性能相对于 Id 选择器开销很大,因为需遍历所有 DOM 元素。
2)频繁操作的 DOM,先缓存起来再操作。用 Jquery 的链式调用更好。
比如:

1
var str=$("a").attr("href");

3)

1
for (var i = size; i < arr.length; i++) {}

for 循环每一次循环都查找了数组 (arr) 的.length 属性,在开始循环的时候设置一个变量来存储这个数字,可以让循环跑得更快:

1
for (var i = size, length = arr.length; i < length; i++) {}

如何判断当前脚本运行在浏览器还是 node 环境中?

通过判断 Global 对象是否为 window,如果不为 window,当前脚本没有运行在浏览器中。即在 node 中的全局变量是 global ,浏览器的全局变量是 window。 可以通过该全局变量是否定义来判断宿主环境

那些操作会造成内存泄漏?

内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。
垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0(没有其他对象引用过该对象),或对该对象的惟一引用是循环的,那么该对象的内存即可回收。
setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)
防止内存泄露: 1)、不要动态绑定事件; 2)、不要在动态添加,或者会被动态移除的 dom 上绑事件,用事件冒泡在父容器监听事件; 3)、如果要违反上面的原则,必须提供 destroy 方法,保证移除 dom 后事件也被移除,这点可以参考 Backbone 的源代码,做的比较好; 4)、单例化,少创建 dom,少绑事件。

什么是“前端路由”?什么时候适合使用“前端路由”? “前端路由”有哪些优点和缺点?

什么是前端路由?

路由是根据不同的 url 地址展示不同的内容或页面。前端路由就是把不同路由对应不同的内容或页面的任务交给前端来做,之前是通过服务端根据 url 的不同返回不同的页面实现的。

什么时候使用前端路由?

在单页面应用,大部分页面结构不变,只改变部分内容的使用

前端路由有什么优点和缺点?
优点:

用户体验好,不需要每次都从服务器全部获取,快速展现给用户。

缺点:

使用浏览器的前进,后退键的时候会重新发送请求,没有合理地利用缓存。
单页面无法记住之前滚动的位置,无法在前进,后退的时候记住滚动的位置

用 js 实现千位分隔符?(提示:正则+replace)

1
2
3
4
5
6
7
8
9
function commafy(num) {
num = num + '';
var reg = /(-?d+)(d{3})/;

if(reg.test(num)){
num = num.replace(reg, '$1,$2');
}
return num;
}

我们给一个 dom 同时绑定两个点击事件,一个用捕获,一个用冒泡。会执行几次事件,会先执行冒泡还是捕获?

从上往下,如有捕获事件,则执行;一直向下到目标元素后,从目标元素开始向上执行冒泡元素,即第三个参数为 true 表示捕获阶段调用事件处理程序,如果是 false 则是冒泡阶段调用事件处理程序。(在向上执行过程中,已经执行过的捕获事件不再执行,只执行冒泡事件。)
点击的某一个元素的时候,其祖先元素的事件是遵循先捕获再冒泡,但是在本元素上的事件是按照代码顺序执行的,与冒泡和捕获无关,写在前面的先执行,所以结论是:绑定在被点击元素的事件是按照代码顺序发生,其他元素通过冒泡或者捕获“感知”的事件,按照 W3C 的标准,先发生捕获事件,后发生冒泡事件。

所有事件的顺序是:
其他元素捕获阶段事件 -> 本元素代码顺序事件 -> 其他元素冒泡阶段事件 。
所以本题是会执行两次,但是执行顺序按照代码的书写顺序执行。

使用 JS 实现获取文件扩展名?

1
2
3
function getFileExtension(filename) {
return filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2);
}

String.lastIndexOf() 方法返回指定值(本例中的’.’)在调用该方法的字符串中最后出现的位置,如果没找到则返回 -1。对于’filename’和’.hiddenfile’,lastIndexOf 的返回值分别为 0 和-1 无符号右移操作符(>>>) 将-1 转换为 4294967295,将-2 转换为 4294967294,这个方法可以保证边缘情况时文件名不变。String.prototype.slice() 从上面计算的索引处提取文件的扩展名。如果索引比文件名的长度大,结果为””。

跨域访问

在同源策略影响下,一个域名 A 的网页可以获取域名 B 下的脚本,css,图片等,但是不能发送 Ajax 请求,也不能操作 Cookie、LocalStorage 等数据。AJAX 通信默认情况下,XHR 对象只能访问与包含它的页面位于同一个域(协议,域名,端口相同)中的资源(使用 XMLHttpRequest 对象发起 HTTP 请求就必须遵守同源策略。),这种安全策略可以防止某些恶意行为。

1)—\$.ajax()支持 get 方式跨域:jsonp(JSON with Padding)

JSONP 是包含在函数调用中的 json。e.g.callback({‘name’:’nini’})。
dataType: ‘jsonp’,原先的 beforeSend 和 error 方法都不再触发,原因可能是 dataType 如果指定为 jsonp 的话,就已经不是 ajax 事件了(即 JSONP 始终是无状态连接,不能获悉连接状态和错误事件),只能使用计时器在规定时间内是否接收到了响应。
原理:
动态添加一个 script 标签,而 script 标签的 src 属性是没有跨域的限制的。这样说来,这种跨域方式其实与 ajax XmlHttpRequest 协议无关了。取而代之的则是 JSONP 协议,主要是利用 script 标签不受同源策略(同源策略,即 JavaScript 或 Cookie 只能访问同域下的内容)限制的特性,向跨域的服务器请求并返回一段 JSON 数据。 JSONP 是一个非官方的协议,它允许在服务器端集成 Script tags 返回至客户端,通过 javascript callback 的形式实现跨域访问 JSONP 即 JSON with Padding。由于同源策略的限制,XmlHttpRequest 只允许请求当前源(域名、协议、端口)的资源。如果要进行跨域请求,我们可以通过使用 html 的 script 标记来进行跨域请求,并在响应中返回要执行的 script 代码,其中可以直接使用 JSON 传递 javascript 对象。这种跨域的通讯方式称为 JSONP。onCallback 函数 是浏览器客户端注册的,获取跨域服务器上的 json 数据后,回调的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$.ajax({
async:false,
url: http://跨域的dns/document!searchJSONResult.action,
type: "GET",
dataType: 'jsonp',
jsonpCallback: 'jsonp1236827957501',// //callback名称
data: qsData,
timeout: 5000,
success: function (json) {//客户端jquery预先定义好的callback函数,成功获取跨域服务器上的json数据后,会动态执行这个callback函数
if(json.actionErrors.length!=0){
alert(json.actionErrors);
}
genDynamicContent(qsData,type,json);
}
});

流程:
1)首先在客户端注册一个 callback (如:’jsoncallback’), 然后把 callback 的名字(如:jsonp1236827957501)传给服务器。注意:服务端得到 callback 的数值后,要用 jsonp1236827957501(……)把将要输出的 json 内容包括起来,此时,服务器生成 json 数据才能被客户端正确接收。
2)以 javascript 语法的方式,生成一个 function , function 名字就是传递上来的参数 ‘jsoncallback’的值 jsonp1236827957501。
3)将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。
客户端浏览器,解析 script 标签,并执行返回的 javascript 文档,此时 javascript 文档数据,作为参数,传入到了客户端预先定义好的 callback 函数(如上例中 jquery \$.ajax()方法封装的的 success: function (json))里。(动态执行回调函数)可以说 jsonp 的方式原理上和 script src=”http://跨域/…xx.js”是一致的(qq空间就是大量采用这种方式来实现跨域数据交换的)。JSONP是一种脚本注入(Script Injection)行为,所以也有一定的安全隐患.
原理代码:

1
2
3
4
5
6
7
8
9
//客户端的JAVASCRIPT代码
var script=document.createElement("script");
script.src="http://www.pl4cj.com:8888/5/6/action.php?param=123&callback="+fnName;
document.getElementsByTagName("head")[0].appendChild(script)
//服务器端的PHP代码,一定要有callback来进行回调,在这里加上括号,是让它以语句块的方式来进行解析

<?php
<SPAN style="COLOR: #ff00ff">echo $_GET["callback"]."(".json_encode($_GET).");";
</SPAN>?

jquey 是不支持 post 方式跨域的。

2)—html5 WebSocket 跨域:IE 浏览器目前不支持 WebSocket 通信

WebSocket protocol 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是 server push 技术的一种很棒的实现。
Web Sockets 使用了自定义的协议,未加密的连接是 ws://加密的连接是 wss://。使用自定义的协议而非标准 HTTP 协议的好处是能够在客户端和服务器端传送非常少的数据,而不必担心 http 那样字节级的开销,由于传递数据包小,web socket 非常适合移动应用。
WebSocket 对象也有一个 readyState 属性表示当前状态 WebSocket.OPENING(0):正在创建连接;WebSocket.OPEN(1):已经创建连接;WebSocket.CLOSING(2):正在关闭连接;WebSocket.CLOSE(3):已经关闭连接。
客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var socket = new WebSocket('ws://localhost:8080');// 创建Socket实例,只能是ws或wss,马上尝试创建连接
socket.onopen = function(event) { // 打开Socket
socket.send('I am the client and I\'m listening!'); // 发送任意字符串JSON.stringify(message)
}
socket.onmessage = function(event) { // 接收到服务器消息时触发
console.log('Client received a message',event.data); //event.data返回的是字符串
};
socket.onclose = function(event) { // 监听Socket的关闭
console.log('Client notified socket has closed',event.data);
};
socket.onerror=function(event){ //event有三个属性wasClean-连接是否明确关闭,code-服务器反
console.log('Connection error'); //回的状态码,reason-字符串包含服务器返回的消息
}
socket.close() // 关闭Socket....

onmessage 事件提供了一个 data 属性,它可以包含消息的 Body 部分。消息的 Body 部分必须是一个字符串,可以进行序列化/反序列化操作,以便传递更多的数据。由于 IE 不支持 WebSocket 通信,Guillermo Rauch 创建了一个 Socket.IO 技术。Socket.IO 使用检测功能来判断是否建立 WebSocket 连接,或者是 AJAX long-polling 连接,或 Flash 等。可快速创建实时的应用程序。Socket.IO 还提供了一个 NodeJS API,它看起来非常像客户端 API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script src="http://cdn.socket.io/stable/socket.io.js"></script>
var socket= new io.Socket('localhost',{ // 创建Socket.IO实例,建立连接
port: 8080
});
socket.connect();
socket.on('connect',function() { // 添加一个连接监听器
console.log('Client has connected to the server!');
});
socket.on('message',function(data) { // 添加一个连接监听器
console.log('Received a message from the server!',data);
});
socket.on('disconnect',function() { // 添加一个关闭连接的监听器
console.log('The client has disconnected!');
});
function sendMessageToServer(message) { // 通过Socket发送一条消息到服务器
socket.send(message);
}

服务器端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 需要HTTP 模块来启动服务器和Socket.IO
var http= require('http'), io= require('socket.io');
// 在8080端口启动服务器
var server= http.createServer(function(req, res){
res.writeHead(200,{ 'Content-Type': 'text/html' }); // 发送HTML的headers和message
res.end('<h1>Hello Socket Lover!</h1>');
});
server.listen(8080);
var socket= io.listen(server); // 创建一个Socket.IO实例,把它传递给服务器
socket.on('connection', function(client){
client.on('message',function(event){ // 成功!现在开始监听接收到的消息
console.log('Received message from client!',event);
});
client.on('disconnect',function(){
clearInterval(interval);
console.log('Server has disconnected');
});
});

javascript 跨域有两种情况:
1、基于同一父域的子域之间,如:a.c.com 和 b.c.com
2、基于不同的父域之间,如:www.a.comwww.b.com
3、端口的不同,如:www.a.com:8080www.a.com:8088
4、协议不同,如:http://www.a.comhttps://www.a.com
对于情况 3 和 4,需要通过后台 proxy 来解决,具体方式如下:
a、在发起方的域下创建 proxy 程序
b、发起方的 js 调用本域下的 proxy 程序
c、proxy 将请求发送给接收方并获取相应数据
d、proxy 将获得的数据返回给发起方的 js
代码和 ajax 调用一致,其实这种方式就是通过 ajax 进行调用的。
而情况 1 和 2 除了通过后台 proxy 这种方式外,还可以有多种办法来解决:
1、document.domain+iframe(只能解决情况 1):
a 、在发起方页面和接收方页面设置 document.domain , 并将值设为父域的主域名(window.location.hostname)
b、在发起方页面创建一个隐藏的 iframe,iframe 的源是接收方页面
c、根据浏览器的不同,通过 iframe.contentDocument || iframe.contentWindow.document 来获得接收方页面的内容
d、通过获得的接收方页面的内容来与接收方进行交互
这种方法有个缺点,就是当一个域被攻击时,另一个域会有安全漏洞出现。
当不能使用 web socket 组合 XHR 和 SSE 也是可以实现双向通信的。

3)—Flash:

跟 WebSocket 一样走的 TCP/IP 套接字协议

4)—AJAX long-polling

模拟 websocket

5)—IFrame:

该方法只适合主域相同但子域不同的情况,比如 a.com 和 www.a.com,我们只需要给这两个页面都加上一句 document.domain = ‘a.com’ ,就可以在其中一个页面嵌套另一个页面,然后进行窗体间的交互。

6)CORS:支持 POST

CORS 定义一种跨域访问的机制,可以让 AJAX 实现跨域访问。基本思想是使用自定义的 HTTP 头部允许浏览器和服务器相互了解对方,从而决定请求或响应成功与否。 在发送头信息的时候,会附加一个额外的 Origin 头部,Origin 头部包含请求页面的头部(协议,域名,端口),这样服务器可以很容易的决定它是否应该提供响应。如果服务器认为这个请求可以接受,就在 Access-Control-Allow-Origin 的头部中回发相同的域信息,如果是公共资源就回发。

IE(IE8+支持)对 CORS 的实现:

引入 XDomainRequest,这个对象不发送和接收 cookie,只能设置请求头信息中的 Content-Type,不能访问返回头,只支持 get 和 post,使用与 XHR 类似,open(请求类型,URL),只支持异步,接到响应后,没有办法确定响应的状态代码,响应有效触发 onload 事件,失败触发 onerror 事件也支持 timeout 和 ontimeout 事件.发送 post 请求时需要设置请求头的 contentType 属性。

其他浏览器对 CORS 的实现:

通过 XHR 实现对 CORS 的原生支持,与 XDR 不同,通过 SHR 跨域对象可以访问 status 和 statusText 属性,而且支持同步,限制了不能使用 setRequestHeader()自定义头部,不能发送和接收 cookie,调用 getAllResponseHeader()总返回空字符串。
默认情况下跨源请求不提供凭据(cookie,http 认证以及客户端 ssl 证明),通过设置 withCredentials 为 ture,指定某个请求应该发送凭据。服务器接收后会返回 Access-Control-Allow-Credentials:true
CORS 提供了一种跨域请求方案,但没有为安全访问提供足够的保障机制。jsonp 是 get 形式,承载的信息量有限,所以信息量较大时 CORS 是不二选择;

Comet:长轮询和流。

SSE:长轮询,短轮询和 HTTP 流,半双工。SSE 支持短轮询、长轮询和 HTTP 流,而且能在断开连接时自动确定何时重新连接。,要创建新的 EventSource 对象,并传入一个入口点:
var source=new EventSource(“myevents.php”);
注意:要传入的 URL 必须与创建对象的页面同源。EventSource 的实例有一个 readyState 属性,值为 0 表示正连接到服务器,值为 1 表示打开了连接,值为 2 表示关闭连接。另外还有以下三个事件:
open:在建立连接时触发。
message:在从服务器接收到新事件时触发。
error:在无法建立连接时触发。
服务器返回的数据以字符串的格式保存在 event.data 中。
默认情况下,EventSource 对象会保存于服务器的活动连接。如果连接断开,还会重新连接。这就意味着 SSE 适合长轮询和 HTTP 流。如果想强制立即断开连接并且不再重新连接,可以调用 close()方法。

7)动态创建 script

事件

IE 的事件流叫事件冒泡,IE5.5 更早版本事件冒泡会跳过 html 元素,其他的都一直冒泡到 window 对象,DOM 事件模型的最独特的性质是,文本节点也触发事件(在 IE 不会)。IE 提出的是冒泡流,而网景提出的是捕获流,后来在 W3C 组织的统一之下,JS 支持了冒泡流和捕获流。但是目前 IE6,IE7,IE8 均只支持冒泡流,所以为了能够兼容更多的浏览器,建议大家使用冒泡流。

1
2
3
4
// 阻止浏览器默认行为兼容性写法
event.preventDefault ? event.preventDefault() :(event.returnValue = false);
// 阻止冒泡写法
event.stopPropagation ? event.stopPropagation(): (event.cancelBubble = true);

DOM 事件流:“DOM2 级事件”规定事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。高版本浏览器都会在捕获阶段触发事件对象上的事件,结果就是有两个机会在目标对象上面操作。
事件处理程序:响应某个事件的函数就叫做事件处理程序。DOM0 级的事件处理程序很简单,只会在冒泡阶段被处理。
0 级 DOM:
分为 2 个:一是在标签内写 onclick 事件
     二是在 JS 写 onlicke=function(){}函数
2 级 DOM:
只有一个:监听方法,原生有两个方法用来添加和移除事件处理程序:addEventListener()和 removeEventListener()。
它们都有三个参数:第一个参数是事件名(如 click);
         第二个参数是事件处理程序函数;
         第三个参数如果是 true 则表示在捕获阶段调用,为 false 表示在冒泡阶段调用。
● addEventListener():可以为元素添加多个事件处理程序,触发时会按照添加顺序依次调用。(这也是为什么 DOM0 级事件兼容各种浏览器,我们却还是要使用 DOM2 的原因之一。)
● removeEventListener():不能移除匿名添加的函数。
而 IE 与 DOM 不同,它有自己的方法:attachEvent()和 detachEvent(),由于 IE8 以及更早版本只支持事件冒泡,所以通过 attachEvent()添加的事件处理程序都会被添加到冒泡阶段(所以不需要第三个参数)。注意第一个参数是 onclick,而非 DOM 标准的 click,在 IE 中使用 attachEvent()与使用 DOM0 级方法的主要区别在于事件处理程序的作用域,在使用 DOM0 级方法的情况下,事件处理程序会在其所属元素的作用域内运行,而在使用 attachEvent()方法的情况下,事件处理程序在全局作用域中运行,因此 this 等于 window(这点要特别注意!!!)。attachEvent()也能添加多个事件处理程序,但是事件的执行顺序和添加顺序相反。

区别:如果定义了两个 dom0 级事件,dom0 级事件会覆盖
dom2 不会覆盖,会依次执行。
dom0 和 dom2 可以共存,不互相覆盖,但是 dom0 之间依然会覆盖

事件委托:

对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,将监听器安放到它们的父元素,只指定一个事件处理程序,就可以管理某一类型的所有事件。新添加的子元素也会拥有该事件。
如何能知道是那个子元素被点击:当子元素的事件冒泡到父元素时,你可以检查事件对象的 target 属性,捕获真正被点击的节点元素的引用
var event= event|| window.event; //获得 event 对象兼容性写法
var target = event.target || event.srcElement; //获得 target 兼容型写法
bind 不能为新添的元素添加已经绑定的事件。
JQuery1.3 通过 live 进行事件委托,但是不能在连缀的 DOM 遍历方法后面使用,默认把事件绑定到$(document)元素,如果DOM嵌套结构很深,事件冒泡通过大量祖先元素会导致性能损失;为了避免生成不必要的jQuery对象,可以使用一种叫做“早委托”的hack,即在$(document).ready()方法外部调用.live():

1
2
3
(function($){
$("#info_table td").live("click",function(){/*显示更多信息*/});
})(jQuery);

(function(\$){…})(jQuery)是一个“立即执行的匿名函数”,构成了一个闭包,可以防止命名冲突。使用这个 hack 时,脚本必须是在页面的 head 元素中链接和(或)执行的。因为这时候刚好 document 元素可用,而整个 DOM 还远未生成。不会产生多余的 jquery 对象。
jQuery 从 1.4 开始支持在使用.live()方法时配合使用一个上下文参数:

1
$("td",$("#info_table")[0]).live("click",function(){});

“受托方”就从默认的$(document)变成了$(“#info_table”)[0],节省了冒泡的旅程,上下文对象使用的是

1
$("#info_table")[0]。

jQuery 1.4.2 干脆直接引入了一个新方法.delegate(),支持在连缀的 DOM 遍历方法后面调用。
提示:使用事件委托时,如果注册到目标元素上的其他事件处理程序使用.stopPropagation()阻止了事件传播,那么事件委托就会失效。
undelegate(): 移除 delegate 的绑定。

jquery 事件绑定,on 和冒泡.我们的页面可以理解为一棵 DOM 树,当我们在叶子结点上做什么事情的时候(如 click 一个 a 元素),如果我们不人为的设置 stopPropagation(Moder Browser), cancelBubble(IE),那么它的所有父元素,祖宗元素都会受之影响,它们上面绑定的事件也会产生作用。

1
$('a').bind('click', function() { alert("That tickles!") });

当我们在 a 上面点击的时候,首先会触发它本身所绑定的 click 事件,然后会一路往上,触发它的父元素,祖先元素上所有绑定的 click 事件。

jquery 的绑定事件有几种方式 ,请举例说明其优缺点。

jQuery 中提供了四种事件监听方式,分别是 bind、live、delegate、on,对应的解除监听的函数分别是 unbind、die、undelegate、off。

.bind()

.bind()是最直接的绑定方法 ,会绑定事件类型和处理函数到 DOM element 上, 这个方法是存在最久的,而且也很好的解决了浏览器在事件处理中的兼容问题。但是这个方法有一些 performance 方面的问题,看下面的代码:

1
2
$( "#members li a" ).bind( "click", function( e ) {} );
$( "#members li a" ).click( function( e ) {} );

上面的两行代码所完成的任务都是一致的,就是把 event handler 加到全部的匹配的 a 元素上。 效率:
一方面,我们隐式地把 click handler 加到所有的 a 标签上,这个过程是昂贵的;另一方面在执行的时候也是一种浪费,因为它们都是做了同一件事却被执行了一次又一次(比如我们可以把它 hook 到它们的父元素上,通过冒泡可以对它们中的每一个进行区分,然后再执行这个 event handler)。
优点:
·这个方法提供了一种在各种浏览器之间对事件处理的兼容性解决方案;
·非常方便简单的绑定事件到元素上;
·.click(), .hover()…这些非常方便的事件绑定,都是 bind 的一种简化处理方式;
·对于利用 ID 选出来的元素是非常好的,不仅仅是很快的可以 hook 上去(因为一个页面只有一个 id),而且当事件发生时,handler 可以立即被执行(相对于后面的 live, delegate)实现方式;
缺点:
·它会绑定事件到所有的选出来的元素上;
·它不会绑定到在它执行完后动态添加的那些元素上;
·当元素很多时,会出现效率问题;
·当页面加载完的时候,你才可以进行 bind(),所以可能产生效率问题;

.live()

.live()方法用到了事件委托的概念来处理事件的绑定。它和用.bind()来绑定事件是一样的。.live()方法会绑定相应的事件到你所选择的元素的根元素上,即是 document 元素上。那么所有通过冒泡上来的事件都可以用这个相同的 handler 来处理了。它的处理机制是这样的,一旦事件冒泡到 document 上,jQuery 将会查找 selector/event metadata,然后决定那个 handler 应该被调用。

1
$( "#members li a" ).live( "click", function( e ) {} );

不需要在每个元素上再去绑定事件,而只在 document 上绑定一次就可以了。尽管这个不是最快的方式,但是确实是最少浪费的。

优点:
·这里仅有一次的事件绑定,绑定到 document 上而不像.bind()那样给所有的元素挨个绑定;
·那些动态添加的 elemtns 依然可以触发那些早先绑定的事件,因为事件真正的绑定是在 document 上;
·你可以在 document ready 之前就可以绑定那些需要的事件;
缺点:
·从 1.7 开始已经不被推荐了,所以你也要开始逐步淘汰它了;
·Chaining 没有被正确的支持;
·当使用 event.stopPropagation()是没用的,因为都要到达 document;
·因为所有的 selector/event 都被绑定到 document, 所以当我们使用 matchSelector 方法来选出那个事件被调用时,会非常慢;
·当发生事件的元素在你的 DOM 树中很深的时候,会有 performance 问题;

.delegate()

.delegate()有点像.live(),不同于.live()的地方在于,它不会把所有的 event 全部绑定到 document,而是由你决定把它放在哪儿。而和.live()相同的地方在于都是用 event delegation.

1
$( "#members" ).delegate( "li a", "click", function( e ) {} )

·可以选择把这个事件放到哪个元素上;
·jQuery 仍然需要迭代查找所有的 selector/event data 来决定那个子元素来匹配,但是因为你可以决定放在那个根元素上,所以可以有效的减小你所要查找的元素;
·可以用在动态添加的元素上;
缺点:
·需要查找哪个元素上发生了哪个事件了,尽管比 document 少很多了,不过,还是得浪费时间来查找;

.on()

其实.bind(), .live(), .delegate()都是通过.on()来实现的,.unbind(), .die(), .undelegate(),也是一样的都是通过.off()来实现的

1
2
3
4
5
6
7
8
9
// Bind
$( "#members li a" ).on( "click", function( e ) {} );
$( "#members li a" ).bind( "click", function( e ) {} );
// Live
$( document ).on( "click", "#members li a", function( e ) {} );
$( "#members li a" ).live( "click", function( e ) {} );
// Delegate
$( "#members" ).on( "click", "li a", function( e ) {} );
$( "#members" ).delegate( "li a", "click", function( e ) {} );

优点:
·提供了一种统一绑定事件的方法;
·仍然提供了.delegate()的优点,当然如果需要你也可以直接用.bind();

总结:

用.bind()的代价是非常大的,它会把相同的一个事件处理程序 hook 到所有匹配的 DOM 元素上;
·不要再用.live()了,它已经不再被推荐了,而且还有许多问题;
·.delegate()会提供很好的方法来提高效率,同时我们可以添加一事件处理方法到动态添加的元素上;
·我们可以用.on()来代替上述的 3 种方法;

当一个 DOM 节点被点击时候,我们希望能够执行一个函数,应该怎么做?

(1)直接在 DOM 里绑定事件:

1
<div onclick=”test()”></div>

(2)在 JS 里通过 onclick 绑定:

1
xxx.onclick = test

(3)通过事件添加进行绑定:

1
2
3
btn.addEventListener(“click”,function(){
alert(his.id);
},false);//最后的参数是true,是在捕获阶段调用,false则是在冒泡阶段调用

IE 事件处理程序:

1
2
btn.attachEvent(“onclick”,function(){
alert(“clicked”); } );
JavaScript 的事件流模型都有什么?

“事件冒泡”:事件开始由最具体的元素接受,然后逐级向上传播
“事件捕捉”:事件由最不具体的节点先接收,然后逐级向下,一直到最具体的
“DOM 事件流”:三个阶段:事件捕捉,目标阶段,事件冒泡

跨浏览器的事件绑定和解绑程序:

1
2
3
4
5
6
7
8
9
addHandler:function(element,type,handler){
if(element.addEventListener){ //removeEventListener
element.addEventListener(type,handler,false);
}else if(element.attachEvent){ //detachEvent
element.attachEvent(“on”+type,handler);
}else{
element[“on”+type]=handler; //element[“on”+type]=null;
}
}
IE 和 DOM 事件流的区别:

执行顺序不一样,参数不一样 event||window.event,event.target||event.srcElement,事件类型加不加 on,this 指向不同(四不同)

http 请求头,请求体,cookie 在哪个里面?url 在哪里面?

HTTP 通信机制是在一次完整的 HTTP 通信过程中,Web 浏览器与 Web 服务器之间将完成下列 7 个步骤:
(1) 建立 TCP 连接
(2) Web 浏览器向 Web 服务器发送请求命令 GET/sample/hello.jsp HTTP/1.1
(3) Web 浏览器发送请求头信息
浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。
(4) Web 服务器应答 HTTP/1.1 200 OK
应答的第一部分是协议的版本号和应答状态码
(5) Web 服务器发送应答头信息
(6) Web 服务器向浏览器发送数据
(7) Web 服务器关闭 TCP 连接
HTTP 请求信息由 3 部分组成:请求方法 URI 协议/版本|请求头(Request Header)|请求正文
请求头包含许多有关的客户端环境和请求正文的有用信息。例如,请求头可以声明浏览器所用的语言,请求正文的长度等。
Accept:浏览器可接受的 MIME 类型。
Accept-Charset:浏览器可接受的字符集。
Accept-Encoding:浏览器能够进行解码的数据编码方式,比如 gzip。
Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。
Connection:表示是否需要持久连接。如果 Servlet 看到这里的值为“Keep-Alive”,或者看到请求使用的是 HTTP
Content-Length:表示请求消息正文的长度。
Cookie:设置 cookie,这是最重要的请求头信息之一
Host:初始 URL 中的主机和端口。
Referer:包含一个 URL,用户从该 URL 代表的页面出发访问当前请求的页面。
User-Agent:浏览器类型,如果 Servlet 返回的内容与浏览器类型有关则该值非常有用。
不同浏览器实际发送的头部有所不同,但是上面的基本上所有浏览器都会发送的
可以在调用 open()和 send()方法之间调用 setRequestHeader()方法设置自定义的请求头信息。当是 POST 请求的时候,如果要表单序列化函数 serialize(form),需设置请求头的 Content-type 为 application/x-www-form-urlencoded 但是在 XMLHttpRequest2 级可以使用对象 FormData(form),这里则不用 XHR 对象设置请求头。
请求正文:
请求头和请求正文之间是一个空行,这个行非常重要,它表示请求头已经结束,接下来的是请求正文。请求正文中可以包含客户提交的查询字符串信息。
HTTP 响应也由 3 个部分构成,分别是:协议状态版本代码描述|响应头(Response Header)|响应正文 响应头(Response Header)响应头也和请求头一样包含许多有用的信息,例如服务器类型、日期时间、内容类型和长度等:响应头和正文之间也必须用空行分隔。   cookie 在请求头里面,url 在请求头里。

{}=={}? []==[]? null==undefined?

==, 两边值类型不同的时候,要先进行类型转换

===: 1)、如果类型不同,就[不相等] 2)、如果两个都是数值,并且是同一个值,那么[相等]。 3)、如果两个都是字符串,每个位置的字符都一样,那么[相等];否则[不相等]。 4)、如果两个值都是 true,或者都是 false,那么[相等]。 5)、如果两个值都引用同一个对象或函数,那么[相等];否则[不相等]。 6)、如果两个值都是 null,或者都是 undefined,那么[相等]。

==,根据以下规则:

一、首先看双等号前后有没有 NaN,如果存在 NaN,一律返回 false。
二、再看双等号前后有没有布尔,有布尔就将布尔转换为数字。(false 是 0,true 是 1)
三、接着看双等号前后有没有字符串, 有三种情况:
1、对方是对象,对象使用 toString()或者 valueOf()进行转换;
2、对方是数字,字符串转数字;(前面已经举例)
3、对方是字符串,直接比较;
4、其他返回 false
四、如果是数字,对方是对象,对象取 valueOf()或者 toString()进行比较, 其他一律返回 false
五、null, undefined 不会进行类型转换, 但它们俩相等
题目结果是 false false true

null 和 undefined 的区别?

null 是一个表示”无”的对象,转为数值时为 0;undefined 是一个表示”无”的原始值,转为数值时为 NaN。
当声明的变量还未被初始化时,变量的默认值为 undefined。 null 用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象。
undefined 表示”缺少值”,就是此处应该有一个值,但是还没有定义。典型用法是:
(1) 变量被声明了,但没有赋值时,就等于 undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于 undefined。
(3) 对象没有赋值的属性,该属性的值为 undefined。
(4) 函数没有返回值时,默认返回 undefined。
null 表示”没有对象”,即该处不应该有值。典型用法是:
(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。

什么叫优雅降级和渐进增强?

优雅降级:Web 站点在所有新式浏览器中都能正常工作,如果用户使用的是老式浏览器,则代码会检查以确认它们是否能正常工作。由于 IE 独特的盒模型布局问题,针对不同版本的 IE 的 hack 实践过优雅降级了,为那些无法支持功能的浏览器增加候选方案,使之在旧式浏览器上以某种形式降级体验却不至于完全失效。
渐进增强:从被所有浏览器支持的基本功能开始,逐步地添加那些只有新式浏览器才支持的功能,向页面增加无害于基础浏览器的额外样式和功能的。当浏览器支持时,它们会自动地呈现出来并发挥作用。

异步加载和延迟加载

1).异步加载的方案: 动态插入 script 标签
2)).通过 ajax 去获取 js 代码,然后通过 eval 执行
3).script 标签上添加 defer 或者 async 属性
4).创建并插入 iframe,让它异步执行 js
5).延迟加载:有些 js 代码并不是页面初始化的时候就立刻需要的,而稍后的某些情况才需要的。

说说你对 Promise 的理解?

ES6 原生提供了 Promise 对象。
所谓 Promise,就是一个对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理。Promise 对象有以下两个特点。
(1)、对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。
(2)、一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。 Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

写一个通用的事件侦听器函数?

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
markyun.Event = {
// 页面加载完成后
readyEvent : function(fn) {
if (fn==null) {
fn=document;
}
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = fn;
} else {
window.onload = function() {
oldonload();
fn();
};
}
},
// 视能力分别使用dom0||dom2||IE方式 来绑定事件
// 参数: 操作的元素,事件名称 ,事件处理程序
addEvent : function(element, type, handler) {
if (element.addEventListener) {
//事件类型、需要执行的函数、是否捕捉
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent('on' + type, function() {
handler.call(element);
});
} else {
element['on' + type] = handler;
}
},
// 移除事件
removeEvent : function(element, type, handler) {
if (element.removeEnentListener) {
element.removeEnentListener(type, handler, false);
} else if (element.datachEvent) {
element.detachEvent('on' + type, handler);
} else {
element['on' + type] = null;
}
},
// 阻止事件 (主要是事件冒泡,因为IE不支持事件捕获)
stopPropagation : function(ev) {
if (ev.stopPropagation) {
ev.stopPropagation();
} else {
ev.cancelBubble = true;
}
},
// 取消事件的默认行为
preventDefault : function(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
// 获取事件目标
getTarget : function(event) {
return event.target || event.srcElement;
},
// 获取event对象的引用,取到事件的所有信息,确保随时能使用event;
getEvent : function(e) {
var ev = e || window.event;
if (!ev) {
var c = this.getEvent.caller;
while (c) {
ev = c.arguments[0];
if (ev && Event == ev.constructor) {
break;
}
c = c.caller;
}
}
return ev;
}
};
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
//创建cookie
function setCookie(name, value, expires, path, domain, secure) {
var cookieText = encodeURIComponent(name) + '=' + encodeURIComponent(value);
if (expires instanceof Date) {
cookieText += '; expires=' + expires;
}
if (path) {
cookieText += '; path=' + path;
}
if (domain) {
cookieText += '; domain=' + domain;
}
if (secure) {
cookieText += '; secure';
}
document.cookie = cookieText;
}
//获取cookie
function getCookie(name) {
var cookieName = encodeURIComponent(name) + '=';
var cookieStart = document.cookie.indexOf(cookieName);
var cookieValue = null;
if (cookieStart > -1) {
var cookieEnd = document.cookie.indexOf(';', cookieStart);
if (cookieEnd == -1) {
cookieEnd = document.cookie.length;
}
cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd));
}
return cookieValue;
}
//删除cookie
function unsetCookie(name) {
document.cookie = name + "= ; expires=" + new Date(0);
}

jQuery 中 attr()、prop()、data()用法及区别?

从性能上对比,.prop() > .data() > .attr()。
attr 返回属性的值(标签自带属性和自定意属性都可以返回)
prop 返回 true 或 false(只能返回标签自带属性,不能返回自定义属性)
data 向被选元素附加数据,或者从被选元素获取数据(即 H5 的自定义属性)
attribute 表示 HTML 文档节点属性,property 表示 JS 对象的属性。

1
2
3
<div id="message"class="test" data_id="123"></div>
// 这里的name、age、url均是obj的property
var obj ={ name: "CodePlayer", age: 18, url:"http://www.365mini.com/" };

prop()的设计目标用于设置或获取指定 DOM 元素(指的是 JS 对象,Element 类型)上的属性(property);
attr()的设计目标是用于设置或获取指定 DOM 元素所对应的文档节点上的属性(attribute)。
在 html5 中 DOM 标签可以添加一些 data-xxx 的属性,可以把 data()看作是存取 data-xxx 这样的 DOM 附加信息的方法。data()存取的内容可以是字符串、数组和对象。

1
<div data-role="page" data-last-value="43"data-hidden="true"></div>

正则表达式验证邮箱,电话号码

验证邮箱:re =/^(\w-.)+@(\w-?)+(.\w{2,})+$/
验证电话号码:区号+号码,区号以0开头,3位或4位;号码由7位或8位数字组成;区号与号码之间可以无连接符,也可以“-”连接: re = /^0\d{2,3}-?\d{7,8}$/;

javascript 的本地对象,内置对象和宿主对象

本地对象为 array object regexp 等可以 new 实例化,ECMA 定义好的对象,是引用类型。
内置对象是本地对象的一种,只有 global 和 Math
宿主为浏览器自带的 document,window 等,所有的 BOM 和 DOM 对象。

5 个技巧避免不必要的浏览器兼容性问题

1). CSS3 风格的前缀
如果你正在使用最新的 CSS 代码,比如 box-sizing,或者 background-clip 等,确保你使用了合适的供应商前缀。

1
2
3
4
-moz- /* Firefox 和其他使用 Mozilla 浏览器引擎的浏览器 */
-webkit- /* Safari,Chrome 和其他使用了 Webkit 引擎的浏览器 */
-o- /* Opera */
-ms- /* IE 浏览器(但不总是 IE) */

2). 使用样式重置
你可以使用 normalize.css 或者其他从网络上能找到的样式重置都可以。这里我给出一个,来自于 Genesis 框架。

1
2
3
4
5
6
7
8
9
10
11
12
13
html,body,div,span,applet,object,iframe,h1,h2,
h3,h4,h5,h6,p,blockquote,a,abbr,acronym,address,
big,cite,del,dfn,em,img,ins,kbd,q,s,samp,small,
strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,
dd,ol,ul,li,fieldset,form,label,legend,table,caption,
tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,
embed,figure,figcaption,footer,header,hgroup,input,menu,
nav,output,ruby,section,summary,time,mark,audio,video {
border: 0;
margin: 0;
padding: 0;
vertical-align: baseline;
}

3). 避免 padding 和 width 一起使用
当你给一个包含 width 的元素加 padding,那它实际显示的要比本应显示的大。因为 width 和 padding 会加到一起。比如一个元素 width 是 100px,又给它加了一个 10px 的 padding。那某些浏览器会将该元素显示成 120px。
为了 fix 这个问题,像下面这样做:

1
2
3
4
5
*{
-webkit-box-sizing: border-box; /* Safari/Chrome 等 WebKit 内核浏览器 */
-moz-box-sizing: border-box; /* Firefox 等 Gecko 内核浏览器 */
box-sizing: border-box;
}

4). 清理浮动
确保你把浮动都清理掉了,如果不清理掉,可能会出现很奇怪的情况。想要了解更多关于浏览器处理浮动的原理,可以看 Chris Coyier 的这篇文章。可以用下面 CSS 代码清理浮动:

1
2
3
4
5
.parent-selector:after {
content: "";
display: table;
clear: both;
}

如果你要把你的大部分代码都包起来,有个更简单的方法就是把它添加到你的 wrap 类里面:

1
2
3
4
5
.wrap:after {
content: "";
display: table;
clear: both;
}

这样你的浮动就被清理掉了。
5). 测试一下
搭建你自己的跨浏览器测试环境,或者用 Endtest 也可以。

Object.is() 与原来的比较操作符“ ===”、“ ==”的区别?

两等号判等,会在比较时进行类型转换;
三等号判等(判断严格),比较时不进行隐式类型转换,(类型不同则会返回 false);
Object.is 在三等号判等的基础上特别处理了 NaN 、-0 和 +0 ,保证 -0 和 +0 不再相同,
但 Object.is(NaN, NaN) 会返回 true.
Object.is 应被认为有其特殊的用途,而不能用它认为它比其它的相等对比更宽松或严格。