javascript理解
来源:互联网 发布:男生不主动追女生知乎 编辑:程序博客网 时间:2024/05/20 18:54
转自:http://www.cnblogs.com/yangjunhua/archive/2012/05/22/2513340.html
一、函数声明与函数表达式
1、函数声明之后,可以在声明之前调用,也可以在声明之后调用,因为所有的函数声明(包括var声明的变量)都会在代码执行之前就加载到作用域中,函数名其实是一个Function类型的对象的引用,声明函数时函数名其实也就被赋值了;2、而函数表达式则不同,函数表达式是将函数赋值给一个变量,只有当代码执行到那一行的时候,函数才真正的有定义,因此这个变量只有在表达式之后才能使用,否则这个变量为undefined,如果这个变量不是通过var关键字声明,那么它就没有任何值。例1:
fn1();
//fn1
fn2();
//fn2 is not a function
console.log(
typeof
fn2);
//undefined
function
fn1(){
console.log(
"fn1"
);
}
var
fn2 =
function
(){
console.log(
"fn2"
);
}
fn2();
//fn2
例2:
var
fn = 10;
function
fn(){
console.log(
"test"
);
}
console.log(fn);
//10
fn();
//fn is not a function
在例2的代码中,变量声明会在代码执行之前就加载到作用域中,函数声明时,函数名就是一个Function类型的对象引用,此时它已经被赋值了,同一个变量被多次声明时,只会忽略后面的声明,但会执行后面的变量初始化;此时变量fn的值已经被覆盖了,值为10。
例2中的代码可以理解为下面的代码:
var
fn;
function
fn(){
console.log(
"test"
);
}
//上一句函数声明其实等价于执行赋值表达式fn = new Function("console.log('test')");
fn = 10;
console.log(fn);
//10
fn();
//fn is not a function
二、函数执行环境、变量对象与作用域链
1、后台中每个函数的执行环境都有一个变量对象,全局环境中的变量对象(浏览器中的window对象)始终存在,当函数被调用时,其活动对象等同于变量对象,函数中的局部执行环境中的活动对象只存在函数执行过程中,函数执行完后,其活动对象就会被撤销;2、当一个函数第一次被执行时,会创建一个执行环境(execution context)和相应的作用域链(包含了所有外部函数的活动对象及全局变量对象),并把作用域链赋值给函数的一个特殊的内部[[Scope]]属性中,然后用this,arguments和其他参数初始化函数的活动对象(activation object),函数在执行时,会通过复制函数的[[Scope]]属性中的对象构建起执行环境的作用域链,注意函数本身也有一个作用域,作为执行环境作用域链的前端。3、理解函数执行环境、变量对象与作用域链,是理解闭包的关键所在。4、关于this,arguments对象需要注意的地方: 当一个函数第一次被执行时,就会用this,arguments对象等其他参数初始化函数的活动对象,但是如果是闭包(即内部函数)在搜索这两个对象时,只会搜索到其活动对象为止,而不会搜索其他函数作用域中的活动对象,也就是说内部函数无法访问其包含函数的this,arguments对象,但是可以通过变量的形式来访问外部函数中的this,arguments对象,由于闭包的执行环境具有全局性,因此其this指向window对象。
三、闭包及其所存在的问题
闭包:是指有权访问外部函数作用域中的变量的函数,创建一个闭包的方式就是在一个函数中创建另一个函数例3:
function
fn(name){
return
function
(){
return
name
}
}
var
nm = fn(
"yjh"
);
console.log(nm());
//yjh
在例3中,fn中有一个匿名函数,即闭包,在上面代码的执行中,结果为yjh,因为闭包可以访问外部函数作用域中的变量,内部函数中的变量是从作用域链的前端开始搜索的,如果没有找到,直至搜索到作用域链的末端(全局执行环境)。
闭包所存在的问题: 就拿例3中的例子来说,在函数fn执行完毕后,其变量name(包括其活动对象)并没有被销毁,因为内部函数(闭包)的作用域链包含了fn中的作用域,内部函数仍然在引用fn中的活动对象,当fn执行完后,其作用域链会被销毁,但是它的活动对象会继续保存在内存中,直到匿名函数被销毁,它的活动对象才会被销毁;由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存,过多的使用闭包会导致内存占用过多。
四、块级作用域
JavaScript中没有块级作用域,不像其他编程语言中拥有块级作用域的概念,但是我们可以在js中模拟块级作用域的概念,我们都知道,一般而言,函数执行完毕后,执行环境中的活动对象就会被撤毁,因此我们可以像下面这样:
(
function
(){
})()
声明一个匿名函数,然后立即调用它,这样在这个匿名函数中声明的变量在函数执行完毕就会被撤毁,外部函数也不能访问其中定义的变量 函数声明和调用的几种写法:
function
(){}
//缺少(或意外)标识符(ie9以下的ie浏览器不会报错)
function
fn(){}
//常规函数声明
var
fn =
function
(){}
//赋值表达式
(fn =
function
(){})()
//函数被立即调用
var
fn =
function
(){}()
//函数被立即调用
(
function
(){})()
//函数被立即调用
五、解析js中常见的一个问题
例4:
<
ul
>
<
li
>1</
li
>
<
li
>2</
li
>
<
li
>3</
li
>
<
li
>4</
li
>
</
ul
>
var
li = document.getElementsByTagName(
"li"
);
for
(
var
i=0;i<li.length;i++){
li[i].onclick =
function
(){
console.log(i);
}
}
例4中的代码相信很多人都遇到过,执行结果为什么不是预期的所想要的解读一下: 首先变量li获取了所有的li元素节点,然后通过for循环为每个li元素的click事件绑定一个事件处理函数,注意click事件绑定函数是通过赋值表达式来绑定的,而当代码执行到那一行的时候,并不会执行(这并不是函数赋值表达式引起的),因为click事件是晚绑定的,即只有当某个li元素的click事件被触发时,才会绑定函数,而在click事件被触发之前,事件处理函数并没有定义(因为赋值表达式语句并没有执行),此时全局环境中的i变量通过for循环已经变为5了,事件处理函数作为一个闭包,访问的全局环境中的i变量都是4,因此每次输出的结果也都是4了。解决办法:可以通过匿名函数传参的形式来解决,因为函数传参,是传值,而不是传址,即复制另一个变量值的副本
方法1
var
li = document.getElementsByTagName(
"li"
);
for
(
var
i=0;i<li.length;i++){
(
function
(j){
li[i].onclick =
function
(){
console.log(j);
}
})(i)
}
方法2
var
li = document.getElementsByTagName(
"li"
);
for
(
var
i=0;i<li.length;i++){
(
function
(){
var
j = i;
li[i].onclick =
function
(){
console.log(j);
}
})(i)
}
两种方法大同小异,都是通过函数传参的形式来保存全局变量i值的一个副本,然后使事件处理函数中的变量来访问这个副本来达到预期的结果。不好意思,之前这里的i变量初始化为1,那么第一个li就没有绑定事件处理函数,忽略小细节了,之前只注重输出结果了,感谢园友笨瓜一号,xujif的提醒。
六、总结:
1、变量,函数声明都会提前加载到作用域中,函数赋值表达式只有当代码执行到那一行时才会有定义2、每一个函数都有一个执行环境(作用域),变量对象(活动对象),以及相应的作用域链(各个活动对象的指针列表),当函数执行时,作用域链会赋值给函数的内部特殊属性[[Scope]]以构建起函数执行环境的作用域链3、闭包就是指有权访问外部作用域的变量的函数,创建闭包的方式就是在一个函数中创建另一个函数,闭包会包含外部函数的作用域,因此占用的内存会比其他函数多,过多的使用闭包会使内存占用过多,还会导致外部函数的活动对象不会被释放,只有闭包的引用被撤销,外部函数作用域中的活动对象才会被释放4、闭包(内部函数)在搜索this,arguments对象时,只会在自身的活动对象中搜索,永远也不能访问外部作用域中的this,arguments对象,但可以通过变量赋值的形式来访问外部函数作用域中的this,arguments对象,由于闭包的执行环境具有全局性,因此其this对象指向window对象,全局环境中没有arguments对象的5、在js中还可以模拟其他语言中的块级作用域,即声明一个匿名函数,然后立即调用它
- 理解JavaScript
- javascript理解
- 理解JavaScript中的事件javascript
- 点点滴滴,对JavaScript理解
- 理解JavaScript中的事件
- 理解JavaScript中的事件
- 理解JavaScript中的事件
- 理解JavaScript中的事件
- 理解JavaScript中的事件
- 理解 JavaScript 闭包
- 深入理解JavaScript函数
- 理解JavaScript中的事件
- 理解JavaScript中的事件!
- 理解 JavaScript 闭包
- 理解JavaScript中的对象
- javascript top理解
- 深刻理解javascript
- 理解 JavaScript 闭包
- easyui为tab添加遮罩层
- VS2010运行速度加快方法
- Android系统的开机画面显示过程分析
- stl基本的框架
- ubuntu12.04 编译android源码4.0.4
- javascript理解
- 随便说说字符集和编码
- php中函数imagestringup显示汉字乱码问题!
- qemu 保存/恢复
- hi3716c android4.0 在ubuntu下编译出现问题
- android camera照片旋转90度
- 秒杀多线程第四篇 一个经典的多线程同步问题
- 编程之美2.3 寻找发帖水王
- Python文件夹与文件的操作