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

函数,作用域链

作者: _达斯基 | 来源:发表于2017-10-10 08:32 被阅读0次

    1.函数声明和函数表达式有什么区别

    函数声明语法:

    function functionName(arg0,arg1,arg2){ //函数体 }
    

    函数表达式语法

    var function = function(arg0,arg1,arg2){ //函数体 }
    

    区别:
    使用function关键字可以声明一个函数,它的特征是函数声明提升,执行代码前会先读取函数声明,即函数声明不必放在调用的前面,它可以放在当前作用域任何位置;函数表达式在使用前必须先赋值,所以声明必须放在调用前面,不然浏览器解析代码时会认为函数还不存在而抛出错误,理解函数提升的关键就是理解函数声明与函数表达式之间的区别。

    错误做法

    //函数调用
    sayHi();
    //函数声明
    var sayHi = function(){
      console.log('Hi!');
    };
    会抛出错误:函数还不存在
    

    正确写法:

    //函数声明
    function sayHi(){
    console.log('hello');
    }
    //调用
    sayHi()
    //调用可以在声明之前
    
    var sayHi = function() {
    console.log();
    }
    sayHi()
    //声明必须在调用前,
    

    2.什么是变量的声明前置?什么是函数的声明前置

    - 变量声明前置

    变量声明出现在代码中的任何位置都会在该代码执行前处理,这意味着变量可以在声明之前使用。这个行为叫"hoisting",即把在指定作用域内声明的变量提升到函数或全局代码的顶部。
    声明变量的作用域限制在其声明位置的上下文中,而未声明变量总是全局的,所以总在作用域最开始声明变量可以使变量的作用域变得清晰。

    console.log(a);  //undefined
    var a=1;
    console.log(b);  //ReferenceError:b is not defined
    

    由上到下执行代码之前,解析器会先找关键字var,找到了var a,就提升var a并将a初始化为undefined
    再由上往下执行,读到consolo.log(a),控制台打印出来的就是undefined
    接着给变量a赋值为1,如果这个时候后面再加一句consolo.log(a),那么控制台就会多打印出一个1
    console.log(b)的结果很好理解,因为没有声明变量b所以抛出错误
    也就是说变量声明会在代码执行之前就创建、初始化并赋值undefined

    注意:
    提升的是var a而不是var a=1,var a=1是后面赋的值。
    这也印证了:变量声明会提升,变量的赋值不会提升!!!
    

    - 函数的声明前置

    使用function关键字可以声明一个函数,它的特征是函数声明提升,执行代码前会先读取函数声明,即函数声明不必放在调用的前面,它可以放在当前作用域的任何位置。
    示例:

    a();
    function a(){
    console.log("hello world")
    };
    

    js引擎有以下过程:
    1找到所有用function声明的变量,在环境中创建这些变量
    2将这些变量初始化并赋值为function(){console.log("hello world")}
    3开始执行代码a()
    也就是说function声明会在代码执行之前就创建、初始化、赋值。

    注意:变量声明和函数声明都会提升,函数声明提升的优先级高于变量声明提升。
    示例:
    console.log(a);
    var a=1;
    function a(){};
    //输出结果a是一个函数即:ƒ a(){}
    

    3.arguments 是什么?

    arguments是一个类数组对象,除了length属性外没有任何数组属性,是所有函数中可用的局部变量,仅在函数内部有效。

    • 可通过arguments对象来访问函数的参数列表,使用方括号语法访问参数列表的每一个元素,第一个条目的索引为0,即第一项为arguments[0],第二项为arguments[1],以此类推。

    • 通过访问arguments对象的length属性可以确有多少个参数传递给了函数。

    • arguments对象中的值可以被重写并会自动反映到对应的命名参数,所以值与对应命名参数的值保持同步。如果只传入了一个参数,那么为arguments[1]设置的值不会反映到命名参数中,因为arguments对象的长度由传入的参数个数决定,而不由定义函数时的命名参数的个数决定。

    • 在严格模式下重写arguments的值会导致语法错误,代码不会执行。

    使用场景:调用一个函数时,当这个函数的参数数量比它显式声明的
    参数数量更多时,就可以使用 arguments 对象。
    

    4.函数的"重载"怎样实现

    • 概念:函数重载指同一函数名对应着多个函数的实现。即每种实现对应一个函数体,这些函数名字相同,但参数类型或个数或顺序不同。

    • 函数重载主要是为了解决几个问题
      可变参数类型
      可变参数个数
      可变参数顺序

    • 基本设计原则:当两个函数除了参数类型和参数个数不同以外其他功能完全相同时,利用函数重载;两个函数功能不同时不应使用重载,而应使用一个名字不同的函数。

    js是弱类型语言,参数不是固定的某个类型,所以在js中没有重载,同名函数后面的会覆盖前面的。但我们也可以实现重载所需要的功能。

    • 实现:
      写一个函数,在函数体内针对不同的参数调用执行不同的逻辑。
    function printPeopleInfo(name,age,sex){
        if(name){
            console.log(name);
        }
        if(age){
            console.log(age);
        }
        if(sex){
            console.log(sex);
        }
    }
    printPeopleInfo("dot",23);  //dot 23
    printPeopleInfo("dot","female",23);  //dot female 23
    
    
    function add(){
        var num=0;
        for(var i=0;i<arguments.length;i++){
            num+=arguments[i];
        }
        console.log(num);
    }
    add(1);  //1
    add(1,2,3);  //6
    
    

    始终记住函数名只是一个指向函数对象的指针,并不会与某个函数绑定

    5.立即执行函数表达式是什么?有什么作用

    • 立即执行函数表达式:
      缩写IIFE,是一种利用javascript函数生成新作用域的编程方法,也叫自执行函数。
    • 作用:
      1令函数中声明的变量绕过js的变量置顶声明规则
      2避免新的变量被解释成全局变量或函数名占用全局变量名的情况
      3在禁止访问函数內变量声明的情况下允许外部对函数的调用
    • 实现:因js里的()里不能包含语句,所以解析器会将()里的代码解析成function表达式并立即执行。
    // 以下都能实现立即执行
    (function(){ /* code */ }());
    (function(){ /* code */ })();
    
    // function前加一元运算符也可实现
    !function () { /* code */ } ();
    ~function () { /* code */ } ();
    -function () { /* code */ } ();
    +function () { /* code */ } ();
    

    6.求n!,用递归来实现

    1.方法一

    var factorial = (function f(n){
        if (n <= 0){
            return 1;
        } else {
            return n * f(n-1);
        }
    });
    factorial(5);  //120
    

    2.方法二

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

    7.以下代码输出什么?

    function getInfo(name, age, sex) {
        console.log('name:', name);
        console.log('age:', age);
        console.log('sex:', sex);
        console.log(arguments);
        arguments[0] = 'valley';
        console.log('name', name);
    }
    
    getInfo('饥人谷', 2, '男');
    getInfo('小谷', 3);
    getInfo('男');
    
    

    输出分别为:

    name: 饥人谷
    age: 2
    sex: 男
    ["饥人谷",2,"男"]
    name valley
    
    name: 小谷
    age: 3
    sex: undefined
    ["小谷",3]
    name valley
    
    name: 男
    age: undefined
    sex: undefined
    ["男"]
    name valley
    

    8.写一个函数,返回参数的平方和

    function sumOfSquares() {
        var sum = 0;
        for (var i = 0; i < arguments.length; i++) {
            sum += Math.pow(arguments[i], 2);
        }
        return sum;
    }
    var result = sumOfSquares(2, 3, 4);
    var result2 = sumOfSquares(1, 3);
    console.log(result);  //29
    console.log(result2);   //10
    

    9.如下代码的输出是?为什么

    console.log(a);  //undefined,因为变量a声明提升并赋值为undefined,先读取变量声明
    var a = 1;
    console.log(b);  //抛出ReferenceError:b is not defined,因为b没有声明
    

    10.如下代码的输出是?为什么

    sayName('world');
    sayAge(10);
    function sayName(name){
        console.log('hello ', name);  //hello world,因为sayName函数声明提升
     }
    var sayAge = function(age){
        console.log(age);  //抛出TypeError: sayAge is not a function,因为sayAge是函数表达式,使用前必须赋值,而声明放在了调用的后面,此时函数还不存在,所以会报错
     };
    
    

    11.如下代码输出什么? 写出作用域链查找过程伪代码

    var x = 10;
    bar() ;
    function foo() {
      console.log(x);
    }
    function bar(){
      var x = 30;
      foo();
    }
    

    结果为10

    // 对应的作用域链
    globalContext = {
        AO: {
            x: 10,
            bar: function() {}
            foo: function() {}
        }
        Scope: null
    }
    bar.[[scope]] = globalContext.AO;
    foo.[[scope]] = globalContext.AO;
    barContext = {
        AO: {
            x: 30,
        }
        Scope: globalContext.AO;
    }
    fooContext = {
        AO: {},
        Scope: globalContext.AO;
    }
    
    

    12.如下代码输出什么? 写出作用域链查找过程伪代码

    var x = 10;
    bar() 
    function bar(){
      var x = 30;
      function foo(){
        console.log(x) 
      }
      foo();
    }   
    

    结果为30

    //  对应的作用域链
    globalContext = {
        AO: {
            x: 10,
            bar: function() {}
        }
        Scope: null
    }
    bar.[[scope]] = globalContext.AO;
    barContext = {
        AO: {
            x: 30,
            foo: function() {}
        }
        Scope: globalContext.AO;
    }
    foo.[[scope]] = barContext.AO;
    fooContext = {
        AO: {},
        Scope: barContext.AO;
    }
    
    

    13、如下代码输出什么? 写出作用域链查找过程伪代码

    var x = 10;
    bar() 
    function bar(){
      var x = 30;
      (function (){
        console.log(x)
      })()
    }
    

    结果为30

    // 作用域链
    globalContext = {
        AO: {
            x: 10,
            bar: function() {};
        }
        Scope: null
    }
    bar.[[scope]] = globalContext.AO;
    
    barContext = {
        AO: {
            x: 30,
             anonymity :function() {}
        }
        Scope:globalContext.AO;
    }
    anonymity.[[scope]] = barContext.AO;
    
    
    

    14、如下代码输出什么? 写出作用域链查找过程伪代码、

    var a = 1;
    
    function fn(){
      console.log(a)         // (1)
      var a = 5
      console.log(a)         //  (2)
      a++
      var a
      fn3()                         // (3)
      fn2()                         // (4)
      console.log(a)          // (5)      
    
      function fn2(){
        console.log(a)
        a = 20
      }
    }
    
    function fn3(){
      console.log(a)
      a = 200
    }
    
    fn()
    console.log(a)                    // (6)
    
    

    在上面标注了输出时代码的序号。

    // 执行到(1)时对应的作用域链
    globalContext = {
        AO:{
            a:1,
            fn: function() {},
            fn3: function() {},
        }
        Scope: null;
    }
    fn.[[scope]] = globalContext.AO;
    fn3.[[scope]] = globalContext.AO;
    
    fnContext = {
        AO: {
            a: undefined,
            fn2: function() {}
        }
        scope: fn.[[scope]] = globalContext.AO;
    }
    fn2.[[scope]] = fnContext.AO;
    

    (1)此时输出 undefined

    // 执行到(2)时对应的作用链
    globalContext = {
        AO:{
            a:1,
            fn: function() {},
            fn3: function() {},
        }
        Scope: null;
    }
    fn.[[scope]] = globalContext.AO;
    fn3.[[scope]] = globalContext.AO;
    
    fnContext = {
        AO: {
            a: 5,
            fn2: function() {}
        }
        scope: fn.[[scope]] = globalContext.AO;
    }
    fn2.[[scope]] = fnContext.AO;
    

    (2)此时结果:undefined 5

    // 执行到(3)时对应的作用域链
    globalContext = {
        AO:{
            a:1,
            fn: function() {},
            fn3: function() {},
        }
        Scope: null;
    }
    fn.[[scope]] = globalContext.AO;
    fn3.[[scope]] = globalContext.AO;
    
    fnContext = {
        AO: {
            a: 6,
            fn2: function() {}
        }
        scope: fn.[[scope]] = globalContext.AO;
    }
    fn2.[[scope]] = fnContext.AO;
    
    fn3Content = {
        AO:{}
        scope: fn3.[[scope]] = globalContext.AO;
    }
    

    (3)此时结果:undefined 5 1

    // 执行到(4)对应......
    globalContext = {
        AO:{
            a:200,
            fn: function() {},
            fn3: function() {},
        }
        Scope: null;
    }
    fn.[[scope]] = globalContext.AO;
    fn3.[[scope]] = globalContext.AO;
    
    fnContext = {
        AO: {
            a: 6,
            fn2: function() {}
        }
        scope: fn.[[scope]] = globalContext.AO;
    }
    fn2.[[scope]] = fnContext.AO;
    
    fn3Content = {
        AO:{}
        scope: fn3.[[scope]] = globalContext.AO;
    }
    
    fn2Content = {
        AO: {}
        scope: fn2.[[scope]] = fnContext.AO;
    }
    
    

    (4)此时结果为 undefined 5 1 6

    // (5)对应的作用链
    globalContext = {
        AO:{
            a:200,
            fn: function() {},
            fn3: function() {},
        }
        Scope: null;
    }
    fn.[[scope]] = globalContext.AO;
    fn3.[[scope]] = globalContext.AO;
    
    fnContext = {
        AO: {
            a: 20,
            fn2: function() {}
        }
        scope: fn.[[scope]] = globalContext.AO;
    }
    fn2.[[scope]] = fnContext.AO;
    
    fn3Content = {
        AO:{}
        scope: fn3.[[scope]] = globalContext.AO;
    }
    
    fn2Content = {
        AO: {}
        scope: fn2.[[scope]] = fnContext.AO;
    }
    

    (5)此时结果为undefined 5 1 6 20

    //  (6)
    globalContext = {
        AO:{
            a:200,
            fn: function() {},
            fn3: function() {},
        }
        Scope: null;
    }
    fn.[[scope]] = globalContext.AO;
    fn3.[[scope]] = globalContext.AO;
    
    fnContext = {
        AO: {
            a: 20,
            fn2: function() {}
        }
        scope: fn.[[scope]] = globalContext.AO;
    }
    fn2.[[scope]] = fnContext.AO;
    
    fn3Content = {
        AO:{}
        scope: fn3.[[scope]] = globalContext.AO;
    }
    
    fn2Content = {
        AO: {}
        scope: fn2.[[scope]] = fnContext.AO;
    }
    
    

    (6)输出结果 : undefined 5 1 6 20 200

    相关文章

      网友评论

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

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