a 链接两问以及预解析问题

旧文一篇,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、函数内部的代码不会和全局变量一块儿预解析,函数内部的预解析发生在调用时。怎么证明呢??

如果这篇文章对你有用,可以点击下面的按钮告诉我

0

发表回复