旧文一篇,2014年发布于前端乱炖中,是当时学习的整理,现在拿过来备份凑个篇数;
一、链接a相关问题
1、链接 a 的默认行为问题
1.1、基础知识
<a href="http://www.baidu.com">baidu</a>
<!-- a 的默认行为是 href,行为具体实现是 href 中的内容,这个没什么好说的 -->
<a href="javascript:void(0);"></a>
<!--
1、"javascript:" 表示后面是JavaScript代码;
2、void 是 JS 中的一个运算符,作用是用undefined替换后面表达式的返回值,
可以理解为 void 的返回值是 undefined,
void(0) 等价于 void 0 (void 的用法和 typeof 用法类似,两种方式都可以);
3、上面的代码等价于 <a href="javascript:undefined;"></a>
-->
<a href="javascript:;"></a>
<!--
1、分号";"为 JS 中一段代码的结尾,所以点击文字,执行一句空代码
2、JS 中并没有强制要求一句代码后必须加分号,所以上面的代码等价于
<a href="javascript:"></a>
-->
想要在 href 中阻止 a 的默认行为,”javascript:”后只能放 语句 或 返回 undefined 的表达式。
- Chrome中阻止 a 的默认行为,”javascript:” 后可以是 false、true、null、var i=1、var i, i ++;
- IE和FireFox中阻止 a 的默认行为,”javascript:”后只能是 语句(语句没有返回值) 或 返回undefined的表达式;
关于表达式和语句的具体内容,可以移步 justjavac 的 代码之谜(二)- 语句与表达式
1.2、问题
1.2.1、既然”javascript:”后的代码为 JS 代码,那么它的作用域是什么呢?
<script type="text/javascript">
window.onload = function(){
document.getElementById("btn").onclick = function(){
alert(dandan);
};
};
</script>
<a href="javascript:var dandan=1;">给dandan赋值</a>
<input type="button" id="btn" value="测试" />
代码结果:点击 ”给dandan赋值” 后再点击”测试”按钮,弹出1;页面加载后,直接点击”测试”按钮,会报错;
结论:href 中 javascript: 后的代码作用域是全局的,但要在执行默认行为之后才能生效。
1.2.2、在 a 的 onclick 事件中 return false; 也是能够阻止默认行为的,那么这两种方式的先后顺序是怎样的?
<script type="text/javascript">
window.onload = function(){
document.getElementById("test").onclick = function(){
alert("run in click");
};
};
</script>
<a id="test" href="javascript:alert('run in href');">测试顺序</a>
代码结果: 先弹”run in click”,再弹”run in href”;
结论:点击a,先执行onclick事件,然后执行默认行为; return false是通过屏蔽默认行为的执行来阻止默认行为的;
综上所述,阻止a的默认行为有两种方式:
替换默认行为,如 <a href="javascript:;"></a>
屏蔽默认行为,如在 a 的 onclick 中 return false;
扩展:理论上 href 可以在一定程度上替代 a 的 onclick 事件,不过在实际中操作性很低,在 href 中调用一个函数和在 onclick 中调用一个函数差别不大,谁会这样用呢:
<a href="javascript:show();" onclick="show()" >测试</a>
1.2.3、一个有意思的问题,当 a 具有 target 属性时,替换默认行为,IE 和 Firefox 中失效,即点击下面代码中的 a ,会打开一个新标签页,Chrome依旧会阻止默认行为;屏蔽默认行为的方式不受影响。
<a id="testa" href="javascript:;" target="_blank">跳转</a>
猜想:href 和 target 共同组成了 a 的默认行为,具体什么原因,暂时无解,是不是需要研究 w3c 标准。
2、浏览器拦截链接 a 打开新窗口
通过a在新标签页打开链接地址,以下代码不会被浏览器拦截(这是基本用法啊,当然不会拦截):
<a id="testa" href="http://hi.baidu.com/superfiresun" target="_blank">跳转</a>
但有一种情况,在 Chrome 和 Firefox 中会被拦截:
非页面控件主动直接触发(非人直接点击或其他操作触发)的事件中请求打开一个新窗口会被拦截;
打开一个新窗口有几种方式:
window.open("")、点击链接 a (target="_blank")、form 提交(target="_blank")
,
<script type="text/javascript">
window.onload = function(){
//1、页面加载的时候,打开新窗口,都会被阻止
window.open("http://hi.baidu.com/superfiresun", "_blank");
document.getElementById("form1").onsubmit();
document.getElementById("test1").click();
//2、在 jquery 的 ajax 的回调函数中跳转,也会被阻止;
var jqXHR = $.ajax({ …… });
jqXHR.done(function(response, status, xhr){
window.open("http://hi.baidu.com/superfiresun", "_blank");
document.getElementById("form1").onsubmit();
document.getElementById("test1").click();
});
};
</script>
<form id="form1" target="_blank" action="http://hi.baidu.com/superfiresun"
<a id="test1" href="http://hi.baidu.com/superfiresun" target="_blank">skip</a>
</form>
这应该与浏览器的安全机制有关系,是为了阻止当年盛行的弹出广告吧。
二、预解析/预加载和全局变量转换
1、预解析
- JS 变量会在该作用域下的代码执行之前进行预解析,即会将变量的声明提到该作用域中的所有js代码之前;
- 局部变量会在其所在函数被调用时进行预解析;
- 预解析是有条件的,必须是声明(像var b;)或者声明+赋值(var b=1;)。
代码示例:
alert(abc); //弹出 undefined
var abc = "super";
//上面两句代码的执行顺序
var abc;
alert(abc);
abc = "super";
如果 var abc = “super”; 一句删除,则 alert(abc) 会报错,信息为abc未定义。
2、自动转换全局变量
赋值语句中,如果左侧变量没有声明,那么 JS 会将它作为全局变量,添加到 window 上, 这种情况是有条件的:必须是单独的赋值语句,像 b=1;;不可以是声明+赋值,如 var a = b+1。
function show(){
a = 1; //自动将a转换成 window.a
}
show();
alert(a); // 弹出1
3、实例
下面是一段代码是面试经常遇到的问题,结合上面两个知识点,详尽地分解下代码。源代码:
var b = 1;
function show(){
alert(b); //undefined
var b = 12;
alert(b); //12
}
show();
alert(b); //1
解析执行过程如下:
var b;
function show(){};
b=1;
show();
var b; //覆盖全局变量b
alert(b); //undefined
b=12;
alert(b); // 12
alert(b); //全局变量 b=1
详细的解析执行过程如下:
//1 =============================== 全局环境预解析开始
var b;
function show(){}; //疑问1:有没有可能是 var show; 呢
// ================================ 全局环境预解析结束
//2 =================================== 全局环境代码执行开始
b=1; //show = function(){}; //如果疑问1的答案是前者,这句话应该是有的
show();
// -------------------------------- 进入show() function show(){
//2.1 ----------------------------- 函数show中预解析开始
var b; //覆盖全局变量b
// -------------------------------- 函数show中预解析结束
//2.2 ----------------------------- 函数show中的代码执行开始
alert(b); //undefined
b=12;
alert(b); // 12
// -------------------------------- 函数show中的代码执行结束
// -------------------------------- }结束show()
alert(b); //全局变量 b=1
// =================================== 全局环境代码执行结束
除了函数那儿有点儿疑问,其他还是很明确的,这样一来,再遇到这种问题就不可能出错了。
1、个人认为上面代码中的”疑问1″是不成立的,原因如下: 摘自“JavaScript 高级程序设计 第三版”: “解析器在向执行环境中加载数据时…解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问)…”,函数声明就是这样的 function show(){};
2、函数内部的代码不会和全局变量一块儿预解析,函数内部的预解析发生在调用时。怎么证明呢??