以下是我在学习过程中遇到的一些javascript的小知识点,保存下来以供参考,持续更新中。。
1、eval函数的作用域
eval
函数会在当前作用域中执行一段 JavaScript 代码字符串。但是 eval
只在被直接调用并且调用函数就是 eval
本身时,才在当前作用域中执行。如果执行了类似于 f = eval; f(); 这样的语句,将等价于在全局作用域中调用 eval,
例如下面的这段代码:
(原文来自《javascript秘密花园》:http://bonsaiden.github.com/JavaScript-Garden/zh/#core.eval)
1 var a = 0 ; 2 var f1 = function() { 3 var a = 1 ; 4 5 eval("a = 2") ; // 由于是直接调用eval函数,因此此时eval函数的作用域是当前作用域 6 console.log(a) ; // 2,在eval函数中修改了局部变量a 7 }; 8 f1() ; 9 console.log(a) ; // 0,全局变量a没有被修改 10 11 var b = 0 ; 12 var f2 = function() { 13 var b = 1 ; 14 var f3 = eval ; 15 16 f3("b = 2") ; // 由于这里将eval函数的引用赋值给了变量f3,并通过f3调用了eval函数,因此此时该函数的作用域是全局作用域 17 console.log(b) ; // 1,局部变量a没有被修改 18 } 19 f2() ; 20 console.log(b) ; // 2,在eval函数中修改了全局变量a
2、querySelector和getElementById的区别
querySelector和getElementById的区别在于,querySelector的参数须是符合 css selector 的字符串,例如对于下面的这个div,用getElementById可以获取,但是querySelector就获取不到了:
(参考出处:http://www.jb51.net/article/30132.htm)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5 </head> 6 <body> 7 <div id="1"></div> 8 </body> 9 </html>
输出结果如下:
1 var div = document.getElementById("1"); 2 console.log(div); 3 // => <div id="1"></div> 4 5 var target = document.querySelector("#1"); 6 console.log(target); 7 // => Uncaught Error: SYNTAX_ERR: DOM Exception 12
3、querySelectorAll和getElementsByClassName的区别
querySelectorAll和getElementsByClassName的区别在于querySelectorAll返回的是一个叫做StaticNodeList的新类型的实例。
顾名思义,StaticNodeList有NodeList所有的属性和方法,但是它底层的实现是元素集合的一个快照,而非总是要重新的针对文档的动态查询。使用StaticNodeList消除了大部分使用NodeList对象带来的性能问题。
只要调用querySelectorAll()都会返回一个StaticNodeList对象不管匹配的元素有几个;如果没有匹配,那么StaticNodeList为空。querySelectorAll()和querySelector()一样存在与Document和Element类型上。
话不多说,上代码:(原文来自:http://www.poluoluo.com/jzxy/201204/163845.html)
1 <!DOCTYPE> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5 <title>querySelectorAll和getElementsByClassName的区别</title> 6 </head> 7 <body> 8 <div class="a"></div> 9 <div class="a"></div> 10 <div class="a"></div> 11 <div class="a"></div> 12 <div class="a"></div> 13 <div class="a"></div> 14 </body> 15 </html>
对于上面的7个div,分别通过querySelectorAll和getElementsByClassName获取它们,然后改变其中一个div的class值,观察两个集合的结果有没有发生变化:
1 var div = document.getElementsByClassName("a"); 2 console.log(div); 3 // => [<div>, <div>, <div>, <div>, <div>, <div>] 4 5 var target = document.querySelectorAll(".a"); 6 console.log(target); 7 // => [<div>, <div>, <div>, <div>, <div>, <div>] 8 9 div[3].className = "b"; //改变第四个div的class值 10 11 console.log(div); 12 // => [<div>, <div>, <div>, <div>, <div>] 13 // 动态NodeList显示出变更后的div集合 14 15 console.log(target); 16 // => [<div>, <div>, <div>, <div>, <div>, <div>] 17 // StaticNodeList显示的还是变更前的div集合
4、使用undefined不为人知的秘密
在jQuery的源码中,其外层沙箱和命名空间大致是这样写的:
(原文来自:http://blog.raphealguo.com/2013/01/17/jquery-src-util/)
1 (function(window, undefined) { 2 ... 3 4 window.jQuery = window.$ = jQuery; 5 6 }(window, undefined));
这里最外层的匿名函数使用undefined作为第二个参数的原因,是因为在jQuery中有一个针对压缩的小小策略,先看以下代码:
1 (function( window, undefined ) { 2 var a = undefined; 3 if (a == undefined){blabla...} 4 5 .... 6 if (c == undefined) return; 7 })( window );
经过压缩后,可以变成:
1 (function(w, u) { 2 var a = u; 3 if (a == u){blabla...} 4 5 .... 6 if (c == u) return; 7 })(w);
因为这个外层函数只传了一个参数,因此沙箱执行时,u自然会undefined,把9个字母缩成1个字母,可以看出压缩后的代码减少一些字节数。
这样做还有另外一个原因:由于在ECMAScript5之前undefined并不是ECMAScript中的关键字,即undefined是可以用作变量名的,也就是undefined是可以赋值的。例如下面所示的这段代码:
1 var undefined = "zhaojian"; 2 3 (function() { 4 console.log(undefined); 5 }());
这段代码在ES5之前是合法的,控制台输出的结果可能会是zhaojian而非undefined。因此将undefined作为参数传入的用处就是防止某些2B程序员对undefined进行赋值,并且即使某个2B程序员真的这么做了,那么这样做的影响也只局限于这一块代码的范围,不会影响到全局范围内其他人的代码。
5、深入理解形参、实参与arguments
arguments对象的值只与实参有关,而与实参无关,arguments与形参的关系是通过实参联系起来的。
先来看看下面的几道面试题:(原文出自:http://hi.baidu.com/flondon/item/544b53b5d4872870254b0999)
1 function tmp(x) { 2 x = 10; 3 console.log(arguments[0]); 4 } 5 tmp(1); 6 7 function tmp(x) { 8 arguments[0] = 10; 9 console.log(arguments[0]); 10 } 11 tmp(1) 12 13 function tmp(x) { 14 arguments[0] = 10; 15 console.log(arguments[0]); 16 } 17 tmp(); 18 19 function tmp(x) { 20 x = 10; 21 console.log(arguments[0]); 22 } 23 tmp(); 24 25 function tmp(x, y) { 26 y = 10; 27 console.log(arguments[1]); 28 } 29 tmp(1);
在上面的五道题中,前两道的答案都为10,后三道的答案都为undefined。关于该答案的要点分析如下:
1、如果函数调用时没有参数传入,那么arguments对象为空(已经声明,但没有元素,并且不指向任何内存单元)。
2、如果函数调用时有参数传入,那么arguments对象和相应的形参同时指向这些传入值的内存单元,此时,对arguments对象中的元素或者函数形参的任何赋值都会直接体现到彼此(因为它们的值存储于同一块内存)。
3、当输入的参数少于形参数目时,其他形参(未传入实参)值的改变不影响arguments的值(如代码段5)。
6、for in 遍历稀疏数组的问题
在使用for in方式遍历数组的时候,它会在遍历过程中跳过没有定义的数组位置。例如下面的代码:
1 var arr = []; 2 arr[1] = 1; // arr = [undefined, 1] 3 console.log(0 in arr); // false 4 console.log(arr.indexOf(undefined)); // -1 5 console.log(arr[0] === undefined); // true
对于上面的数组,js认为它的第一项是不存在的,虽然它有一个undefined的默认值,但是如果用indexOf去寻找值为undefined的数组元素,返回的结果是找不到(-1)。
上面的是正常的情况,但是有正常就有不正常,例如下面的代码:
1 var arr = [undefined, 1]; // arr = [undefined, 1] 2 console.log(0 in arr); // true 3 console.log(arr.indexOf(undefined)); // 0 4 console.log(arr[0] === undefined); // true
对于这里的数组,js认为它的第一项是存在的,虽然它的值也为undefined,而且如果用indexOf去寻找值为undefined的数组元素是能找到的(返回结果为0)。
因此可以得出结论,虽然上面的两个数组从外表上看起来一模一样,但是第二个数组中的undefined值是显式声明的,而第一个数组中的undefined值是隐式声明的,所以for in会忽略隐式声明的undefined数组元素,而把显示声明的数组元素作为正常的数组元素来看待