js预处理和闭包
2017-12-10 17:49
113 查看
1.全局变量和函数执行分为两个阶段
(1)预编译阶段①全局词法环境(lexicalenvironme)也就是执行上下文环境。用于存储预处理数据。预处理数据包括:var方式定义的变量和声明方式定义的函数;var方式定义的变量直接赋值undefine,声明方式定义函数赋值整个函数体,this赋值。
②预处理覆盖问题(函数以声明方式进行定义)
如果后声明的函数名和之前声明的函数名冲突,冲突函数后面的会覆盖前面的;
如果后声明的函数名和定义的属性名冲突,冲突函数后面的会覆盖前面的;
如果后定义的属性名和之前声明的函数名冲突,则会忽略属性定义。
③过程:•先初始化函数的参数到词法环境中;
•内部声明方式定义的函数初始化到词法环境中;
•初始化arguments属性;
•内部var方式定义的变量初始化到词法环境中,赋值为undefine;
•函数和变量定义冲突处理方式和全局是一致的。
(2)执行阶段
从js代码块的第一行开始执行,并给词法环境中的变量赋值,如果变量在词法环境中不存在,则往词法环境中加入变量并赋值。
覆盖问题:执行阶段不会再处理声明方式定义的函数,声明方式定义函数不会覆盖任何定义的函数和属性,后赋值的属性会覆盖先赋值属性内容。
2.执行上下文栈
(1)定义:执行上下文的压栈和出栈过程(2)原理:当执行全局函数时候,会产生一个执行上下文;
每调用一次函数,同样会产生一个上下文;
当函数调用完毕后,执行上下文会销毁。
如:
var a = 10, //全局上下文环境
fn,
bar = function(x){
var b = 5;
fn(x+b); //fn上下文环境
};
fn = function(y){
var c = 5;
console.log(y+c);
}
bar(10); //bar上下文环境
3.作用域
在JavaScript里,域指的是代码当前的上下文语境;js中作用域分为:全局作用域和本地作用域。JS中不存在块级作用域,即{}里的一个块。(1)全局作用域:整个页面,全局域只能有一个,全局域有一个内置对象window
所有全局域的变量和方法都绑定到window对象上。
(2)本地作用域(函数作用域):在全局域中定义的域,通常指的是函数作用域,在函数内部定义的变量和函数。如果在一个新的域中定义了一些函数和变量,它们是无法从当前那个域的外部被访问的。
var a = 10, //全局作用域
fn,
bar = function(x){ //bar作用域
var b = 5;
fn(x+b);
};
fn = function(y){ //fn作用域
var c = 5;
console.log(y+c);
}
bar(10);
作用域只是一个“地盘”,一个抽象的概念,其中没有变量。要通过作用域对应的执行上下文环境来获取变量的值。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。所以,作用域中变量的值是在执行过程中产生的确定的,而作用域却是在函数创建时就确定了。
所以,如果要查找一个作用域下某个变量的值,就需要找到这个作用域对应的执行上下文环境,再在其中寻找变量的值。
4.作用域链
js中所有元素都是对象,包括function函数也是一个对象。任何一个函数对象都有一个内部属性。该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问取自由变量时的这个“作用域链”过程:(假设a是自由量)
(1)现在当前作用域查找a,如果有则获取并结束。如果没有则继续;
(2)如果当前作用域是全局作用域,则证明a未定义,结束;否则继续;
(3)(不是全局作用域,那就是函数作用域)将创建该函数的作用域作为当前作用域;
(4)跳转到(1)。
5.作用域和上下文环境的区别
(1)作用域在函数定义时就已经确定了,上下文环境在执行过程中产生;(2)作用域是用于定义范围的,作用域内部的属性必须要通过上下文获取;
(3)作用域中变量的值是在执行过程中产生的确定的,而作用域却是在函数创建时就确定了;
(4)如果要查找一个作用域下某个变量的值,就需要找到这个作用域对应的执行上下文环境,再在其中寻找变量的值。
6.闭包(Closure)
(1)定义:闭包就是指外部能够随意读取其他函数内部变量的函数,也可以理解成“定义在一个函数内部的函数”,闭包是将函数内部和函数外部连接起来的桥梁。(2)作用:可以读取函数内部的变量,减少全局变量定义;减少函数传递参数的数量;让函数内部变量的值始终保持在内存中,不会在函数调用完毕后被自动清除。
(3)全局作用域变量问题: 一个html文件中会包含大量的js外部文件引用;如果所有js文件全部使用全局作用域变量定义,则容易引起冲突。全局作用域变量定义访问效率低下,因为每次访问全局变量需要从作用域链中顶层找到底层。
注意事项:闭包使用父函数变量时候,只是个引用,而不是复制
父函数每调用一次,会产生不同的闭包。
函数声明和表达式的区别
表达式是由运算元和运算符(可选)构成,并产生运算结果的语法结构。
(function {})是表达式
function {}是函数声明.
闭包的两个条件:函数作为返回值;函数作为参数传递
(1)函数作为返回值
function f(){
var a = 10;
return function f1(x){
if(x==a){
console.log(x);
}
};
}
var fn = f();
fn(12);
f1函数作为返回值,赋值给fn变量。执行fn(12)时,用到了f1作用域下的a变量的值
(2)函数作为参数传递
var a = 10;
fn = function(x){
if(x==a){
console.log(x);
}
};
(function(f){
var a = 100;
f(15);
})(fn);
fn函数作为一个参数被传递进入另一个函数,赋值给f参数。执行f(15)时,max变量的取值是10,而不是100。