JS函数与作用域

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

在ECMAScript中,有两个最常用的创建函数对象的方法,即使用函数表达式或者使用函数声明。对此,ECMAScript规范明确了一点,即是,即函数声明 必须始终带有一个标识符(Identifier),也就是我们所说的函数名,而函数表达式则可以省略。

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

    ECMA 5(13.0)定义语法:
    Function Identifier ( FormalParameterList[opt] ) { FunctionBody }

function bar() {
        return 3
    }
bar();//3
bar;// function
  • 什么是 Function Expression(函数表达式)?
    Function Expression 将函数定义为表达式语句(通常是变量赋值)的一部分。通过 Function Expression 定义的函数可以是命名的,也可以是匿名的。Function Expression 不能以“function”开头(下面自调用的例子要用括号将其括起来)。
//anonymous function expression
var a = function() {
 return 3;
}
  
//named function expression
var a = function bar() {
 return 3;
}
  
//self invoking function expression
(function sayHello() {
 alert("hello!");
})();

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

这里引用 Ben Cherry的话:“Function declaration和function variable(函数变量)通常会被 JavaScript 解释器移(‘hoisted')到当前作用域顶部”。对于variable(变量)也是如此。

  • 变量声明前置:JavaScript引擎的工作方式是,先解析代码,获取所有被声明的变量,然后线性运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部。
console.log(a);
    var a = 1;
    //var a;
    //console.log(a); undefined;
    //a =1;
  • 函数声明前置:JavaScript引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。
bar();// 4
function bar() {
 return 4;
    };

arguments 是什么?

  • arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。
var argumentTest = function() {
        console.log(arguments[0]);
        console.log(arguments[1]);
        console.log(arguments[2]);
    };
argumentTest(1,2,3);
//1
//2
//3

函数的"重载"怎样实现?

  • 重载是很多面向对象语言实现多态的手段之一,在静态语言中确定一个函数的手段是靠方法签名——函数名+参数列表,也就是说相同名字的函数参数个数不同或者顺序不同都被认为是不同的函数,称为函数重载。
  • 在JavaScript中没有函数重载的概念,函数通过名字确定唯一性,参数不同也被认为是相同的函数,后面的覆盖前面的,但可以在函数体针对不同的参数调用执行相应的逻辑。
function printPeopleInfo (name, age, sex){
    if(name){
      console.log(name);
    }
    if(age){
      console.log(age);
    }
    if(sex){
      console.log(sex);
    }
    }
  printPeopleInfo("Byron", "male");// "Byron", "male"
  printPeopleInfo("Byron", 26, "male");//"Byron", 26, "male"

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

函数定义后如果不调用其不会被执行。而立即执行函数指定义函数后立即执行函数。其作用是隔离作用域减少对全局作用域的污染。常见的形式有:

(function(){var a =1;})();
var i = function(){return 10;}();
true && function(){return 10;}();
0, function(){return 10;}();

求n!,用递归来实现?

程序调用自身的编程技巧称为递归( recursion)。

function recursion(n) {
        if(n === 1 || n === 0) {
            return 1;
        }else {
            return n*recursion(n-1);
        }
    }

代码题目1

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, '男'); 
  // name:饥人谷; age:2;sex:男; ['饥人谷', 2, '男']; name valley;
  getInfo('小谷', 3); 
  // name: 小谷; age: 3; sex: undefined; ['小谷', 3]; name valley;
  getInfo('男'); 
  // name: 男; age: undefined; sex: undefined; ['男']; name valley;

代码题目2: 写一个函数,返回参数的平方和

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

代码题目3, 如下代码的输出?为什么?

console.log(a);// undefined
var a = 1;
console.log(b);// Uncaught ReferenceError: b is not defined.
/*原因,解析器解析过程。
var a; // undefined.
console.log(a);  //undefined.
a = 1;
console.log(b); b没声明也没赋值,所有报错,b is not defined
 */

代码题目4, 如下代码的输出?为什么?

sayName('world');//输出结果 hello world.
sayAge(10);// TypeError: sayAge is not a function.
function sayName(name){
console.log('hello ', name);
    }
var sayAge = function(age){
console.log(age);
    };  
/*
解析过程。
函数,变量在作用域前置。
function sayName(name){
console.log('hello ', name);
    }
var sayAge;// undefined.
sayName('world'); //输出结果 hello world.
sayAge(10); // TypeError: sayAge is not a function.
sayAge = function(age){
console.log(age);
    };  
 */

代码题目5, 如下代码输出什么? 写出作用域链查找过程伪代码。

var x = 10
bar() // 10
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
//调用bar时,bar的执行上下文
barContext = {
    AO:{
     x:30
     foo:function
    }
    scope:bar.[[scope]] = globalContext.AO
}
//调用foo时,先从bar的AO中,找不到再从scope中找,这里在barConetext中能够找到
,就立即调用。
//调用时进入foo的执行上下文
fooContext = {
    AO:{    
     x:10;
    }
    scope:foo.[[scope]] = globalContext.AO
}
//所以从scope中即globalContext.AO中可以找到x:10。
 */

代码题目6, 如下代码输出什么? 写出作用域链查找过程伪代码。

var x = 10;
bar(); // 30 
function bar(){
  var x = 30;
  function foo(){
    console.log(x) // 30
  }
  foo();
}   
/*解析过程
全局作用域
globalContext = {
    AO: {
    x: 10,
    bar: function
    }
    scope =  null
}
//调用bar()的时候,bar的执行上下文是 bar[[scope]] = globalConext
barContext = {
    AO: {
    x: 30,
    foo: function
    }
    bar[[scope]] = globalConext.AO
}
//调用foo的时候,foo的执行上下文是 foo[[scope]] = barContext
fooConext = {
    AO: {
    x: 30 //只能从上一个作用域找
    }
    foo[[scope]] = barContext.AO
}
//foo()输出的结果为30,foo()的作用域属于bar()。
所以bar()里面的函数执行结果为30,bar()为30。
 */

代码题目7, 如下代码输出什么? 写出作用域链查找过程伪代码。

var x = 10;
bar() // 30
function bar(){
  var x = 30;
  (function (){
    console.log(x)// 30
  })()
}
/*解析过程
globalContext = {
    AO: {
    x : 10,
    bar : function
    }
    scope = null
}

barContext = {
    AO: {
    x: 30
    function() {}
    }
    bar[[scope]] = globalConext.AO
}

functionContext= {
    AO: {
    x: 30 //x为30, 只能从bar里找活动对象。
    }
    function[[scope]] = barConext.AO
}
所以(function (){console.log(x)})为30,同时返回给它的上文bar (),所以bar()为30。
 */

代码题目8, 如下代码输出什么? 写出作用域链查找过程伪代码。

var a = 1;
function fn(){
  console.log(a)//undefined
  var a = 5
  console.log(a)//5
  a++//6 
  var a
  fn3()
  fn2()
  console.log(a)//20
  function fn2(){
    console.log(a)//6
    a = 20
  }
}
function fn3(){
  console.log(a)//1
  a = 200
}
fn();
console.log(a);//200
/*
解析过程
1. globalContext = {
    AO: {
    a: 1, ==> 200
    fn: function,
    fn3: function
    },
    scope = null
}
fn[[scope]] = globalConext, fn3[[scope]] = globalConext.

2. fnContext = {
    AO: {
    a: 5, ==>6 ===>20 
    fn2: function
    }
    fn2[[scope]] = fnContext.AO
}

3. fn3Context = {
    AO: {
    a: 1
    },
    fn3[[scope]] = globalConext.AO
}
 */

推荐阅读更多精彩内容

  • 1.函数声明和函数表达式有什么区别 函数声明 代码执行时函数声明会被提升到最前执行,所以函数的调用与函数声明的顺序...
    Feiyu_有猫病阅读 76评论 0 0
  • JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。 函数声明和函数表达式有什么区...
    毕子歌阅读 50评论 0 0
  • 1.函数声明和函数表达式有什么区别 函数声明可以看作是函数的初始化,我们将给函数传参并建立函数体的表达式,当我门建...
    高进哥哥阅读 26评论 0 0
  • 函数声明和函数表达式有什么区别 JavaScript 中需要创建函数的话,有两种方法:函数声明、函数表达式,各自写...
    萧雪圣阅读 503评论 2 2
  • 应朋友之邀,设计了这几款《权利的游戏》主题T恤,在第七季结束之前,以这种特殊的方式作为纪念。希望你们能喜欢,嘿嘿
    丑丑苑阅读 93评论 0 0