函数及作用域链
作用域
变量
var声明的变量,其作用域是函数体的全部,没有块作用域
let声明的变量拥有块级作用域。
1) 局部变量:
函数内声明的变量为局部变量,为局部作用域,只能在函数内访问;
function studentnum()
{
var nums = 10;
console.log(nums);
}
studentnum();
console.log(nums);
2)全局变量:
函数外声明的变量为全局变量,具有全局作用域,在声明处后面的网页脚本中均可使用;
var nums = 10;
function studentnum()
{
console.log(nums);
}
studentnum();
console.log(nums);
//balabala
function studentnum() {
num = 50;
console.log(num);
}
studentnum();
console.log(num);
//balabala
function studentnum() {
num = 50;
console.log(num);
}
num=100;
studentnum();
console.log(num);
如果变量在函数内没有使用var进行声明,则默认为全局变量(前提是函数必须执行了):
3) 变量声明周期:
变量在声明时初始化;
局部变量在函数执行完后销毁;
全局变量在页面关闭后销毁;
二、函数
1.函数基本定义:
function functionname()
{
执行代码
}
2.匿名函数
function(a){return a;}
可用来定义匿名函数来执行某些一次性任务。
3.表达式函数:
函数可以定义为一个表达式储存在变量中;
var y = function (h, q){return h%q;}
document.write(y(3, 4));
这样就定义了一个匿名函数并保存在变量中了;
4.自调用函数:
自调用的方法是
1.匿名函数自己被小括号抱起来();
2.在函数后面紧跟小括号()调用;
表达式函数可以自调用,声明的函数不能自调用;
(function (){
var h = '专业始于专注';
document.write(h);})(); //正常执行
function fnName(){
console.log(123);
}()
var fnName = function () {
console.log(123);
}() // 正常执行
5.使用关键词Function来new一个函数:
var fun1 = new Function('var h = 3, q = 5; return h + q;');
alert(fun1());
6.内部函数(私有函数)
function a(param){
function b(i){
return i*2;
};
return b(param)+1;
};
当我们调用a()时,本地函数b()也会在内部被调用。由于b()是本地函数,它在a()以外的地方是看不见的,所以b为私有函数
使用私有函数的好处:
有助于确保全局名字空间的纯净性(命名冲突的机会减小)
私有性---可以选择只将一些必要的函数暴露给外部,并且保留属于自己的函数,使它们不被应用程序的其他部分所用
7.返回函数的函数
function func(){
var b=3;
return function(){
console.log(b);
}
}
var newFunc=func();
newFunc();
函数的生命周期
函数的的生命周期分为创建和执行两个阶段。
在函数创建阶段,JS解析引擎进行预解析,会将函数声明提前,同时将该函数放到全局作用域中或当前函数的上一级函数的局部作用域中。
在函数执行阶段,会创建该函数的执行上下文并且JS引擎会将当前函数的局部变量和内部函数进行声明提前,然后再执行业务代码,当函数执行完退出时,释放该函数的执行上下文,并注销该函数的局部变量。
//balabala
var a=123;
function f(){
console.log(a);
var a=1;
console.log(a);
}
f();
JavaScript变量提升和函数提升
1.变量提升:
把变量提升到函数的最开始位置;只是提升变量的声明,不会提升赋值;
function hoistvar()
{
console.log(h);
var h = '专业始于专注';
}
hoistvar();
2.函数提升:
把声明的函数提到前面去;只有声明形式的函数才会提到前面去(字面量形式的不会,字面量就是如何表达这个值,一般等号右边的都认为是字面量,也就是表达式函数);
hoistfun();
function hoistfun(){
var a=2;
var b=3;
return a+b;
}
//balala
console.log(a);
var a=2;
function a() {
console.log(1);
}
a = function() {
console.log(2);
}
a();
//balala
function studentnum() {
var studentsnum = 50;
console.log(studentnum);
}
studentnum();
函数的声明优先于变量声明
函数提升和变量提升给我们的启示:
在编写程序时,尽量按照提升后的执行顺序写代码,避免不必要的错误和理解误差
作用域链
作用域链
当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途是保证对执行环境(执行上下文)有权访问的所有变量和函数的有序访问。
变量对象(VO):变量对象即包含变量的对象,变量对象我们无法访问,除此之外和普通对象没什么区别。变量对象存储了在上下文中定义的变量和函数声明
活动对象(AO):是在进入函数执行环境时刻被创建的,它通过函数的 arguments 属性初始化。
变量对象和活动对象的关系
未进入执行阶段之前,变量对象(VO)中的属性都不能访问,只是声明但是进入执行阶段之后,变量对象(VO)转变为了活动对象(AO),里面的属性都能被访问了,然后开始进行执行阶段的操作。它们其实都是同一个对象,只是处于执行环境的不同生命周期。AO 实际上是包含了 VO 的。因为除了 VO 之外,AO 还包含函数的 parameters,以及 arguments 这个特殊对象。也就是说 AO 的确是在进入到执行阶段的时候被激活,但是激活的除了 VO 之外,还包括函数执行时传入的参数
和 arguments 这个特殊对象。
执行环境的组成
当JavaScript代码执行的时候,会进入不同的执行环境,这些执行环境会构成一个执行环境栈
执行环境分析
js的执行顺序是根据函数的调用来决定的,当一个函数被调用时,该函数环境的变量对象就被压入一个环境栈中。而在函数执行之后,栈将该函数的变量对象弹出,把控制权交给之前的执行环境变量对象。
eg:
var scope = "global";
function fn1(){
return scope;
}
function fn2(){
return scope;
}
fn1();
fn2();
当某个函数第一次被调用时,就会创建一个执行环境(execution context)以及相应的作用域链,并把作用域链赋值给一个特殊的内部属性([scope])。然后使用this.arguments(arguments在全局环境中不存在)和其他命名参数的值来初始化函数的活动对象(activation object)。当前执行环境的变量对象始终在作用域链的第0位。当第一次调用fn1时
function outer(){
var scope = "outer";
function inner(){
return scope;
}
return inner;
}
var fn = outer();
fn();
JavaScript闭包
作用域 私有函数
垃圾回收机制 引用
函数是可以嵌套函数的,内部function可以访问外部function的变量;
闭包:通过引用访问函数内的函数,实现信息的驻留;
访问函数内的函数,突破变量作用域限制
var person = function() {
var personnum = 0; //私有privite,外部不能直接访问
function getCount() {
return personnum++;
}
return getCount; //返回函数名(public,暴露内部信息 )
}
var p = person();//通过子函数引用,能访问局部变量console.log('count...' + p());
引用在,空间不灭
function fun() {
var h = 5;
return function() {
console.log(h);
return h++;
};
}
var historys = fun();
historys();
historys();
var historys2 = fun();
historys2();
historys2();
historys2();
fun()();
fun()();
共享封闭空间
function fn() {
var _num = 0;
return {
add1: function() {
_num++;
console.log(_num);
},
add2: function() {
_num += 2;
console.log(_num);
}
}
}
var c = fn();
c.add1();
c.add2();
c.add1();
function fun(a) {
return function(b){
return function(c){ console.log(a+b+c); };
};
}
var x = fun(1 );
var y = fun(5)(8);
var z = fun(2)(4);
x(2)(1);
y(2); //在y快照基础上再运算 5+8+2
z(2);
//
function f1() {
var n = 999;
nAdd = function () {
n += 1;
console.log(n);
}
function f2() {
n++;
console.log(n);
}
return f2;
}
var n = 668;
console.log(n);
var result = f1();
result();
nAdd();
result();