js进阶语法之变量提升、函数提升以及参数的命名冲突问题解决

 更新时间:2024年09月10日 09:41:00   作者:I_am_shy  
JavaScript代码在执行前会经历一个预处理阶段,这个阶段主要涉及变量提升和函数提升,下面这篇文章主要介绍了js进阶语法之变量提升、函数提升以及参数的命名冲突问题解决的相关资料,需要的朋友可以参考下

前言

预处理阶段:js在编译代码时有一个预处理阶段,这个过程会将原本的代码进行分割处理;

源程序 ---> 预处理块+ 剩余代码块

预处理块中的代码会优先于剩余代码块中的代码执行

什么是变量提升,函数提升

变量提升:当声明一个变量时,这个声明过程会被预处理机制塞入到预处理块中优先执行,这就是变量提升,使用var定义的变量拥有变量提升

函数提升:当声明一个函数时,这个声明过程会被预处理机制塞入到预处理块中优先执行,这就是函数提升,使用function定义的函数拥有函数提升

提升的判断:当一个变量和函数在正常的顺序中没有声明但被调用时就可能产生“提升”

变量提升

变量提升,将变量名放入预编译的词法环境 var a(undefined)

console.log(a);
var a = 1;

 以上的代码会产生变量提升,它等价于下面的代码,

{// 模拟预处理块
  var a;
}
// 正常执行的剩余代码块
console.log(a); // undefined
a = 1;

因为变量提升的缘故,a会在打印执行前被声明,所以会打印成undefined

函数提升

 函数提升,将函数体放入预编译的词法环境 function b(){}

console.log(b);
function b(){
  console.log('b')
};

 以上的代码会产生函数提升,它等价于下面的代码,

{// 模拟预处理块
  function b(){
    console.log('b')
  };
}

// 正常执行的剩余代码块
console.log(b);

function声明的函数都存在函数提升,所以可以在声明函数之前使用函数(先写出函数的用法,在完成函数的实现)

关于变量提升和函数提升的判断

了解了基本的变量提升和函数提升,接下来开始区分一些二者的区别和同时在场的优先级

1.变量赋值的函数

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

很显然这是由var声明的变量,拥有变量提升,只不过它的值是一个函数体

{// 模拟预处理块
  var a;
}
// 正常执行的剩余代码块
console.log(a); // undefined
a = function(){
  console.log('a')
};

所以这个a会打印成undefined

2.优先级判断

console.log(b);
var b = function(){
  console.log('b1')
};
function b(){
  console.log('b2')
};
console.log(b);

函数提升的优先级是比变量提升的优先级大的,当出现声明同名的变量和函数时,在预处理块中最终都会变成函数,

{// 模拟预处理块
  var b;
  function b(){
    console.log('b2')
  };
}
// 正常执行的剩余代码块
console.log(b);// 能打印'b2'的函数
b = function(){
  console.log('b1')
};
console.log(b);// 能打印'b1'的函数

除了上面这种优先级的理解方式,还有下面这种,将function声明拆成了两个部分,先声明函数名,在赋值函数体,而变量的声明在它们之间

{// 模拟预处理块
  function b
  var b;
  b = function(){
    console.log('b2')
  };
}
// 正常执行的剩余代码块
console.log(b);// 能打印'b2'的函数
b = function(){
  console.log('b1')
};
console.log(b);// 能打印'b1'的函数

tips:以上两种方式的结果是一样的,采用哪种分析方式都可以

 3.同名参数名,变量名,函数名的优先级判断

同名参数名,变量名的判断

var foo = 'hello';
(function(foo){
  console.log(foo);
  var foo = foo || 'world';
  console.log(foo);
})(foo);
console.log(foo);

这里只分析了立即执行函数内的预处理

var foo = 'hello';

(function(foo){
  // 模拟立即执行函数内的预处理块
  {
    var foo;
    foo = foo; //传入的参数('hello')
  }

  // 正常执行的剩余代码块
  console.log(foo);
  foo = foo || 'world';
  console.log(foo);

})(foo);

console.log(foo);

所以最后连续打印了3个hello,传入的参数会在预处理中声明后赋值给同名变量,foo不为undefined,后面的赋值就不会变成world

当同名参数名,变量名,函数名同时存在

function fn (m){
  console.log(m);
  var m = 1;
  function m(){

  }
  console.log(m);
}
fn(10)

 同样有两种理解方式

function fn (m){
  // 模拟预处理块
  {
    var m;
    m = m;// 传入的参数(10)
    function m(){

    }
  };
 }

  // 正常执行的剩余代码块
  console.log(m);// function(){}
  m = 1;
  console.log(m);// 1
}
fn(10)
function fn (m){
  // 模拟预处理块
  {
    function m
    var m;
    m = m;// 传入的参数(10)
    m = function(){

    }
  };
 }

  // 正常执行的剩余代码块
  console.log(m);// function(){}
  m = 1;
  console.log(m);// 1
}
fn(10)

可以看到这个函数的输出和传入的参数没有任何关系,因为传入的参数会在函数提升的前面(函数体赋值的前面),导致传入的参数被覆盖

总结

所以可以得出同名参数名,变量名,函数名的预处理顺序为:

变量提升---参数传递---函数提升 (声明函数名---变量提升---参数传递---函数体赋值)

所以最终这个名称会被函数夺取,这就是函数提升优先级最高的原因 

完整代码和运行结果展示

// 变量提升,将变量名放入预编译的词法环境 var a(undefined)
console.log(a);
var a = function(){
  console.log('a')
};
console.log(a);

// 函数提升,将函数体放入预编译的词法环境 function b(){}
console.log(b);
var b = function(){
  console.log('b1')
};
function b(){
  console.log('b2')
};
console.log(b);

// function b 函数名提升
// var b  变量名提升
// b = (){} 函数体初始化
// 因为函数体初始化总在变量名之后,所以每次都是优先预编译成函数


// 结合自执行函数
var foo = 'hello';
(function(foo){
  console.log(foo);
  var foo = foo || 'world';
  console.log(foo);
})(foo);
console.log(foo);
// 依次输出 hello hello hello

// 预编译后
var foo = 'hello';
(function (foo) {
    var foo;  // undefined;
    foo= 'hello'; //传入的foo的值
    console.log(foo); // hello
    foo = foo || 'world';// 因为foo有值所以没有赋值world
    console.log(foo); //hello
})(foo);
console.log(foo);// hello,打印的是var foo = 'hello' 的值(变量作用域)


// function m(){
//   console.log('m');
// };
// var m;
// m =1;
// function m(){

// }
// console.log(m)

function fn (m){
  console.log(m);
  var m = 1;
  function m(){

  }
  console.log(m);
}
fn(10)

// function m
// var m
// 参数m
// m = (){}
// m = 1

// function > parmas > var

到此这篇关于js进阶语法之变量提升、函数提升以及参数的命名冲突问题解决的文章就介绍到这了,更多相关js变量、函数提升及参数命名冲突内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Webpack打包过程中处理ES6模块的循环依赖问题小结

    Webpack打包过程中处理ES6模块的循环依赖问题小结

    Webpack通过“暂时性引用”特性处理ES6模块的循环依赖,即在模块加载时创建占位符,确保模块能够正确加载,本文介绍Webpack打包过程中如何处理ES6模块的循环依赖,感兴趣的朋友一起看看吧
    2025-02-02
  • javascript事件捕获机制【深入分析IE和DOM中的事件模型】

    javascript事件捕获机制【深入分析IE和DOM中的事件模型】

    这篇文章主要介绍了javascript事件捕获机制,结合实例形式分析了冒泡的原理、事件捕获、各浏览器事件处理机制与IE和DOM中的事件模型等,需要的朋友可以参考下
    2016-12-12
  • JavaScript中将负数转换为正数的超详细过程

    JavaScript中将负数转换为正数的超详细过程

    在JavaScript编程中经常需要处理数字的正负值,如果你需要将一个负数转换为正数,可以使用一些简单的数学操作和条件语句来实现,这篇文章主要介绍了JavaScript中将负数转换为正数的相关资料,需要的朋友可以参考下
    2025-10-10
  • 详解JavaScript中的强制类型转换

    详解JavaScript中的强制类型转换

    这篇文章主要介绍了JavaScript中的强制类型转换,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • JS闭包与延迟求值用法示例

    JS闭包与延迟求值用法示例

    这篇文章主要介绍了JS闭包与延迟求值,结合简单实例分析了JS针对运算量较大的情况下闭包与延迟求值的实现技巧,需要的朋友可以参考下
    2016-12-12
  • 详解JS获取HTML DOM元素的8种方法

    详解JS获取HTML DOM元素的8种方法

    本篇文章主要介绍了详解JS获取HTML DOM元素的8种方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • Javascript 注册事件浅析

    Javascript 注册事件浅析

    JavaScript事件驱动
    2008-08-08
  • Javascript HTML5 Canvas实现的一个画板

    Javascript HTML5 Canvas实现的一个画板

    这篇文章主要为大家详细介绍了Javascript HTML5 Canvas实现的一个画板的相关资料,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-05-05
  • layui实现数据表格点击搜索功能

    layui实现数据表格点击搜索功能

    这篇文章主要为大家详细介绍了layui实现数据表格点击搜索功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-07-07
  • JavaScript+html5 canvas绘制渐变区域完整实例

    JavaScript+html5 canvas绘制渐变区域完整实例

    这篇文章主要介绍了JavaScript+html5 canvas绘制渐变区域的方法,结合完整实例形式分析了canvas颜色调用与图形绘制的相关技巧,需要的朋友可以参考下
    2016-01-01

最新评论