美文网首页
函数是什么,作用域链

函数是什么,作用域链

作者: yytyff | 来源:发表于2017-04-16 11:16 被阅读0次

    JavaScript函数是指一个特定代码块,可能包含多条语句,可以通过名字来供其它语句调用以执行
    函数包含的代码语句。

    比如我们有一个特定的功能需要三条语句实现

      statement1;
      statement2;
      statement3;
    

    那么每次想实现这个功能的时候就需要写这三句话,很麻烦,我们可以把这三条语句打包为一个函数

      function doSomething(){ 函数的命名一般用动词,两个单词用驼峰
        statement1;
        statement2;
        statement3;
      }
    

    这样每次想实现功能的时候我们就调用一下函数就可以了,调用函数通过函数名称()的形式调用

      doSomething();
    

    声明函数

    使用function关键字可以声明一个函数

      //函数声明
      function sayHello(){
        console.log('hello')
      }
    
      //函数调用
      sayHello()
    

    声明不必放到调用的前面

    函数表达式

      var sayHello = function(){
        console.log('hello');
      }
    
      sayHello()
    

    声明必须放到调用的前面

    参数

      function sayHello(name){
        console.log('hello ' +  name)
      }
    
      sayHello('若愚')
      sayHello('饥人谷)
    

    多个参数

    函数在定义的时候可以写多个参数

      function printInfo(name, age, sex){
        console.log(name);
        console.log(age);
        console.log(sex);
      }
      printInfo('饥人谷', 2, 'boy')(其实并不能一一对应)
    

    arguments

    在函数内部,你可以使用arguments对象获取到该函数的所有传入参数

      function printPersonInfo(name, age, sex){
        console.log(name);
        console.log(age);
        console.log(sex);
        console.log(arguments);
      }
    

    重载

    其他语言重载范例:例如C语言中的
    int sum(int num1, int num2){  整数
      return num1 + num2;
    }
    
    float sum(float num1, float num2){  浮点数
      return num1 + num2;
    }
    
    sum(1, 2);
    sum(1.5, 2.4);
    

    在 JS 中
    没有重载! 同名函数会覆盖。 但可以在函数体针对不同的参数调用执行相应的逻辑

    function printPeopleInfo(name, age, sex){
        if(name){
          console.log(name);
        }
    
        if(age){
          console.log(age);
        }
    
        if(sex){
          console.log(sex);
        }
      }
    
    
      printPeopleInfo('Byron', 26);
      printPeopleInfo('Byron', 26, 'male');
    

    返回值

    有时候我们希望在函数执行后得到一个结果供别人使用,可以通过return来实现

      function sum(a, b){
        a++;
        b++;
        return a + b;
      }
    
      var result = sum(2, 3);
      conslole.log(result);
    

    注意点
    如果不写return语句,函数也会默认给我们返回undefined

    注意点2
    函数在执行过程中,只要遇到return就会立即结束退出函数体

    function fn(a){
      if(a < 0){
        return;  
      }
      //下面没用 else ,但效果一样
      a++;
      return a + a;
    }
    

    注意点3
    函数的返回值和 console.log()是两个不同的东西,千万不要这样

    function getAge(age){
      return console.log(age);
    }
     console.log()作用是我们去调试代码,是把东西展示出来
    

    声明前置

    var 和 function 的声明前置
    在一个作用域下,var 声明的变量和function 声明的函数会前置

    console.log(a); //undefined
    var a = 3;
    console.log(a); //3
    
    sayHello();
    
    function sayHello(){
      console.log('hello');
    }
    

    函数表达式

      console.log(fn); //undefined
      fn(); //报错
    
      var fn = function(){}
    

    函数表达式和 var 一个变量没什么区别

    先有,再 用

    函数内部的声明前置

    function fn(){
      console.log(a)  //undefined
      var a = 3
      console.log(a)
    }
    fn()
    

    当命名冲突时

    先 前置,再 覆盖

       var fn = 3;
      function fn(){}
    
      console.log(fn); // 3
    
      function fn(){}
      var fn = 3;
    
      console.log(fn); // 3
    

    参数重名

      function fn(fn){
        console.log(fn);
    
        var fn = 3;
        console.log(fn);
      }
    
      fn(10); //10 3
    
    

    作用域

    在 JS 中只有函数作用域,没有块作用域

      function fn(){
        var a =1;
    
        if(a > 2){
          var b = 3;
        }
        console.log(b);
      }
    
      fn(); // undefined
    
      console.log(a); // "ReferenceError: a is not defined
    

    var

    声明一个已经存在的变量

    function fn(){}
    var fn
    console.log(fn)
    
    var a = 1
    var a
    var a 
    console.log(a)
    

    var重复声明一个已经存在的变量,原变量值不变

    不加var作用

    function fn(){
      a = 1;
    }
    
    fn();
    
    console.log(a); // 1
    

    可以看到不写var会声明一个全局的变量,这是我们在编程中应该要避免的,即使真的需要全局变量,也应该在最外层作用域使用var声明

    更深入了解,参考下一节 作用域链

    递归

    自己调用自己
    设定终止条件
    优点: 算法简单
    缺点: 效率低

    求 n 的阶乘 n!

    function factor(n){
      if(n === 1) {
        return 1
      }
      return n * factor(n-1)
    }
    
    factor(5)
    

    求 1+2+...+n 的值

    function sum(n){
      if(n === 1) {
        return 1
      }
      return n + sum(n-1)
    }
    sum(10)
    

    立即执行函数表达式

    (function(){      用小括号,里面相当于 一个表达式
      var a  = 1;
    })()
    console.log(a); //undefined
    

    作用: 隔离作用域

    其他写法

    (function fn1() {});
     
    // 在数组初始化器内只能是表达式
    [function fn2() {}];
     
    // 逗号也只能操作表达式
    1, function fn3() {};
    

    作用域链

    相关概念

    执行上下文 executionContext
    活动对象 AO
    Scope 属性

    执行顺序

    范例1

    var x = 10
    bar()
    function foo() {
      console.log(x)
    }
    function bar(){
      var x = 30
      foo() //  输出什么
    }
    
    globalContext = {
      AO: {
        x: 10
        foo: function
        bar: function
      },
      Scope: null
    }
    
    //声明 foo 时 得到下面
    foo.[[scope]] = globalContext.AO
    //声明 bar 时 得到下面
    bar.[[scope]] = globalContext.AO
    

    注意: 在当前的执行上下文内声明的函数,这个函数的[[scope]]就执行当前执行上下文的 AO

    范例2

    var x = 10;
    bar() //  输出什么
    function bar(){
      var x = 30;
      function foo(){
        console.log(x)
      }
      foo();
    }
    
    globalContext = {
      AO: {
        x: 10
        bar: function
      },
      Scope: null
    }
    
    //声明 bar 时 得到下面
    bar.[[scope]] = globalContext.AO
    

    注意: 在当前的执行上下文内声明的函数,这个函数的[[scope]]就执行当前执行上下文的 AO

    当调用 bar() 时, 进入 bar 的执行上下文

    barContext = {
      AO: {
        x: 30,
        foo: function
      },
      Scope: bar.[[scope]] //globalContext.AO
    }
    //在 bar 的执行上下文里声明 foo 时 得到下面
    foo.[[scope]] = barContext.AO
    

    当调用 foo() 时,先从 bar 执行上下文中的 AO里找,找到后即调用

    当调用 foo() 时,进入 foo 的执行上下文

    fooContext = {
      AO: {},
      Scope: foo.[[scope]] // barContext.AO
    }
    

    所以 console.log(x)是 30

    相关文章

      网友评论

          本文标题:函数是什么,作用域链

          本文链接:https://www.haomeiwen.com/subject/iqhaattx.html