JavaScript

Published: 18 Nov 2015 Category: javascript

JavaScript是一种基于对象的脚本编程语言,是浏览器上的程序语言。当web容器输出内容到浏览器时,这个内容是包含js源代码的,此时,JavaScript可以操作浏览器上的一切内容,在浏览器上提供用户交互,页面美化,增加页面的智能性。

1、JS基础

1.1 闭包

闭包 是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量

闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。

闭包是javascript语言的一大特点,主要应用闭包场合主要是为了:设计私有的方法和变量。

一般函数执行完毕后,局部活动对象就被销毁,内存中仅仅保存全局作用域。但闭包的情况不同!

闭包有三个特性:

  1. 函数嵌套函数
  2. 函数内部可以引用外部的参数和变量
  3. 参数和变量不会被垃圾回收机制回收

嵌套函数的闭包

function aaa() {  
  var a = 1;  
  return function(){
   alert(a++);
  };  
}         
var fun = aaa();  
fun();// 1 执行后 a++,,然后a还在~  
fun();// 2   
var fun2 = aaa(); 
fun2(); //1
fun2(); //2
fun2 = null;
fun = null;//a被回收!!

下面是不用闭包的情况,局部变量没有累加!

<script>    
function abc(){
        var a = 1;
        a++;
        alert(a);
}
abc();                       //2
abc();                    //2
</script>

那么使用闭包有什么好处呢?使用闭包的好处是:

  1. 希望一个变量长期驻扎在内存中。
  2. 类似独立的命名空间,避免变量污染到全局。
  3. 私有成员的存在(对象有私有属性或方法不希望外部直接访问的时候。)

闭包主要涉及到js的几个其他的特性:作用域链,垃圾(内存)回收机制,函数嵌套,等等.

  • 作用域链就是函数在定义的时候创建的,用于寻找使用到的变量的值的一个索引,而他内部的规则是,把函数自身的本地变量放在最前面,把自身的父级函数中的变量放在其次,把再高一级函数中的变量放在更后面,以此类推直至全局对象为止.当函数中需要查询一个变量的值的时候,js解释器会去作用域链去查找,从最前面的本地变量中先找,如果没有找到对应的变量,则到下一级的链上找,一旦找到了变量,则不再继续.如果找到最后也没找到需要的变量,则解释器返回undefined.

  • 一个函数在执行开始的时候,会给其中定义的变量划分内存空间保存,以备后面的语句所用,等到函数执行完毕返回了,这些变量就被认为是无用的了.对应的内存空间也就被回收了.下次再执行此函数的时候,所有的变量又回到最初的状态,重新赋值使用.

  • 但是如果这个函数内部又嵌套了另一个函数,而这个函数是有可能在外部被调用到的.并且这个内部函数又使用了外部函数的某些变量的话.这种内存回收机制就会出现问题.如果在外部函数返回后,又直接调用了内部函数,那么内部函数就无法读取到他所需要的外部函数中变量的值了.所以js解释器在遇到函数定义的时候,会自动把函数和他可能使用的变量(包括本地变量和父级和祖先级函数的变量(自由变量))一起保存起来.也就是构建一个闭包,这些变量将不会被内存回收器所回收,只有当内部的函数不可能被调用以后(例如被删除了,或者没有了指针),才会销毁这个闭包,而没有任何一个闭包引用的变量才会被下一次内存回收启动时所回收.

闭包还有一些特性,却往往让程序员觉得很难理解.如:

var result=[];
function foo(){
    var i= 0;
    for (;i<3;i=i+1){
        result[i]=function(){
            alert(i)
        }
    }
};
foo();
result[0](); // 3
result[1](); // 3
result[2](); // 3

闭包中所记录的自由变量,只是对这个变量的一个引用,而非变量的值,当这个变量被改变了,闭包里获取到的变量值,也会被改变.

解决的方法之一,是让内部函数在循环创建的时候立即执行,并且捕捉当前的索引值,然后记录在自己的一个本地变量里.然后利用返回函数的方法,重写内部函数,让下一次调用的时候,返回本地变量的值,改进后的代码:

var result=[];
function foo(){
    var i= 0;
    for (;i<3;i=i+1){
        result[i]=(function(j){
            return function(){
                alert(j);
            };
        })(i);
    }
};
foo();
result[0](); // 0
result[1](); // 1
result[2](); // 2

1.2 JavaScript是弱类型,除了在switch语句中

当JavaScript在case比较时,它是非弱类型。

var myVar = 5;
if(myVar == '5'){ //返回true,因为JavaScript是弱类型
    alert("hi");  //这个alert将执行,因为JavaScript通常不在意数据类型
}
switch(myVar){
case '5':
     alert("hi"); //这个alert将不会执行,因为数据类型不匹配
}

1.3 javascript的垃圾回收原理

(1)在javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收;
(2)如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。

2、面试题

2.1 回答以下代码,alert的值分别是多少?

<span style="font-family:Microsoft YaHei;font-size:18px;">
<script>  
var a = 10;  
function test() {  
    a = 100;  
    alert(a);  
    alert(this.a);  
    var a;  
    alert(a);  
}  
test();  
</script>  
</span>   正确答案是:100, 10, 100

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象

彻底理解js中this的指向

<span style="font-family:Microsoft YaHei;font-size:18px;"><script>  
var a = 100;  
function test(){  
    alert(a);  
    var a = 10;  
    alert(a);  
}  
test();    
</script>  
</span>  

正确答案是: undefined, 10

2.2 运算结果

alert(typeof(null))  // object 
alert(typeof(undefined))  // undefined 
alert(typeof(NaN))  // number 
alert(NaN==undefined)  // false 
alert(NaN==NaN)  // false 
var str="123abc"; 
alert(typeof(str++)) // number 
alert(str) // string 

2.3 js编译

问执行完毕后 x, y, z 的值分别是多少?

<script> 
  var x = 1, y = z = 0; 
  function add(n) { 
    n = n+1; 
 } 

  y = add(x); 
    
  function add(n) { 
    n = n + 3; 
  } 

  z = add(x); 
</script> 

x, y 和 z 分别是 1, undefined 和 undefined。(注意没有return)

<script> 
  var x = 1, y = z = 0; 
  function add(n) { 
    return n = n+1; 
 } 

  y = add(x); 
    
  function add(n) { 
    return n = n + 3; 
  } 

  z = add(x); 
</script> 

那么这时 y 和 z 分别是什么呢?

马上想到是 2 和 4,不过结果却是 4 和 4。

这说明,在第一次调用 add 函数之前,第二个 add 函数已经覆盖了第一个 add 函数。原来,这是 JS 解释器的”预编译”,JS 解析器在执行语句前会将函数声明和变量定义进行”预编译”,而这个”预编译”,并非一个页面一个页面地”预编译”,而是一段一段地预编译,所谓的段就是一个 <script> 块。且看下面的代码

<script> 
  function add(n) { 
    return n = n+1; 
 } 
  alert(add(1)); 
</script> 

<script> 
  function add(n) { 
    return n = n+3; 
 } 
  alert(add(1)); 
</script> 

会分别弹出 2 和 4。

<script> 
  alert(typeof addA); 
  addA(); 
  function addA() { 
    alert("A executed!"); 
  }; 
</script> 
<script> 
  alert(typeof addB); 
  addB(); 
  var addB = function() { 
    alert("B executed!"); 
  }; 
</script> 

按照前面的知识,第一个 <script> 块执行正常,结果就是弹出 “function” 和 “A executed!” 的对话框。

那么第二个 <script> 块呢? 执行结果是弹出 “undefined” 的对话框后报 JS 错误,说 addB 不是一个 function。 有点出乎意料?呵呵,其实第一个 script 块中的 addA 一句是函数声明,当然进行了”预编译”,但是第二个 script 块中的 addB 一句并非函数声明。只不过在执行这段 script 之前对变量进行了”预声明”,因此一开始变量addB是存在的,只不过是 undefined 的.

<script> 
  alert(typeof addB); 
  var addB = "variable"; 
  function addB() { 
    alert("function addB"); 
  } 
  alert(addB); 
</script> 

执行结果是”function”和”variable”。

JS解析器先预定义了 addB 变量为 undefined, 但是 addB 函数覆盖了此变量,因此一开始执行结果是 function,然后 addB 被赋值为 “variable”,因此最后执行结果是 “variable”,上面的代码即使变为 JS代码

<script> 
  alert(typeof addB); 
  function addB() { 
    alert("function addB"); 
  } 
  var addB = "variable"; 
  alert(addB); 
</script> 

结果也一样,这说明JS解析器先预声明变量,再预定义函数

小结一下:JS 在执行前会进行类似”预编译”的操作,而且先预定义变量再预定义函数。

2.4 其他问题

1、form中的input有哪些类型?各是做什么处理使用的?

text   radio   checkbox   file   button   image   submit   reset   hidden 

2、table标签中border,cellpadding td标签中colspan,rowspan分别起什么作用?

border边界 
cellpadding边距 
colspan跨列数 
rowspan跨行数 

3、form中的input可以设置readonly和disable,请问这两项属性有什么区别?

readonly不可编辑,但可以选择和复制   
disable不能编辑复制选择 

4、JS中的三种弹出式消息提醒(警告窗口、确认窗口、信息输入窗口)的命令是什么?

alert   
confirm   
prompt  

3、开发遇到的问题

3.1 页面动态加入<script>便签并执行代码