javascript-声明前置/arguments/重载/立即执行函数

一:函数声明和函数表达式有什么区别

1.Function Declaration ( 函数声明 )

Function Declaration 可以定义命名的函数变量,而无需给变量赋值。
Function Declaration 是一种独立的结构,不能嵌套在非功能模块中。可以将它类比为 Variable Declaration(变量声明)。就像 Variable Declaration 必须以“var”开头一样,Function Declaration 必须以“function”开头。

函数名在自身作用域和父作用域内是可获取的(否则就取不到函数了)。

2. Function Expression(函数表达式)

Function Expression 将函数定义为表达式语句(通常是变量赋值)的一部分。通过 Function Expression 定义的函数可以是命名的,也可以是匿名的。Function Expression 不能以“function”开头

  • 区别如下:
    1).以函数声明的方法定义的函数,函数名是必须的,而函数表达式的函数名是可选的;
    2).以函数声明的方法定义的函数,函数可以在函数声明之前调用,而函数表达式的函数只能在声明之后调用;
    3).以函数声明的方法定义的函数并不是真正的声明,它们仅仅可以出现在全局中,或者嵌套在其他的函数中,但是它们不能出现在循环,条件或者try/catch/finally中,而函数表达式可以在任何地方声明。

二、什么是变量的声明前置?什么是函数的声明前置?

  • 变量声明前置:
    JavaScript引擎的工作方式是,先解析代码,获取所有被声明的变量,然后线性运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部。只是只是提升变量的声明,并不会将变量的赋值一同提升上来。
  • 函数声明前置:
    JavaScript引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。

在我们写js 代码的时候,我们有两种写法,一种是函数表达式,另外一种是函数声明方式。
函数表达式 var fn=function fn(){}
函数声明方式 function fn(){code}
我们需要重点注意的是,只有函数声明形式才能被提升。
变量赋值并没有被提升,只是声明被提升了。但是,函数的声明有点不一样,函数体也会一同被提升,函数提升是把整个函数都提到前面去。

三、arguments 是什么?

  • arguments也是个对象,我们把这种对象叫做类数组对象,它的属性名是按照传入参数的序列来的,第1个参数的属性名是’0’,第2个参数的属性名是’1’,以此类推,并且它还有length属性,存储的是当前传入函数参数的个数。


    arguments.png
  • 基于此对象,无需给函数的形参指定参数名以获取函数的参数;
  • js并不会验证传递给函数的参数个数是否等于函数定义的参数个数,所以可以使用arguments调用参数可以不混淆不同函数之间的参数名。另外为也能用arguments来判断当前传入参数的个数是否与我们需要的数量一致;
  • arguments有一个属性callee,结果是可以完整显示函数代码段(ECMAScript4已禁止);
    以上特性可以使得arguments实现“重载”,“递归”等属性;

四、函数"重载"的实现

JavaScript中的函数(或对象方法)完全靠函数名称唯一确定,JS不将参数列表作为区分函数的依据。更关键的是,在JS中,函数是作为一种特殊对象类型存在的,函数的名字只是一个普通的变量,本质与var a = 1中的变量a没什么区别。所以如果我们先后定义了两个同名函数,实际上相当于先后将两个函数对象绑定到了同一个变量上,所以后者必然覆盖前者,不会共存,也自然不存在重载了。

  • 可以在函数内部通过判断参数个数或者类型来实现重载,eg:
 function add(num1, num2) {
    if(arguments.length == 1) {
        return num1;
    }
    if(arguments.length == 2) {
        return num1 + num2;
    }
}

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

表达式:

  • (function(){alert('我是匿名函数')}) ();
    作用:
  • 创建一个独立作用域,避免“变量污染”;

六、用递归求n!

 function   fn(n){
    if(n<=1){
       return 1;
  }    return n*fn(n-1)  ;
}

七、下列代码输出什么?

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

八、写一个函数,返回参数的平方和?

如:

function sumOfSquares(){  }
   var result = sumOfSquares(2,3,4)
   var result2 = sumOfSquares(1,3)
   console.log(result)  //29
   console.log(result2)  //10
8.PNG

九、如下代码的输出?为什么?

console.log(a);  //undefined    已经声明但是没有赋值
    var a = 1;
    console.log(b);//报错 未声明b

十、如下代码的输出?为什么?

sayName('world');    // hello world
    sayAge(10);    //报错 函数表达式必须在调用前声明
    function sayName(name){
        console.log('hello ', name);
    }
    var sayAge = function(age){
        console.log(age);
>};

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

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

输出结果:10
思路:

1.定义一个全局的变量 var x =10;然后调用bar()函数;
2.bar()里面的局部域,其中定义了一个局部的变量 var x =30并调用foo()函数;
3.返回foo()函数,得到console.log(x);因为foo函数本身并未定义任何变量,所以默认是取全局的变量 var x =10,因此结果console.log(x)显示的是全局变量x=10,结果自然是10。

伪代码过程:

globalContext = {
    AO: {
        x: 10
        foo: function(){}
        bar: function(){}
    },
    Scope: null
}

foo.[[scope]] = globalContext.AO
bar.[[scope]] = globalContext.AO

//调用 bar() 时,进入 bar 的执行上下文
barContext = {
    AO: {
        x: 30
    },
    Scope: bar.[[scope]] 
}
// 调用 foo 时,先从 bar 执行上下文中的 AO 里找,找不到再从 bar 的 [[scope]] 里找,找到后即调用

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

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

输出:30
执行foo()时,是在局部进行的,所以会在局部查找x,而不会去全局变量查找。所以找到的x=30,输出30;
伪代码:

globalContext = {
    AO: {
        x: 10
        bar: function(){}
    },
    Scope: null
}

bar.[[scope]] = globalContext.AO 

// 调用 bar 时 进入 bar 的上下文
barContext = {
    AO: {
        x: 30
        foo: function(){}
    },
    Scope: bar.[[scope]]
}

// 声明 foo 时 得到
foo.[[scope]] = barContext.AO

// 调用 foo 时 进入 foo 的上下文
fooContext = {
    AO: {},
    Scope: foo.[[scope]]
}

// 调用 foo 时,先从 bar 执行上下文的 AO 里找,找不到再从 bar 的 全局作用域找,找到后即调用

十三、以下代码输出什么? 写出作用域链的查找过程伪代码

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

输出:30
局部变量是x=30,函数bar()内部直接执行了console.log(x)语句,所以结果输出30
伪代码:

GlobalContext = {
        AO: {
            x: 10,
            bar: function() {}
        }
    }
    bar.[[scope]] = GlobalContext.AO
    
    barContext = {
        AO: {
            x: 30,
            匿名函数: function () {}
        },
        scope: GlobalContext.AO
    }
    
    匿名函数Context = {
        AO: {},
        scope: barContext.AO
    }

十四、以下代码输出什么? 写出作用域链的查找过程伪代码

var a = 1; //声明全局变量a=1
function fn(){  //声明函数fn
  console.log(a) //声明前置并未赋值,输出undefined
  var a = 5    //函数内部定义变量a并赋值为5,fnContext中a=5
  console.log(a) //输出5
  a++  //a自加,fnContext中a=6
  var a
  fn3()
  fn2()  //执行函数fn2和fn3
  console.log(a)  //此时的a仍是全局变量中的a=1,输出1
  function fn2(){
    console.log(a) //函数fn2中,此语句执行时,fnContext中a=6,输出6
    a = 20  //赋值fnContext中a=20
  }
}
function fn3(){
  console.log(a) //此时fnContext中a=20,输出20
  a = 200 //赋值fnContext中a=200
}
fn() //执行函数fn,执行结束后得到fnContext中a=200
console.log(a) //globalContext中a = 200,所以输出200

输出:undefined 5 1 6 20 200
伪代码:

globalContext = {
    AO: {
        a: 1
        fn: function(){}
        fn3: function(){}
    },
    Scope: null
}

fn.[[scope]] = globalContext.AO
fn3.[[scope]] = globalContext.AO

// 调用 fn 时 进入 fn 的上下文
fnContext = {
    AO: {
        a:undefined,//解析时的值是undefined,
        fn2: function(){}
    },
    Scope: fn.[[scope]] //globalContext.AO
}
    fn2.[[scope]] = fnContext.AO

fn3Context = {
        AO: {
           a: 200, 
        },
        Scope:fn3.[[scope]]//globalContext.AO 
    }
    fn2Context = {
        AO: {
          a: 20,
        }, 
        Scope:fn2.[[scope]]//fnContext.AO
    }
  • 个人备注:
    AO: Active Object
    [[scope]]:
    函数内部的[[scope]]属性是为了让我们更好的理解函数,虚拟出来的一个属性,我们实际访问时访问不到这个属性的。
    我们在创建函数时就会生成这样的一个属性,这个属性保存着这个函数的父作用域的作用域链。在函数执行时,函数会生成一个scope属性,这个属性保存着函数在执行上下文时创建的活动对象(活动对象包括函数内部的局部变量和函数参数)和函数的内部的[[scope]]属性。(https://blog.csdn.net/q1056843325/article/details/53086893?locationNum=12&fps=1)

推荐阅读更多精彩内容

  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    枫叶appiosg阅读 2,227评论 0 12
  • 请各位读者添加一下作者的微信公众号,以后有新的文章,将在微信公众号直接推送给各位,非常感谢。 最近感冒了,身体太疲...
    MR_LIXP阅读 288评论 1 5
  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 1,390评论 0 5
  • 本地有风俗,临产的时候,母亲不要守在身边。原先听了只觉得是婆婆说着玩。 母亲也不信,她怎么能不在我身边呢,...
    从小笑到大笑阅读 72评论 0 0
  • 2018年度美国国际电子消费展(CES)正在拉斯维加斯如火如荼的进行着。我在跨洋的另一端——中国,远程感受CES的...
    互联网追风人阅读 119评论 0 0