函数 js预编译 作用域 作用域链 闭包

184 阅读9分钟

函数:可控的代码的集合

  1. 函数是一个对象,每个函数都是Function类型的实例,而且都有引用类型一样的属性和方法,函数名就是保存的就是函数对象的地址值(也叫指针)跟其他对象一样每一个都是唯一的,且相互独立.定义函数使用function固定语法创建的
  2. 函数没有重载,相同函数名会被覆盖掉
  3. 创建对象两种方式声明和表达式(解析器会率先读取函数声明,且在任何代码执行之前,表达式则会在解析器执行到他所在的代码行才会执行
  4. 函数可以作为值 作为返回值 作为参数的话是因为函数名不加括号就是一个变量,该变量的值是该函数的指针,用作返回的话,该函数就成了闭包函数了
  5. 函数内部会自动添加属性 this 和 arguments还有caller三个都是对象,arguments主要是保存函数参数的,arguments对象有一个属性是callee,是指向拥有arguments对象的函数作用是为了解决函数执行与函数名耦合的问题(在函数名有可能变动时),caller保存着调用当前函数函数的引用,如果在全局作用域中调用当前函数他的值为null,this可以用作创建构造函数,继承等 当函数为构造函数是返回值只要不是引用数据类型,都默认返回this,不返回默认也是this
  6. 函数作为对象也有属性和方法,length prototype 属性 call()/apply() 方法;length属性返回的是参数的个数,prototype是保存引用类型的所有实例方法的地方,比如toString,valueOf 等都在他名下,创建自定义引用类型及实现继承是都会用到,该函数不可枚举,所以for-in无法发现

函数的作用

  1. 递归
  2. 模仿块级作用域
  3. 私有化变量
  4. 闭包

定义

    //表达式
    var a = function (){}
    //声明
    function a(){}

函数执行

只要函数定义了就可以在任何地方执行

  1. 在自己的函数体里面执行可以做递归.
  2. 执行前要预编译,创建自己的执行上下文并添加到该函数的作用域链最上边面去

预编译

js特点 单线程 解释性语言执行之前 1.通篇扫描就叫语法分析过程 2.预编译3.解释一行执行一行

  1. 预编译就是函数执行前的对函数作用域中的类型进行整理的过程.
  2. 1.变量和参数提升并赋值undefined
  3. 形参和实参统一
  4. 函数什么提升并赋值函数
  5. 按照顺序一行一行执行
Text(); //执行 函数声明整体提升.不管在哪里调用本质上都在下面调用;意思是只要定义了函数哪里调都可以执行

//   function Text() {
//     document.write('11')
//   }
//     //变量 声明提升
//   console.log(a)
//   var a = 10;

例题一

//预编译
function fn(a) {
    console.log(a);
    var a = 123;
    console.log(a)
    function a () {

    }
    console.log(a)
var  b = function () {

}
console.log(b)
function d() {};
}
fn(1) // function a(){} 123 123 function(){}
// 1.创建Ao
AO = {
    // 2.找形参和变量声明,值为undefined;
    a:undefined,
    b:undefined,
    //3将形参和实参统一
    a:1,
    b:undefined,
    //4.在函数体内找到函数声明,赋值函数体
    a:function a() {},
    b:undefined,
    d:function d(){},
    //5.执行函数 把没有执行的执行了
    a : 123,
    b: function () {},
    d: function(){}
}

例题2

    function text(a,b) {
        console.log(a);
        c = 0;
        var c;
        a = 3;
        b = 2;
        console.log(b);
        function b() {};
        function d() {};
        console.log(b)
    }
    test(1) // 1 2 2
    //创建 AO
    AO = {
    //2.找到变量声明和形参,并赋值undefined
        a:undefined,
        b:undefined,
        c:undefined
    //第3步形参和实参统一
      a:1,
      b:undefined,
      c:undefined
   
    //第四步,函数声明提升,并赋值函数体
    a:1,
    b:function b(){}
    c:undefined,
    d:function d(){}
    //第五步 执行函数
    a:1,
    b:2,
    c:0,
    d:function d(){}
    }

例题3

    function text(a,b) {
        console.log(a);
        console.log(b);
        var b = 123;
        console.log(b);
        a = 1234;
        console.log(a);
        function a () { };
        var a;
        b = 234;
        var b = function(){};
        console.log(a);
        console.log(b);
    }
    text(1); //function a(){} undefined 123 1234 1234 function(){}
    //创建AO
    AO = {
        //找到变量声明和形参 赋值undefined
        a:undefined,
        b:undefined,
        //形参和实参统一
        a:1,
        b:undefined,
        //函数提升 并赋值 函数体,
        a:function a() {},
        b:undefined,
        //执行函数
        a:1234,
        b:function(){}
    }

GO对象

GO === window

    var a = 123;
    function a() {}
    console.log(a) //123

GO 和 AO

例题1

    console.log(text);
    function text(text) {
        console.log(text);
        var text = 234;
        console.log(text);
        function text(){}
    }
text(1) //function text(text){...} function text(){} 234
//创建GO
    GO = {
        //变量 和参数 提升 赋值undefined
        //实参形参 匹配
        //函数提升
        function text() {}
    }
    //创建AO
    AO ={
    //先用自己的 函数
        text:function text(){}
    }

例题2

    global = 100;
    function fn() {
        console.log(global);
        global = 200;
        console.log(global);
        var global = 300;
    }
    fn(); //undefined 200
    var global;
    Go = {
        global:100,
        fn:function fn() {}
    }
    AO = {
    
        glogbal:undefined,
         //赋值
        glogbal:200,
        glogbal:300
        
    }

作用域

执行上下文:是一个对象,包含了执行代码所需要的信息,执行一个函数时需要建立执行上下文,在开始执行

执行上下文的内容:

  1. VO variable object,变量对象,存的是函数和全局代码执行过程中所需要的局部变量,
  2. scope:作用域,
  3. this

  1. [[scrpe]]:是函数对象不能访问的属性中的其中一个,
  2. 执行上下文vo (AO(局部作用域)+ GO作用域(全局))
  3. [[scrpe]]:储存了运行期上下文的一个链式集合.
  4. 作用域链:就是[[scope]]集合呈链式链接,我们把这种链式链接.
  5. 函数每次执行都会产生一个独立的执行上下文AO,GO不变
function fn(){}
fn() //创建一个Ao,
fn() //创建一个新的AO覆盖前面的AO

例题1

    function a() {
        var num = 123;
        function b() {
            num++;
            console.log(num)
        }
        retrun b;
    }
    var demo = a();
    demo();//124
    demo();//125
    //b函数在定义状态时就有a函数的作用域,执行b函数时使用的是a函数的变量.
    //a函数的变量改变后,b函数注销不了a函数的变量,而且b函数一直有a函数的作用域.
    //b函数的作用域的基础就是a的作用域和全局作用域
1. a 被定义,a就有了一个属性对象[[scope]]作用域,
[[scope]]执行上下文的集合(作用域链)

集合里面有一个GO
GO = {
    this:window,
    window:(Object),
    document:(Object),
    a:function,
}

2.a 执行前创建AO 并放到a函数作用域链顶端
AO = {
    this:window,
    arguments:[],
    a:123,
    b:function,
}

3.a执行时的 作用域链
AO = {
    this:window,
    arguments:[],
    a:123,
    b:function,
}
GO = {
    this:window,
    window:(Object),
    document:(Object),
    a:function,
}

4.a 执行 完之后 释放局部作用域
GO = {
    this:window,
    window:(Object),
    document:(Object),
    a:function,
}
1. 定义b 时, b的作用域链为
a的AO = {
    this:window,
    arguments:[],
    a:123,
    b:function,
}
GO = {
    this:window,
    window:(Object),
    document:(Object),
    a:function,
}

2.执行前 每次执行都创建一个唯一的AO执行完消耗 添加了一个自己的作用域 并排到最上面
b的AO = {
    this:window,
    arguments:[]
}
3.执行时的作用域链
b的AO = {
    this:window,
    arguments:[]
}
a的AO = {
    this:window,
    arguments:[],
    a:123,
    b:function,
}
GO = {
    this:window,
    window:(Object),
    document:(Object),
    a:function,
}
4.执行后 销毁自己的 作用域 AO 回到定义时的作用域
a的AO = {
    this:window,
    arguments:[],
    a:123,
    b:function,
}
GO = {
    this:window,
    window:(Object),
    document:(Object),
    a:function,
}

闭包

函数内部的函数返回到了外部(相当于a作用域必须释放后再执行里面的函数)

闭包的问题

  1. 闭包与变量 例:循环绑定事件
  2. 关于this对象
        var name = 'the window';
        var object = {
            name:'my object',
            getNameFunc:function() {
                return function() {
                    retrun this.name
                }
            }
        }
        alert(object.getNameFunc()()) // the window
        
     //this对象是运行时基于函数的执行环境绑定的而函数被作为对象的方法时this等于那个对象 匿名函数的执行环境具有全局性,因此其this通常指向window,一般是通过new call appla bind 以及自定义赋值才能改变
     
         var name = 'the window';
        var object = {
            name:'my object',
            getNameFunc:function() {
              //var thant = this
                return function() {
                   // retrun thant.name //因为该函数中保留了 //getNameFunc函数的作用域,可以使用它的变量.而他的this等于obj 相当找了一个中间商
                }
            }
        }
        alert(object.getNameFunc()()) // my object
        alert(object.getNameFunc().call(object));  //输出 “my object”
        alert(object.getNameFunc().apply(object));
        alert(object.getNameFunc().bind(object)());  
  1. 内存泄漏

闭包作用


实现累加器

    function a(){
        var num = 0;
     function b() {
         num++;
         console.log(num)
     }
     retrun b
    }
    var demo = a()
    demo()//1
    demo()//2

可以做缓存

    function eater() {
        var food = '';
        var obj = {
            get:function(){
                console.log(food);
                food = '';
            }
            set:function(myFood) {
                food = myFood;
            }
        }
        retrun obj;
    }
    var eater1 = eater()
    eater1.get()
    eater1.set()

实现封装,属性私有化 私有化变量

function Deng(name,wife) {
    var prep = 'xiaoshan';
    this.name = name;
    this.wife = weife;
    this.divore = function() {
        this.wife = prep;
    };
    this.get = function() {
        console.log(prep)
    }
    this.set = function(target) {
        prep = target;
    }
}
var Deng = new Deng('dd','jjj')

var inherit = (function() {
  //F 就是一个私有化的变量
    var F = function(){};
    retrun function (Target,Origin) {
        F.prototype = Origin.prototype;
        Target.prototype = new F();
        target.prototype.constuctor = Target;
        target.prototype.uber = Origin.prototype;
    }
}())


模块化开发,防止变量污染

    <!--命名空间 , 每个人都有自己的js文件 大家的变量有冲突-->
    <script src = 'liuson.js'></script>
    <script src = 'lilin.js'><script>
    //全局
    var org = {
        <!--部门-->
        departiment1:{
        //的谁
            liulin: {
                name : 'abc',
                age : 123
            },
            liusijian: {
                
            }
        }
    } 
    var liulin = org.departiment1.liulin;
    liulin.name
    
    //改别人代码,并且不冲突
    var init = (function () {
        var name = 'abc';
        function callName() {
            console.log(name);
        }
        retrun function () {
            callName();
        }
    }())

init()    

立即执行函数(针对初始化功能的函数)

    functon text() {
        var arr = [];
        for(var i = 0;i < 10; i++) {
            arr[i] = function() {
                console.log(i)
            }
        }
        retrun arr
    }
    var myarr = text() //10个10
     for(var j = 0;j <10; j++) {
        myarr[j]()//10个10 console.log(i)
    }
    //原因是函数的作用域是对象类型的,保存的值是最终的那个值
    //每次arr[i]变化跟后面函数没有关系,因为函数text AO中 i最终是10
//闭包解决闭包
//就是把返回出去的函数,使用的是立即执行函数的AO,不使用text AO中的变量了
//虽然立即执行函数执行释放了,但是里面的函数还有他AO的应用在
      functon text() {
        var arr = [];
        for(var i = 0;i < 10; i++) {
        
         (function (j){
         //j=0
             arr[j] = function(){
                 console.log(j)
             }
         }(i))
        }
        retrun arr
    }
    var myarr = text() 
    for(var j = 0;j <10; j++) {
        myarr[j]()//10个10
    }

//私有化变量

var inherit = (function() {
  //F 就是一个私有化的变量
    var F = function(){};
    retrun function (Target,Origin) {
        F.prototype = Origin.prototype;
        Target.prototype = new F();
        target.prototype.constuctor = Target;
        target.prototype.uber = Origin.prototype;
    }
}())

匿名函数

  1. 异步操作获取值
  2. 同步操作设置值
  3. 方法里面有异步操作的,参数都有一个匿名函数接收值;
  4. 事件处理函数
  5. 里面的this指向最近最近作用域所属的对象.