JavaScript 变量和函数提升问题总结

一 什么是JavaScript
变量提升?

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()
//声明必须在调用前,

— JS程序运行时,

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

(a)变量的声明会被解释器”提升”到方法体内的顶部,初始化赋值操作不提升按顺序执行

– 变量声明前置

mgm娱乐场,变量声明出现在代码中的任何位置都会在该代码执行前处理,这意味着变量可以在声明之前使用。这个行为叫”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是后面赋的值。
这也印证了:变量声明会提升,变量的赋值不会提升!!!

(b)函数体内未声明的变量,解释器会在函数体外声明变量,成为全局变量

– 函数的声明前置

使用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(){}

(c)声明过的函数,整个函数体会被解释器提升到方法体的顶部,初始化赋值操作按顺序执行

3.arguments 是什么?

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

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

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

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

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

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

1-1 变量提升

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

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

eg:变量的声明提升,初始化赋值不提升。

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

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

// 以下都能实现立即执行
(function(){ /* code */ }());
(function(){ /* code */ })();

// function前加一元运算符也可实现
!function () { /* code */ } ();
~function () { /* code */ } ();
-function () { /* code */ } ();
+function () { /* code */ } ();
<script>
    console.log(a);    // undefined
    var a=3;           // 若没有var声明,会报错 a is not defined
    console.log(a);    // 3
    function fn() { 
      console.log(a);  // undefined  
      var a = 'a';   //此a为局部变量,没有这一句上下输出的结果都变为3
      console.log(a);  // a
    }
    fn();
    console.log(a);    // 3
</script>

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
<body>
 <script>
    var a;
    console.log(a);    // undefined
    a=3;               // 若没有var声明,会报错 a is not defined
    console.log(a);    // 3
    function fn() { 
      var a; 
      console.log(a);  // undefined  
      a = 'a';        //没有这一句上下输出的结果都变为3
      console.log(a);  // a
    }
    fn();
    console.log(a);  // 3
  </script>
</body>

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

对比改掉函数中a的var声明

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

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

相关文章