zoukankan      html  css  js  c++  java
  • JavaScript进阶之理解篇

    一、函数声明与函数表达式


    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;
    fn = 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(){})() //函数被立即调用

    总结:

    1、变量,函数声明都会提前加载到作用域中,函数赋值表达式只有当代码执行到那一行时才会有定义

    2、每一个函数都有一个执行环境(作用域),变量对象(活动对象),以及相应的作用域链(各个活动对象的指针列表),当函数执行时,作用域链会赋值给函数
    的内部特殊属性[[Scope]]以构建起函数执行环境的作用域链

    3、闭包就是指有权访问外部作用域的变量的函数,创建闭包的方式就是在一个函数中创建另一个函数,闭包会包含外部函数的作用域,因此占用的内存会比其他函数多,
    过多的使用闭包会使内存占用过多,还会导致外部函数的活动对象不会被释放,只有闭包的引用被撤销,外部函数作用域中的活动对象才会被释放

    4、闭包(内部函数)在搜索this,arguments对象时,只会在自身的活动对象中搜索,永远也不能访问外部作用域中的this,arguments对象,但可以通过变量赋值的
    形式来访问外部函数作用域中的this,arguments对象,由于闭包的执行环境具有全局性,因此其this对象指向window对象,全局环境中没有arguments对象的

    5、在js中还可以模拟其他语言中的块级作用域,即声明一个匿名函数,然后立即调用它

    
    
  • 相关阅读:
    抗战纪念日之前
    学习VS生活
    来到这里,我放弃了多少- UI基础-疯狂猜图,我们都疯狂了-
    开班典礼-老师玩命的教,大家玩命的学,沉静,18K
    见老同学记
    6月第一周学习总结
    临界区和不变量和谓词
    C库得到系统当前的时间
    查看一个进程有多少个线程
    bubble chart|Matrix Scatter|Overlay Scatter|Scatterplots|drop-line|box plot|Stem-and-leaf plot|Histogram|Bar chart|Pareto chart|Pie chart|doughnut chart|
  • 原文地址:https://www.cnblogs.com/yangjunhua/p/2513340.html
Copyright © 2011-2022 走看看