JavaScript前端面试
系列文章:
JS一些算法题:
FE-interview-questions,满意点个star。(待更新)
0. 序
本文没有太多基础性的js内容,推荐一本书《JavaScript高级程序设计》,基础不好的同学建议先学习该本书。重点内容:事件,闭包,作用域练,原型,继承。
1.情简述一下javascript的执行环境(执行上下文)以及所涉及到到一些概念?(该文的整体笔记丢失,详细内容建议查看引用链接)
每当控制器转到可执行环境时,就会进入一个执行环境。
而执行环境大致分为三类:
- 全局环境
- 函数环境
- eval(个人不了解,开发也不会用到不做讲解)
执行环境是靠栈来存储,每当开始执行js代码的时候,就进入到了全局执行环境,于是乎,就把全局执行环境压到了这个栈(我们暂时称它为执行环境栈)中。
而在全局代码中,会定义很多函数,函数中又会定义很多函数,而每当控制器执行到函数时,则就进入到了这个函数的执行环境中。
2.sessionStorage,localStorage,cookie区别
- 共同点:都是浏览器端的数据存储,同源;
- 不同点:
- cookie在同源的http请求中携带,浏览器与服务器之前回传。cookie有路径的概念;cookie大小不超过4k。
- sessionStorage(浏览器关闭窗口前有效)和localStorage(一直有效)达到5M或者更大。
- cookie在设置的有效期前有效
- session不在不同的浏览器窗口中共享,loacl共享,cookie也共享。
- sessionStorage和localStorage支持事件机制
3.localStorage应该如何进行存储?
// 将数据存在loaclStorage中
export function saveToLocal(id, key, value) {
let seller = window.localStorage.__seller__;
if (!seller) {
seller = {};
seller[id] = {};
} else {
seller = JSON.parse(seller);
if (!seller[id]) {
seller[id] = {};
}
}
seller[id][key] = value;
window.localStorage.__seller__ = JSON.stringify(seller);
};
export function loadFromLocal(id, key, def) {
let seller = window.localStorage.__seller__;
if (!seller) {
return def;
}
seller = JSON.parse(seller)[id];
if (!seller) {
return def;
}
let ret = seller[key];
return ret || def;
};
4.GET与POST的区别?
- GET与POST都是HTTP协议中的请求发送方式,实际上他们都是TCP,所能做的事情都是一样的。
- 不同在于,HTTP规定,发送GET请求时,在HTTP Header的请求行上就声明GET,反之POST就声明POST。
- HTTP要求,GET把数据放在url上,所以GET常用于发少量的数据用于查询;POST把数据放在body中,数据量相对较大用来存储。
- GET请求只发一次,POST发两次,header返回后再发送date。
5.同源策略指的是什么?
- 同源指的是以下三个都相同:
- 协议相同
- 域名相同
- 端口相同
- 所谓同源策略指的是:浏览器对不同源的脚本或者文本的访问方式进行的限制。比如源a的js不能操作引入的源b的元素属性。
- 限制主要为:
- Cookie、LocalStorage 和 IndexDB 无法读取。
- DOM无法获取
- AJAX请求不能发送
6.如何才能跨域,跨域方式有哪些?
- 设置document.domain
Cookie是服务器写入浏览器的一小段信息,只有同源的网页才能共享。但是,两个网页的一级域名相同,只是二级域名不相同,浏览器允许通过设置document.domain共享Cookie。
在 www.a.com/a.html 中
document.domain = 'a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://www.script.a.com/b.html';
ifr.display = none;
document.body.appendChild(ifr);
ifr.onload = function(){
var doc = ifr.contentDocument || ifr.contentWindow.document;
// 这里就能b.html的cookie了
ifr.cooike = doc.cookie;
};
在 script.a.com/b.html 中
document.cookie = "test1=hello";
document.domain = 'a.com';
这样不仅能访问b的cookie,还可以访问b的dom,但是无法访问到LocalStorage 和 IndexDB,而且主要限制是a,b必须一级域名必须相同。
- window.name
浏览器窗口都有window.name这个属性,这个属性最大的特点是,无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页就可以读取它。
在b.com/data.html中有这样的数据
<script>
window.name = '我是你们想要的数据。';
</script>
现在a.com/index.html想获取这个数据应该怎么办?通过iframe充当中间人
<script>
function getDate(url){
var iframe = document.createElement('iframe');
iframe.style.display='none';
var state = 0;
iframe.onload = function(){
if(state == 1){
var dataPage = iframe.contentWindow;
var data = dataPage.name;
console.log(data);
dataPage.document.write('');
dataPage.close();
document.body.remove(iframe);
}else
{
state = 1;
iframe.contentWindow.location = url;
}
};
// 代理源,创建好且设置了document.name
iframe.src = 'a.com/b.html';
}
getData('b.com/data.html');
</script>
些人可能会问为什么不直接把iframe的src设置为目的源(b.com/data.html)来获取数据,而是在设置为目的源之后,还要把src设置为同域名下的其他源(a.com/b.html)才获取数据?
如果直接设置src,那么iframe和本网页(a.com/index.html)会因为同源策略限制不能访问。而把iframe先设置为目的源,再设置为同域名下的其他源,那么同域名下的其他源就和目的源共享了一个窗口,故拥有同样window.name,并且由于是同域名下的源,并且设置了domain,故可以访问目标源的window.name。
state用来干什么 ?
每次设置src,都会刷新,state是标志位,让获取了数据就销毁掉。
- window.postMessage
上面的方法都是破解,H5提供了官方的API:window.postMessage。允许跨窗口通信,不论这两个窗口是否同源。
// 举例来说,父窗口http://aaa.com向子窗口http://bbb.com发消息,调用postMessage方法就可以了。
var popup = window.open('http://bbb.com', 'title');
popup.postMessage('Hello World!', 'http://bbb.com');
postMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为*,表示不限制域名,向所有窗口发送。
// 子窗口向父窗口发送消息的写法类似。
window.opener.postMessage('Nice to see you', 'http://aaa.com');
// 父窗口和子窗口都可以通过message事件,监听对方的消息。
window.addEventListener('message', function(e) {
console.log(e.data);
},false);
// event.source:发送消息的窗口
// event.origin: 消息发向的网址
// event.data: 消息内容
下面的例子是,子窗口通过event.source属性引用父窗口,然后发送消息。
window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
event.source.postMessage('Nice to see you!', '*');
}
- AJAX
利用jsonp
// 首先,网页动态插入<script>元素,由它向跨源网址发出请求。
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src;
document.body.appendChild(script);
}
window.onload = function () {
addScriptTag('http://example.com/ip?callback=foo');
}
function foo(data) {
console.log('Your public IP address is: ' + data.ip);
};
阮一峰老师:浏览器同源政策及其规避方法
7. AJAX模型是什么?
- AJAX全称为“AsynchronousJavaScript and XML” 异步的JavaScript和Xml,是一种创建交互式网页应用开发的新技术。
- 运用AJAX
// 创建xmlHttpRequest对象
function createXmlHttp(){
var xmlHttp;
if(window.XMLHttpRequest){
xmlHttp = new XMLHttpRequest();
}else{
xmlHttp = new ActiveXObject('Microsoft.XMLHTTP');
}
return xmlHttp;
}
var xhr = createXmlHttp();
// 发送请求GET或者POST
// 第三个参数代表是否异步
xhr.open('GET',url+'?date='+ new Date().getTime(),true);
//GET发送的数据在url上 POST发送的数据在send()中
xhr.send();
// 回调函数响应
xhr.onreadystatechange=function(){
if(xhr.readyState == 4 && xhr.status == 200){
document.getElementById("myDiv").innerHTML=xhr.responseText;
}
}
// xhr.readyState存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。
// 0: 请求未初始化
// 1: 服务器连接已建立
// 2: 请求已接收
// 3: 请求处理中
// 4: 请求已完成,且响应已就绪
// status:状态码
// 如果是同步,直接调用xhr.responseText即可
8.为什么说jsonp不是真正的ajax?
- 为什么说jsonp是ajax?
因为他们目的一样,都是请求一个url,然后把服务器返回的数据进行处理。故jquery把jsonp封装为ajax的一种形式。
- 那为什么jsonp不是真正的ajax?
ajax和jsonp其实本质上是不同的东西,ajax的核心通过XmlHttpRequest获取非本页面内容,而jsnop的核心是动态加载script来调用服务器提供的js脚本。
9.property 和 attribute有什么区别?
- property表示DOM的基础属性。
<input id='ipt' value='123' other='1111' />
console.log(document.getElementById('ipt'));
可以发现有attributes,value,id,以及很多我们并没有赋值的属性,但是并没有other属性。value,id这样的就叫做DOM的基础属性。
- 正如打印出来的结果还有一个叫attributes的属性,类型是NamedNodeMap。这里就可以说attributes是DOM 的property的其中一个。
点击开这个你会发现有
0: id
1: value
2: other
length: 3
__proto__: NamedNodeMap
这个属性包含了显示定义在标签上的属性。
console.log(ipt.attributes.other); // other="1111";
并且
console.log(typeof ipt.attributes.other); // object
返回的是一个对象。
- property能够从attribute中得到同步,attribute不会同步property上的值;
in1.value = 'new value of prop';
console.log(in1.value); // 'new value of prop'
console.log(in1.attributes.value); // 'value="1"'
in1.attributes.value.value = 'new value of attr';
console.log(in1.value); // 'new value of attr'
console.log(in1.attributes.value); // 'new value of attr'
10. 关于==的强制类型转换
-
重点:
- Number与String比较,String转Number。
- Number与Object比较,Object转String。
- 当比较中出现Boolean,无论另一个数据是什么类型,都先把Boolean转为Number。
- undefined与null,undefined与undefined,
null与null都返回true。 - Object 与 Object只有引用相同返回true。
-
数据类型转化为Boolean的规则:
- String的 “” 转换为Boolean为false其他都为true
- Number的0和NaN转换为Boolean为false其他都为true
- Object的null转换为Boolean为false其他都为true
- Undefined转换为false 不能转换为true
[] == ![] //true
// 1.由于!的优先级比较高,先执行![],[]是空数组,数组是对象,由2.3(需要了解的知识文字序号),则[]转换为boolean结果为true,![]结果就为false,表达式变为判断 []==false
// 2.根据1.3,将false转为Number,结果为0,表达式变为判断 [] == 0
// 3.根据1.2,将[]变为String,结果为"",表达式变为判断 "" == 0
// 4.根据1.1,将""变为Number,结果为0,表达式变为判断 0 == 0
返回结果 true
console.log(1 == true); // 文字1.3,Number(true) = 1 -> 1 == 1 true
console.log(2 == true); // 文字1.3,Number(true) = 2 -> 2 == 1 false
console.log(0 == false);//文字 1.3,Number(false)= 0 -> 0 == 0 true
console.log(NaN == NaN);// 图片1.c.i/1.c.ii false
console.log({} == {});// 图片1.f {}是对象,比较引用指向的空间,因为是两个不同的空对象,地址也不一样 false
console.log([] == []);// 同理
console.log(null == 0);// 文字1.2 null是对象,String(null) == "null" -> "null" == 0 ,文字1.1 Number("null") == NaN -> NaN == 0 false
console.log(undefined == 0);// 这里将执行String(undefined),之后执行步骤同上
总结:
undefined == null,结果是true。且它俩与所有其他值比较的结果都是false。
String == Boolean,需要两个操作数同时转为Number。
String/Boolean == Number,需要String/Boolean转为Number。
Object == Primitive,需要Object转为Primitive(具体通过valueOf和toString方法)。
11.支持正则表达式的方法:
- RegExp 对象方法
// 1. complie() 用于编译正则表达式或改变正则表达式
let str = 'woman1manm';
let patt = /(wo)?man/g;
let str2 = str.replace(patt,'2222'); // 222212222m
let patt2 = /2222/g;
patt.compile(patt2); // 改变了正则表达式
let str3 = str2.replace(patt,'woman'); // 返回 woman1womanmm
// 2. exec() 未匹配到返回一个null,匹配到则返回一个数组arr
// arr[0]表示匹配到的这个字符串,arr[1]...arr[n]表示依次匹配到的圆括号的值,如果有的话
// 并且arr有两个属性,index表示第一次匹配到的字符串下标,input表示需要匹配的字符串本身
let str = '22222womanmmmmmmmanmmmmman';
let patt = /(wo)?(man)/g;
let what = patt.exec(str);
console.log(what);
// 返回 :
// [ 'woman',
// 'wo',
// 'man',
// index: 5,
// input: '22222womanmmmmmmmanmmmmman' ]
// 因为patt是全局搜索,所以exec还可以继续搜索,并且这个时候patt还有一个属性lastIndex表示 匹配文本的最后一个字符的下一个位置
console.log(patt.lastIndex) // 10
what = patt.exec(str);
console.log(what);
// 返回 :
// [ 'man',
// 'undefined',
// 'man',
// index: 16,
// input: '22222womanmmmmmmmanmmmmman' ]
//当patt再也匹配不到时,lastIndex会被重置为0
//3. test(str) 检索字符串是否有这个模式,有返回true否则false
- String对象方法
// 1. search(patt),搜索字符串是否含有这个匹配,如果有就返回其实位置下标,没有就返回-1。
// 2. match(patt), 在字符串内检索指定的模式,返回存放匹配结果的数组,该数组内容依赖于regexp是否具有g。
// 如果没有g,则返回的结果与exec相同。既,str.match(patt) 和 patt.exec(str)的返回结果相同。如果有g,是全局匹配,则会返回所有匹配结果。
let str = '22222womanmmmmmmmanmmmmman';
let patt = /(wo)?(man)/g;
let result = str.match(patt);
console.log(result); // [ 'woman', 'man', 'man' ]
// 3. replace(patt,str) ,返回一个新的字符串,如果有g将所有匹配值换成目标字符串,没有则替换第一个。
// $1、$2、...、$99代表圆括号所匹配的值
var name = "Doe, John";
console.log(name.replace(/(\w+)\s*, \s*(\w+)/, "$2 $1")); // John Doe
12.常用的正则表达式:
- 匹配一个邮箱:
/^([a-zA-Z0-9])+([a-zA-Z0-9_-\.])*@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/
开始必须是数组英文字母开头,然后是可以是任意数字字母下划线或者点,然后@,之后是邮箱名,域名。
- 匹配ip地址:
/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/
- 匹配电话号码
/^[1][34578][0-9]{9}$/
- 检查输入字符串是否是带小数的数字格式,可以是负数
/^-?(\d+)[\.]?(\d+)$/
13. es6中的let,const和var有什么区别?
- let的作用域是外层块,不是外层函数,而var是外层函数。
if(true){
let a =0;
var b =0;
}
console.log(a) // 报错,未定义
console.log(b) // 就是0
- let没有声明提升,但是有暂时性死区。
console.log(a); // 结果是报错,不是undefined
let a = 0;
if(true){
var c = 0; // 报错,因为c已经定义在此区域了。
let c = 0;
}
- 重新声明会报错
- const与let一样,不同在于const引用的地址不能改变,值也不能改变。声明必须赋值。
14.var f = function g(){ return 23; }; typeof g()结果是什么?为什么?
- typeof f :function,因为f本来就是一个函数。
- typeof f():number,因为f()先执行,返回了一个数字,23.
- typeof g:undefined,因为typeof一个未声明的变量,都默认返回undefined。
- typeof g():报错,g未定义。因为function是函数表达式的形式,并且赋值给了f,所以这个函数就算f,而g只是一个匿名,只能在函数f中使用,在全局访问不到。
15.最完美的数组去重:
function normalize(arr) {
// 判断传入的是否是数组
if (arr && Object.prototype.toString.call(arr) !== '[object Array]') {
return;
}
// 当作为对象属性时,会发生强制类型转换,为了区别类型,创建不同对象来去重。
// 例如: 1,'1' 都会被转化为 obj['1'],导致去重出错, 故区别类型。
const objectSet = {};
// Object.create(null), 以防和原型上的函数名冲突。
// 例如: obj[toString], 本来是没有重复的,但是obj[toString]会查找到
// 原型上的toString方法,“temp in map”会为true,故利用Object.create(null)
// 使原型为空
objectSet.number = Object.create(null);
objectSet.string = Object.create(null);
objectSet.array = Object.create(null);
objectSet.boolean = Object.create(null);
objectSet.object = Object.create(null);
objectSet.undefined = Object.create(null);
let len = arr.length, temp, type, map;
for (let i = len - 1; i >= 0; i--) {
temp = arr[i];
// 取相应的去重对象。
if(Object.prototype.toString.call(temp) === '[object Array]'){
map = objectSet.array;
}else{
type = typeof temp;
map = objectSet[type];
}
if (temp in map) {
arr.splice(i, 1);
} else {
map[temp] = true;
}
}
return arr;
}
const arr = [1, '1', 1, 'toString', ['toString'], 1, '', 2, '', null, 'null', 2, 2, null, 3, 3];
console.log(normalize(arr));
16. 把字符串转化为大小写:
toUpperCase(): 转大写
toLowerCase(): 转小写
17. 为什么js是阻塞加载的?
当引用了js的时候,浏览器发送一个js请求,就会一直等待请求的返回,因为浏览器需要一个稳定的dom结构,而js中很有可能直接改变了dom结构,为了防止渲染好的树又被js代码修改,所以就会阻塞其他的下载和呈现。
18. 数组长度问题:
- 为什么结果不是3?
var arr = [];
arr[0] = 'a';
arr[1] = 'b';
arr.foo = 'c';
console.log(arr.length); //2
length返回的是array数组索引长度。
var arr = [];
arr[9] = 1;
console.log(arr.length); // 10;
console.log(arr[8]); // undefines;
19. ToPrimitive()是什么?
简单来说,就是将对象(object)转化为原始类型(undefined, null, boolean ,string, number)的方法,在计算一些强制类型转换时,都需要将对象转化为原始类型再进行计算。
ToPrimitive(obj)等价于,obj.valueOf(),如果为原始值则返回结果,否则,计算obj.toString(),如果是原始值返回,否则抛出异常。
注:此处有个例外,即Date类型的对象,它会先调用toString()方法,后调用valueOf()方法。
- 不同对象的valueOf,toString结果不同
// Array
console.log([,2,'ss'].valueOf()); // 返回数组本身[,2,'ss'] ,仍然不是原始类型
console.log([,2,'ss'].toString()); // 返回每个元素中间以逗号隔开的字符串:,2,ss
// Object
console.log({a:11,b:'uu'}.valueOf()); // 返回对象本身{ a: 11, b: 'uu' }
console.log({a:11,b:'uu'}.toString()); // 返回字符串 [object Object]
console.log(function(){return 1}.valueOf()); // 返回[Function]代表函数本身
console.log(function(){return 1}.toString()); // 返回函数定义的字符串 :function (){return 1}
20. '+new Array(017)' 的结果?
- 017是8进制,为15 -> + new Array(15);
- +为强制类型转Number -> Number(new Array(15));
- new Array(15) -> ToPrimitive([,,,,,,,,,,,,]);
- ToPrimitive([,,,,,,,,,,,,]) -> [,,,,,,,,,,].valueOf() 结果为 [,,,,,,,,,,]
- [,,,,,,,,,,].toString() -> ',,,,,,,,,'
- Number(,,,,,,,,,) 为NaN
21. 不能用变量提升的思路取思考
var foo = {n: 1};
var bar = function(foo){
console.log(foo.n)
var foo = {n:2};
}
bar(foo);
这里有正常的传参和执行。
22. innerHTML, outerHTML, innerText, outerText的区别?
<div id="div1"><span>abcd</span></div>
//写
//div.innerHTML = "<p>大米</p>"; //div保留
//div.outerHTML = "<p>大米</p>"; //div也被取代了
//div.innerText = "米老鼠";
//console.log(div); //div中包裹米老鼠
div.outerText = '<p>www</p>' //将原来单元素直接变成了纯文本,包括外围<div>
23. Array方法的返回结果?
- 返回本身:reverse(),sort(),
- 返回一个副本:concat(),slice(),map(),filter
- 返回其他:
- 改变数组:
- pop:返回arr最后一个元素
- push:返回新的长度
- shift:返回arr第一个元素
- unshift:返回新的长度
- splice:返回被删除的项目
- 不改变:
- join:返回字符串
- some/every:boolean
- findIndex:下标
- 改变数组:
24. 常用排序时间复杂度?
25. AMD 和 CMD 的区别有哪些?
- AMD规范:github.com/amdjs/amdjs-api/wiki/AMD,是RequireJS在推广过程中对模块化定义的规范化产出。
- CMD规范:github.com/seajs/seajs/issues/242CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。
- 区别
- 对于模块的依赖,AMD是提前执行,CMD是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.
- CMD 推崇依赖就近,AMD 推崇依赖前置。
- AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。CMD 里,每个 API 都简单纯粹。
// 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() // ...
})
- 世界上,有两种比较流行的 JavaScript 模块化体系,一个是 Node 实现的 CommonJS,另外一个是 AMD。很多类库都同时支持 AMD 和 CommonJS,但是不支持 CMD。或许国内有很多 CMD 模块,但并没有在世界上流行起来。
26.keydown、keypress、keyup的区别
- keypress主要用来捕获数字(shift+数字的符号)、字母(区分大小写)、小键盘等除了F1-12、SHIFT、Alt、Ctrl、Insert、Home、PgUp、Delete、End、PgDn、ScrollLock、Pause、NumLock、{菜单键}、{开始键}和方向键外的ANSI字符。keypress事件不能对系统功能键(例如:删除,后退等,还有中文输入法)进行正常的响应。
- keypress只能响应单个字符
- keydown和keyup可以响应除prscrn的所有按键,可以捕捉组合键。
- keydown和keyup不可以判断字符大小写,keypress可以。(keyCode)
- keypress事件的which值无法区分主键盘上的数字键和附键盘数字键的,而keydown、keyup的which值对主附键盘的数字键敏感。
27. 原生实现点击按钮切换弹框的隐藏,且点击弹框以外的地方使弹框隐藏:
<!DOCTYPE html>
<html lang="en">
<head>
<style>
html {
height: 100%;
}
body {
height: 100%;
}
.div {
height: 300px;
width: 300px;
background-color: rosybrown;
transition: opacity 0.4s ease;
}
</style>
</head>
<body>
<button class="btn1">toggle</button>
<div class="div" style="opacity: 1;">
<button class="btn2">console1</button>
<button class="btn3">console2</button>
</div>
<br />
<div class="div"></div>
<script>
var toggle = document.getElementsByTagName('button')[0];
var console1 = document.getElementsByTagName('button')[1];
var console2 = document.getElementsByTagName('button')[2];
var div = document.getElementsByClassName('div')[0];
var isShow = true;
toggle.onclick = function () {
if (isShow) {
div.style.opacity = '0';
isShow = false;
} else {
div.style.opacity = '1';
isShow = true;
}
}
div.addEventListener('click', function (e) {
e.stopPropagation();
}, false);
console1.addEventListener('click', function (e) {
console.log(1);
// e.stopPropagation();
}, false);
console2.onclick = function (e) {
console.log(2);
// e.stopPropagation();
}
document.body.addEventListener('click', function (e) {
var target = e.target;
// 点击toggle事件也会冒泡到body上,本身toggle的效果就没了,所以事件在toggle时直接退出不执行
if(target === toggle){
return;
}
div.style.opacity = '0';
isShow = false;
}, false);
</script>
</body>
</html>
思路:切换隐藏很简单,主要是点击周围的地方隐藏弹框。那么就在body上绑定一个事件,点击页面任何一个地方都隐藏,然后再去除弹框区域就行了。如何去除,就在弹框上绑定一个click事件,此事件发生后停止冒泡,那么点击弹框上任何内容的时候,事件都会冒泡到弹框上,然后弹框再停止冒泡,那么事件就不会走到body上,也就不会在弹框区域触发隐藏弹框事件。
28. js单例模式:
var Universe;
(function () {
var instance;
Universe = function Universe() {
if (instance) {
return instance;
}
instance = this;
// 其它内容
this.start_time = 0;
this.bang = "Big";
};
} ());
//测试代码
var a = new Universe();
var b = new Universe();
alert(a === b); // true
a.bang = "123";
alert(b.bang); // 123
var single = (function(){
var unique;
function getInstance(){
if( unique === undefined ){
unique = new Construct();
}
return unique;
}
function Construct(){
// ... 生成单例的构造函数的代码
}
return {
getInstance : getInstance
}
})();