变量提升的常见情况总结

什么叫变量提升?

把当前上下文中带有var(提升声明)/function(提升声明+定义)进行提升的声明或者定义。变量提升是将变量声明提升到它所在作用域的最开始的部分。

  • 全局上下文中:基于var/function声明的变量,也相当于给window设置了对应的属性。

实例 1

var t = 1; 
function a(){
    console.log(t);
    var t=2;
}
a();//undefined;

这是因为函数里的变量t声明提升到函数最开始的部分了。上面的代码1相当于代码2:

//代码2
var t = 1; 
function a(){
    var t;
    console.log(t);
    t=2;
}
a();//undefined;

在函数内部,如果没有用var进行申明,则创建的变量是全局变量,而不是局部变量了。所以,建议变量声明加上var关键字。

//代码3
var t = 1; 
function a(){
    console.log(t)//undefined
    t=4;
    console.log(t);//4
    var t=2;
    console.log(t);//2
}
a();

function声明会比var声明优先级更高一点。

//代码4
console.log(foo);
var foo=10;
console.log(foo);
function foo(){
    console.log(10);
}
console.log(foo);

相当于下面的代码:

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

运行结果如下:

//代码5
function foo(){
    console.log(a);//2
}
function bar(){
    var a=3;
    foo();
}
var a=2;
bar();

​ 上面的代码的运行过程是 bar()-->foo(),此时的a由于在调用 bar()之前已经初始化了,所以相当于给在 foo() 函数的所在作用域中的this 对象添加了a属性,并赋值为2,所以调用 foo() 函数的a时,实际上调用的 foo() 所在作用域的 this 的a属性。

实例 2

function Foo(){
    //未定义,所以是属于this的一个属性,这里的this是window
    getName = function () {
      console.log(1);
    }
    return this;
}
Foo.getName = function() {
  console.log(2);
};
Foo.prototype.getName = function(){
  console.log(3);
};
//变量声明提升,将getName变量提升(只提升定义)
var getName = function(){
  console.log(4);
};
//变量声明提升到作用域的顶部 (声明+定义一起提升)
function getName(){
  console.log(5)
};

Foo.getName(); // 2,直接调用
getName(); // 4,变量声明提升
Foo().getName(); // 1,将getName()方法添加到window对象上
getName(); // 1
new Foo.getName(); // 2,直接调用
new Foo().getName(); // 3 相当于 var f = new Foo()  f.getName()     
new new Foo().getName(); // 3

实例 3

var a = 0
function b(){
  console.log(a) // fun a 函数声明提升到作用域的顶部
  a = 10 //a是局部变量,赋值为10
  console.log(a) // 10
  return;
  function a(){}
}
b()
console.log(a) // 0,此时的a还是外部作用域的a

实例 4

函数执行,函数的作用域[scope]跟它在哪执行无关,只跟它在哪定义有关。

var i = 0;
function A () {
    /**
     * EC(A)
     * 作用域链:<EC(A),EC(G)>
     */
    var i = 10;
    function x () {
        /**
         * EC(X)
         * 作用域链:<EC(x),EC(A)>
         */
        console.log(i);
    }
    return x;
}
var y = A();
y();//10
function B () {
    var i = 20;
    /**
     *函数的作用域跟它在哪执行无关,只跟它在哪定义有关。
     *y的作用域[[scope]]是 EC(A)
     */
    y();//10
}
B();

实例 5

var a = 1;
/**
 * EC(G)
 *  a
 *  fn=0x000000 [[scope]]:EC(G)
 */
function fn (a) {
    /**
     * 私有上下文 EC(FN)
     *  私有变量赋值
     *  a=1
     *   =0x000001 [[scope]]:EC(FN)
     *   =2
     *  作用域链:<EC(FN),EC(G)>
     *  形参赋值:a=1
     *  变量提升:
     *      var a;
     *      function a(){};不需要重新声明,但是需要重新赋值
     */
    console.log(a); //[Function:a]
    var a = 2; //在这里知识重新赋值
    function a () { } //在变量提升阶段都处理完了
    console.log(a);//2
}
fn(a);
console.log(a);//1

例题 6

获取一个变量的值,首先看是否是自己的私有变量,不是话则会根据作用域链向上级上下文查找...一直到全局上下文(window)。找到则返回,没找到则报错:x is not undefined,并且他下面的代码也不会执行了。

//首先看是否是全局变量,不是,则再看是否为window的一个属性,如果还不是,报错:ReferenceError: a is not defined(这行代码一旦报错,下面的代码都不会处理了)
console.log(a);//ReferenceError: a is not defined
a = 12;
function fn () {
    console.log(a);
    a = 13;
}
fn();
console.log(a);//13

实例 7

var foo = "james";
(function (foo) {
    /**
     * EC(ANY)
     *  foo:jmaes
     *  作用域链:<EC(ANY),EC(G)>
     *  形参赋值:foo="james"
     *  变量提升: var foo
     */
    console.log(foo);//james
    var foo = foo || 'world';
    console.log(foo) //jamaes
})(foo)
console.log(foo);//james

实例 8

在新版本浏览器中 function(){} + 函数如果没有出现在 {}中,则变量提升阶段是"声明+定义"(老版本浏览器不论是否出现在{}中都是"声明+定义") + 函数如果出现在{}中(除函数、对象的大括号外)则只声明。

形式1

{
    //把这一行代码之前对于foo的操作都映射给去全局一份
    //之后的操作都认为是私有的
    function foo () { }
    foo = 1
}
console.log(foo) //[Function :foo]

形式2

/**
 * EC(G)
 *  foo
 *  变量提升:function foo
 */
{
    /**
     * EC(BLOCK)
     *  foo=ex000000
     *     =ex000001
     *  变量提升
     *     function foo(n){}
     *     function foo(m){}
     */
    //把之前对foo的操作"映射"给全局
    function foo () { }
    foo = 1
    //把之前对foo的操作"映射"给全局
    function foo () { }
    console.log(foo); //1
}
console.log(foo) //1

形式3

/**
 * EC(G)
 *  foo
 *  变量提升:function foo只声明不定义
 */
 console.log(foo);//undefined
{
    /**
     * EC(BLOCK)
     *  foo=ex000000
     *     =ex000001
     *  变量提升
     *     function foo(n){}
     *     function foo(m){}
     */
    //把之前对foo的操作"映射"给全局
    function foo () { }
    foo = 1
    //把之前对foo的操作"映射"给全局
    function foo () { }
    foo=2;//私有的操作,跟全局无关
    console.log(foo); //2
}
console.log(foo) //1

实例 9

形式1

var x = 1;
function func (x, y = function any () { x = 2 }) {
    //执行func(5)
    /**
     * AO(FUNC)
     *  x=5
     *  y=oxooooo1
     * 作用域链:<EC(FUNC,EC(G))>
     * 形参赋值
     *  x=5;
     *  y=function any(){...}
     * 变量提升:——
     * 代码执行
     *  x=3;
     *  y();
     * conosle.log(x)
     */
    //执行y()
    /**
     * AO(Y)
     * 作用域链:<EC(Y),EC(FUNC)>
     * 形参赋值:——
     * 变量提升:——
     * 代码执行:
     *  x=2
     */
    x = 3;
    y();
    console.log(x);//2
}
func(5);
console.log(x);//1

注意: 在函数执行时。 + 条件1:有形参赋值默认值(不论是否传递实参,也不论默认值的类型) + 条件2:函数体中有变量声明( + 必须是基于let/const/var,注意let/const不允许重复声明,不能和形参变量名一致)。 + 函数体中用function声明的变量必须和形参中的某一个变量名字一致,才会有下述的机制。 这两个条件存在的情况下,除了默认形成的函数私有上下文,还会多创建一个块级私有上下文(函数体到括号包起来的)。

块级私有上下文 + 在其中声明的变量是块级上下文中私有的,和函数私有上下文没啥关系了。 + 它的上级上下文是函数私有上下文。 + 并且会把函数私有上下文"形参赋值"结束后的结果,映射给私有块级上下文中的同名字段。

形式2

var x = 1;
//条件1 形参赋值
function func (x, y = function any () { x = 2 }) {
    //条件 2声明变量
    var x = 3;
    y();//y()是在函数私有上下文中执行的
    //x是私有块级上下文中的x,不是函数私有上下文中的x
    console.log(x);//3
}
func(5);
console.log(x);//1

形式3

var x = 1;
function func (x, y = function anouy () { x = 2 }) {


    var x = 3;
    var y = function any () { x = 4 }
    y();
    console.log(x)//4
}
func(5);
console.log(x);//1

实例 10

var a = 1;
function fn (a) {
    /**
     * EC(FN)
     *  作用域链:<EC(FN),EC(G)>
     *  形参赋值:a=1
     *  变量提升:(会进行赋值覆盖)
     *      var a;这一步浏览器回忽略,因为a私有变量已经存在AO(FN)中了
     *      a=0x001;[[scope]]:EC(FN),不会重复声明,但是会重新赋值
     *  代码执行:
     */
    console.log(a)// Function a
    var a = 2;//赋值
    console.log(a);//2
    function a () { };//代码执行到这里,不会重新赋值,在变量提升阶段已经赋值了
    console.log(a);//2    
}
fn(a);
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 本次Python课程的结课作业是一个小型的数据处理项目。该项目是一个简单的文本分析程序,旨在帮助用户快速了解和处理文本数据。 该项目要求我们使用Python语言编写程序,并读取一段文本。然后,我们需要对该文本进行分析并生成相应的报告。 首先,我们需要对文本进行清理。也就是说,我们需要去除其中所有的标点符号、数字和其他无关单词。这可以通过使用Python内置的字符串处理函数和正则表达式来实现。 接下来,我们需要分析文本中的单词频率。我们可以使用Python中的字典数据类型来实现这一功能。我们需要将每个单词作为键,并将其出现的次数作为值。然后,我们可以将这个字典按照值的大小进行排序,并输出前几个高频单词。 最后,我们可以使用Python中的Matplotlib库将单词频率数据可视化。这将帮助我们更直观地了解词频分布和趋势。 总的来说,这个项目旨在训练我们使用Python语言处理文本数据的能力。在完成本次作业后,我们将能够更熟练地使用Python中的字符串处理函数、正则表达式、字典和Matplotlib库等功能,从而为我们未来的数据处理和分析工作打下坚实的基础。 ### 回答2: Python是一门现代化、高效和通用性语言,由于Python设计与易学易用的特点,一直以来都很受程序员、数据科学家和机器学习领域的青睐。此次Python结课作业,让我更加深入地了解了Python语言的特性和应用。在这期间,我学习了基础的语法和常用的模块。 首先,我了解了Python的基础语法,包括变量、流程语句、函数、类和模块等等。尤其是函数和类这两个方面,让我更深刻地认识到Python语言的面向对象编程(OOP)特性,并且更加强化了我对OOP的理解。 其次,我也学习了许多实用的Python模块,例如Numpy、Pandas、Matplotlib和Scikit-learn等等。这些模块是Python程序员重要利器,在数据处理、数据分析和机器学习领域中都不可或缺。特别是Scikit-learn模块,在我的学习中,这个模块提供了许多高级的机器学习算法,让我能够更深入地探讨这个领域,极大地提升了我的学习和掌握能力。 最后,我从这次Python结课作业中,收获了许多,这让我对Python编程有了更深刻的认识,这门语言有强大的特性和应用,可以便捷地解决众多问题,就有发挥的巨大空间。我也深深体会到了编程对思维的锻炼作用,这次Python结课作业是我在编程学习道路上的重要一步。 ### 回答3: 作为Python的结课作业,我首先想到的是从基础开始复习,进一步加深对Python的理解。为此,我认真学习了Python的语法、常用函数、数据类型、控制语句等内容,并通过实践掌握了各种常用方法和技巧。 在这个过程中,我另外选择了一些Python库和框架进行学习和实践,例如:NumPy、Pandas、Matplotlib、Django等等。这些库和框架能够帮助我们更加高效地处理数据和开发Web应用,可以说是Python中不可或缺的部分。 最后,我还特别留意了一些实际应用和常见问题,例如:数据分析、机器学习、爬虫等等,并尝试了一些具体的实现。这样,我能够更深入地了解Python的优势和应用价值,看到它在现实生活中的作用和可能性。 总之,这个Python的结课作业,不仅让我更加熟悉Python,也让我认识到Python在未来的重要性和发展空间。无论是在学习、工作还是生活中,我都会继续使用Python,并将其应用到更多的领域中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值