精华内容
下载资源
问答
  • 变量声明和变量使用的注意事项

    千次阅读 2020-04-06 13:37:30
    一、变量声明的注意事项   1. 重复的声明   2. 遗漏的声明   3. 作为属性的变量 二、变量使用的注意事项   1. 变量作用域   2. 函数作用域和声明提前   3. 作用域链 一、变量声明的注意事项 1. 重复的...
     
    
    一、变量声明的注意事项
      1. 重复的声明
      2. 遗漏的声明
      3. 作为属性的变量
    二、变量使用的注意事项
      1. 变量作用域
      2. 函数作用域和声明提前
      3. 作用域链



    一、变量声明的注意事项

    1. 重复的声明

      使用 var 重复声明变量是合法且无害的。如果重复声明带有初始化器(即声明且赋初始值),那么这就和一条简单的赋值语句没有不同。

                var a = 12;
                console.log(a); // 12
                var a;
                console.log(a);// 12
                var a = 'abc';
                console.log(a);//abc
    
    2. 遗漏的声明

      在 JavaScript 中的变量必须先声明后使用(使用而不是赋值),没有声明过的变量不能直接使用,否则 JavaScript 会产生 Uncaught ReferenceError 异常。

      虽然 JavaScript 不能读取未声明的变量,但是,在非严格模式下,JavaScript 却可以给一个未声明的变量赋值,此时 JavaScript 实际上会给全局对象创建一个同名属性,并且它工作起来像(但并不完全一样)一个正确声明的全局变量。这意味着我们可以侥幸不声明全局变量,但这是一个不好的习惯并会造成很多 bug ,因此,应该始终使用 var 来声明变量。而在 ECMAScript 5 严格模式下,给一个没有声明的变量赋值也会报错。

    3. 作为属性的变量

      如上文所述,当声明一个 JavaScript 全局变量时,实际上是定义了全局对象的正常的可配置属性,并且可以删除它们。当然,使用 var 声明变量创建的属性依然是不可配置的,即无法通过 delete 运算符删除。

                var  truevar  = 1;
                fakevar = 2;
                this.fakevar2 = 3;
                console.log(delete truevar);    //false
                console.log(delete fakevar);    //true
                console.log(delete this.fakevar2); //true
    

      JavaScript 全局变量是全局对象的属性,这是在 ECMAScript 规范中强制规定的。对于局部变量则没有如此规定,但我们完全可以把局部变量当做跟函数调用相关的某个对象的属性。ECMAScript 3 规范称该对象为 "调用对象"(call object),ECMAScript 5 规范称为 "声明上下文对象"(declarative environment record)。

      JavaScript 可以允许使用 this 关键字来引用全局对象,却没有方法可以引用局部变量中存放的对象。这种存放局部变量的对象的特有性质,是一种对我们不可见的内部实现。然而,这些局部变量对象存在的观念是非常重要的。(详情见下文)



    二、变量使用的注意事项

    1. 变量作用域

      一个变量的作用域(scope)是程序源代码中定义这个变量的区域。全局(global)变量拥有全局作用域,在 JavaScript 代码中的任何地方都是有定义的;而如函数内声明的变量、函数参数等局部(local)变量拥有局部作用域,只在函数体内有定义。在函数体内,局部变量的优先级高于同名的全局变量。

      对于嵌套函数的变量,其作用域是变量声明所在的函数体内以及其嵌套的函数体内,但不能在父级或父级以上的函数体内起作用。

    2. 函数作用域和声明提前

      在一些类似 C 语言的编程语言中,花括号内的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的,我们称之为块级作用域(block scope)。而在 JavaScript 中没有块级作用域,取而代之的是函数作用域(function scope):变量在声明它们的函数体内以及这个函数体嵌套的任意函数体内都是有定义的。这就是说,在函数内声明的所有变量在函数体内始终是可见的,也就意味着变量可能在声明之前甚至已经可用——JavaScript 的这个特性被非正式地称为声明提前或声明提升(hoisting),即 JavaScript 函数里声明(并不涉及赋值)的所有变量都被 "提前(或提升)" 至函数体的顶部。

                var scope = "global";
                function f(){
                    console.log(scope);     //undefined
                    var scope = "local";    //local
                    console.log(scope);
                }
                f();
    

      上述函数执行过程等价于

    			function f(){
    				var scope;
    				console.log(scope);
    				scope = "local";
    				console.log(scope);
    			}
    

      因此,在具有函数作用域的 JavaScript 语言中,将变量声明放在函数体顶部,而不是靠近放在使用变量之处,可以使得源代码非常清晰地反映真实的变量作用域。

    3. 作用域链

      如果将一个局部变量看做是自定义实现的对象的属性的话,那么可以换个角度来解读变量作用域。

      每一段 JavaScript 代码(全局代码或函数)都有一个与之关联的作用域链(scope chain),这个作用域链是一个对象列表或者链表,这组对象定义了这段代码 "作用域中" 的变量。当 JavaScript 需要查找变量 x 的值的时候(这个过程叫做 "变量解析"(variable resolution)),它会从链中的第一个对象开始查找,如果这个对象有一个名为 x 的属性,则会直接使用这个属性的值;如果第一个对象中不存在名为 x 的属性,JavaScript 会继续查找链上的下一个对象,以此类推。如果作用域链上没有任何一个对象含有属性 x ,那么就认为这段代码的作用域链上不存在 x,并最终抛出一个引用错误(ReferenceError)异常。

      在 JavaScript 的最顶层代码中(也就是不包含在任何函数定义内的代码),作用域链由一个全局对象组成;  而在不包含嵌套的函数体内,作用域链上有两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局对象;  在一个嵌套的函数体内,作用域链上至少有三个对象。

      理解对象链的创建规则是非常重要的。当定义一个函数时,它实际上保存一个作用域链。当调用这个函数时,它创建一个新的对象来存储它的局部变量,并将这个对象添加至保存的那个作用域链上,同时创建一个新的更长的表示函数调用作用域的 "链"。  对于嵌套函数来讲,事情变得更加有趣,每次调用外部函数时,内部函数又会重新定义一遍。因为每次调用外部函数的时候,作用域链都是不同的。内部函数在每次定义的时候都有微妙的差别——在每次调用外部函数时,内部函数的代码都是相同的,而且关联这段代码的作用域链也不相同。

    展开全文
  • 变量声明和变量提升

    千次阅读 2018-05-31 13:57:29
    变量起作用的范围就是变量的作用域。在JavaScript中唯一能产生作用域的东西是函数。 1)块级作用域:使用代码块限定的作用域。JavaScript中没有块级作用域 2)词法作用域:在代码写好的那一刻,变量的作用域就...

    JS代码的执行过程分为两个阶段:
    1.预解析过程:找到所有的变量和函数声明,提升到作用域的最前面
    2.执行过程

    1.作用域

    变量起作用的范围就是变量的作用域。在JavaScript中唯一能产生作用域的东西是函数。

    • 全局作用域
      1)在<script>标签中定义的变量——全局变量
      2)不使用var声明直接使用的变量——全局变量
    • 局部作用域
      1)只有在函数内部使用var定义的变量才是局部变量
      2)超出函数的作用范围后,局部变量被销毁
    • 块级作用域
      1)使用代码块限定的作用域。JavaScript中没有块级作用域
      2)iffor中使用var定义的变量都是全局变量

    使用规则:

    • 函数允许访问函数外的变量
    • 整个代码结构中只有函数可以限定作用域
    • 作用域规则首先使用提升规则分析
    • 如果当前作用域中有了该变量,就不再考虑外面的同名变量
    2.关于变量声明
    • 设置值的时候,也是访问变量;获取值的时候也是访问变量
    • 并不是在函数内部写了变量,这个变量就属于这个函数的作用域;而是必须使用var来声明变量,这个变量才会属于这个作用域
    • 函数在声明的时候里面的代码不会执行,只有在调用的时候,代码才会执行
    • 声明函数时的函数名,其实也是一个变量名,可以通过这个变量名来给其赋值
    3.用var、let、const 声明变量时的区别

    1)用 var 声明的变量作用域是它当前的执行上下文,它可以是嵌套的函数,也可以是声明在任何函数外的变量。而 let 和 const 是块级作用域,意味着他们只能在最近的一组花括号(function、if-else 代码块或 for 循环)中访问。

    if(true){
    	var bar = "bar";
    	let baz = "baz";
    	const qux = "qux";
    }
    console.log(bar);//bar
    console.log(baz);//baz is not defined
    console.log(qux);//qux is not defined
    
    

    2)var 会使变量提升,这意味着变量可以在声明之前使用。而 let 和 const 不会使变量提升,提前使用会报错。

    console.log(foo);//undefined 
    //这里注意var声明的变量可以提升但是赋值不可以,所以是undefined
    var foo = "foo";
    
    console.log(baz);//ReferenceError
    let baz = "baz";
    
    console.log(bar);//ReferenceError
    const bar = "bar";
    

    3)用 var 重复声明变量,不会报错,但是 let 和 const 这样做会报错。

    var foo = "foo";
    var foo = "bar";
    console.log(foo); //bar
    let baz = "baz";
    let baz = "qux";//SyntaxError
    

    4)let 和 const 的区别在于:let 允许多次赋值,而 const 只允许一次。

    //不会报错
    let foo = "foo";
    foo = "bar";
    //会报错
    const baz = "baz";
    baz = "qux";
    
    4.变量提升和函数提升

    在分析代码的时候,首先应将以var声明变量名和以function开头的函数进行提升,再去执行代码的具体执行过程。
    1.变量的提升是分作用域的

    function foo(){
    	var num = 123;
    	console.log(num);
    }
    foo();//123
    console.log(num);//num is not defined
    

    这里需要注意的是:
    undefined 是定义了没有赋值
    is not defined 是没有定义

    2.函数表达式中函数的声明不会被提升,但是变量会被提升

    var scope = "global";
    foo();
    function foo(){
    	console.log(scope);//undefined
    	var scope = "local";
    	console.log(scope);//local
    }
    
    //提升后的代码为
    var scope;
    function foo(){
    	var scope;
    	console.log(scope);//undefoned
    	scope = "loacl";
    	console.log(scope);//local
    }
    foo();
    scope = "global";
    
    //函数表达式
    var scope = "global";
    foo();    //foo() is not a function
    var foo = function(){
    	console.log(scope);
    	var scope = "local";
    	console.log(scope);
    }
    

    3.函数同名:预处理的时候,会将两个函数全部提升,但是后面的函数会将前面的函数覆盖

    var m = 1;
    function add(n){
    	return n = n + 1;
    }
    y = add(m);
    function add(n){
    	return n = n + 3;
    }
    z = add(m);
    console.log(y);//4
    console.log(z);//4
    

    function add() { } 定义的函数会优先解析,而不是顺序解析。因此整个过程中,首先解析两个 add 函数,但是由于两个函数同名,所以后者会覆盖前者;然后顺序解析其余的代码。y = add(m);z = add(m); 语句调用的都是第二个 add 函数,所以返回的值都是4。

    4.变量与函数同名:在提升的时候,如果有变量和函数同名,则会忽略掉变量,只提升函数

    console.log(foo); //foo(){ }
    function foo(){}
    var foo = 2;
    console.log(foo); //2
    
    //提升后的代码
    function foo(){};
    alert(foo);
    foo=2;
    alert(foo);
    
    

    5.变量提升只会将变量和函数提升到当前作用域的最上方,而赋值不会提升

    //例1
    if(a in window){
    	var a = 10;
    }
    console.log(a);//10
    
    //提升之后的代码
    var a;
    if(a in window){
    	a = 10;
    }
    cosnole.log(a);//10
    
    //例2
    function f1(){
    	if(a in window){
    		var a = 10;
    	}
    	console.log(a);
    }
    f1();  //undefined  (错误,更正如下:
    f1()//10
    
    //提升后的代码
    function f1(){
    	var a;
    	if(a in window){
    		a = 10;
    	}
    	console.log(a);
    }
    f1(); //undefined   (错误,更正如下:
    f1(); //10
    
    
    
    展开全文
  • 1)变量声明: var foo = function () { .... } 2)函数声明: function foo(){ ..... } 函数参数:都是按值传递(把函数外部的值复制给函数内部的参数) 而变量复制则有两种方式——按地址传递还是按值...

    一、函数

    声明方式:

    1)变量声明:

    var foo = function () {
          ....
    }

    2)函数声明:

    function foo(){
       .....
    }

    函数参数:都是按值传递(把函数外部的值复制给函数内部的参数)

    而变量复制则有两种方式——按地址传递还是按值传递,这个就要涉及到变量的基础类型和应用类型的区别,详情见下文

    函数体中要用到的中间变量尽量在函数体里面申明为局部变量,如果直接引用全局变量,不仅增加了程序的耦合性,而且在页面关闭之前是不会释放内存,也会造成内存的浪费。如下面的例子:

    var a = 2;
    var b = 3;
    dosome();
    function dosome(){
        var temp = a;
        a = b;
        b = temp;
    }
    foo(a,b);
    function foo(num1,num2){   //最佳方案
        var temp = num1;
        num1 = num2;
        num2 = temp;
    }

    二、变量

    声明方式:

    1)var:声明的变量会自动被添加到最接近的环境,存在变量提升(即变量可以再声明之前使用,值为undenfied),可以重复声明变量。初始化变量不使用任何关键字,会被默认添加到全局环境。

    2)let:局部变量,不存在变量提升(因为存在暂时死亡区),所声明的变量,只在let命令所在的代码块内有效。

      暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

      相同作用域不可以重复声明变量,如下面的例子:

    function func(arg) {
      let arg; // 报错
    }
    
    function func(arg) {
      {
        let arg; // 不报错
      }
    }

    3)const:局部常量,不存在变量提升(因为存在暂时死亡区),只读常量,一旦声明就必须初始化,而且值不可更改。

     const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

    const a = [];
    a.push('Hello'); // 可执行
    a.length = 0;    // 可执行
    a = ['Dave'];    // 报错

    所有变量都存在于一个执行环境(也称作为作用域)当中,这个执行环境决定了变量的生命周期。

    注意:声明是在函数第一行代码执行之前就已经完成,而赋值是在函数执行时期才开始赋值。所以,声明总是存在于赋值之前。

    三、执行环境及作用域

    【执行环境】

    执行环境(execution context):定义了变量和函数有权访问的其他数据,决定了它们的各自行为。分为全局执行环境和局部执行环境(函数执行环境)。每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中(代码是无法访问的)。

    全局执行环境是最外围的一个执行环境,在Web浏览器中,全局执行环境被认为是window对象。某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数也随之销毁(全局执行环境知道应用程序退出,如关闭网页或者浏览器时才会被销毁)

    每个函数都有自己的执行环境。当执行到一个函数的时候。函数的环境就会推入一个环境栈中,而在函数执行完后,栈会将其环境弹出,把控制权返回给之前的执行环境。

    当代码在一个环境中执行时,会创建变量对象的一个作用域链,作用域链保证了对执行环境中的所有变量和函数的有序访问,作用域链的前段永远是当前执行环境的变量对象,如果执行环境是函数,则将其活动对象作为变量对象。活动对象(activation object)——保存了所定义的变量和函数同时还有函数的arguments对象

    标识符解析就是按着作用域链一级一级的搜索标识符的过程。

     

    【作用域】

     函数的作用域,也就是函数的执行环境,所以函数作用域内肯定保存着函数内部声明的所有的变量。

         一个函数在执行时所用到的变量无外乎来源于下面三种:

    1. 函数的参数----来源于函数内部的作用域
    2. 在函数内部声明的变量(普通变量和函数变量)----也来源于函数内部作用域
    3. 来源于函数的外部作用域的变量。

    比如下面这样:

    var x = 1;
    function add(num) () {
      var y = 1; 
      return x + num + y;   //x来源于外部作用域,num来源于参数(参数也属于内部作用域),y来源于内部作用域。
    }

    那么一个函数的作用域到底是什么呢?

    在一个函数被调用的时候,函数的作用域才会存在(即JS是在运行的时候才绑定作用域的)。此时,在函数还没有开始执行的时候,开始创建函数的作用域:

      函数作用域的创建步骤:

    1. 函数形参的声明。
    2. 函数变量的声明。
    3. 普通变量的声明。  
    4. 函数内部的this指针赋值。

           ......函数内部代码开始执行!  

         所以,在这里也解释了,为什么说函数被调用时,声明提前,在创建函数作用域的时候就会先声明各种变量。

       关于变量的声明,这里有几点需要强调

       1. 函数形参在声明的时候已经指定其形参的值。  

    function add(num) {
      var num;
      console.log(num);   //1
    }
    add(1);

    2. 在第二步函数变量的生命中,函数变量会覆盖以前声明过的同名声明。

    function add(num1, fun1) {
      function fun1() {
        var x = 2;
      } 
      console.log(fun1.toString()) //functon fun1(){ var x=2;}
    }
    add(100, function () {
      var x = 1
    }); 
    
    // 如果是使用函数表达式的方式
    function add1 (num1, fun2) {
        var fun2 = function () {
            var x = 3;
        }
        console.log(fun2.toString()); // functon (){ var x=3;}
    }
    add1(100, function () {
      var x = 1
    });

    3.  在第三步中,普通变量的声明,不会覆盖以前的同名参数

    function add(fun,num) {
      var fun,num;
      console.log(fun) //function(){}
      console.log(num);      //1
    }
    add(function(){},1);

    在所有的声明结束后,函数才开始执行代码!!! 

    在ES5的时候之前只有全局作用域和函数作用域,也就是for,if,while,等语句是不会创建作用域的。这样存在许多问题,所以在ES6里新增了块级作用域。

    // 全局作用域
    var a = 123;
    function foo (){
      // 函数作用域
       var b = 456;
    }

    全局作用域:从开始执行代码开始

    函数作用域:声明一个函数后,函数内的作用域即函数作用域

    块级作用域:相当于是以代码块来划分,作用域可以任意嵌套,外层作用域无法读取内层作用域的变量

    ES6 的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。函数声明类似于var,即会提升到全局作用域或函数作用域的头部。同时,函数声明还会提升到所在的块级作用域的头部。

    if (true) {
      function f() {}
    }
    // 报错
    if (true)
      function f() {}

    【注意】作用域的例子

    var a = 30;
    var c = function () {
        console.log(a)
    }
    var test = function () {
        var a = 40;
        c();
        return 0;
    }
    // 执行 test(), 最后输出的结果是30
    // c函数的环境变量是全局对象,c()是直接调用,和test函数没关系,所以本函数内没有变量a就会查询到全局对象上的a啦
    
    // 区别下面的写法
    var a = 30;
    var test1 = function () {
        var a = 40;
        function c () {
            console.log(a); 
        }
        c();
        return 0;
    }
    // 执行 test1(), 最后输出的结果是40
    var test1 = function () {
        a = 5;    // 按道理初始化变量的时候,没有使用任何关键字,默认会别添加到全局变量
                  // 但是后面的var a = 10语句,声明了变量a,而且因为声明提前的缘故,执行a = 5的时候,
                  // 变量a已经声明了,且是函数test1内部的变量,而不是全局变量
        console.log(window.a); 
        var a = 10;
        console.log(a);
        return 0;
    }
    
    // 执行 test1() 最后输出的结果是undefined, 10
    var foo = {n:1};  // 一定要加分号,否则会报错的
    (function(foo){
        console.log(foo.n);  
        foo.n = 3;  
        console.log(foo.n);  
        var foo = {n:2};  // 在函数执行前,变量声明提升,默认值为undefined,但是不会覆盖形参数的值
        console.log(foo.n);  
    })(foo);
    console.log('d', foo.n);  
    
    
    // ### 解析代码如下 ### //
    var foo = {n:1};   //如果前面定义的是变量,后面写的是匿名函数或者闭包什么的,
                       //如果变量后不加分号的话(即使已经换行了),js会自动的认为你是匿名函数自调,
                       //会被认为是变量名而不是方法,从而报错.所以变量后面要加分号!
    (function(foo){
        var foo;  // 声明和形参同名的变量,不会覆盖形参传递的值,形参和实参都指向同一片地址空间
        console.log(foo.n);  // 输出结果:1
        foo.n = 3;    // 修改形参指向地址中,变量n的值为3
        console.log(foo.n);  // 输出结果:3
        foo = {n:2};   // 将形参指向新的地址空间
        console.log(foo.n);  //新地址空间里,变量n的值为2
                             // 输出结果:2
    })(foo);
    console.log('d', foo.n);  // 实参还是指向原来的地址空间,但变量n的值已经被修改为3
                              // 输出结果: 3

    作用域参考文章:https://www.cnblogs.com/renlong0602/p/4398883.html

    作用域变量查找:https://blog.csdn.net/DepressedPrince/article/details/88369006

    三、顶层对象

    window(浏览器):在ES5中,顶层对象和全局变量是一致的。而ES6中,顶层对象和全局变量将逐渐脱钩,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。

    var a = 1;
    // 如果在 Node 的 REPL 环境,可以写成 global.a
    // 或者采用通用方法,写成 this.a
    window.a // 1
    
    let b = 1;
    window.b // undefined

    global(Node):

    四、基础类型和应用类型

    基础类型:保存在栈内存中的简单数据。Undefined, Null, Boolean, Number, String,在内存中分别占有固定大小的空间,一般是按值访问,因为可以直接操作保存在变量中的实际的值。

    引用类型:保存在堆内存中由多个值构成的对象。Object, Function, Array以及自定义的对象等。实质是变量中保存的只是一个指针,这个指针指向内存中的另一个位置。该位置保存的对象由于大小不固定(比如数组可以动态增加),不能保存在栈内存中,但它们的内存地址是固定的,可以存储在栈内存中,而具体内容则存储在堆内存中。当我们访问引用类型的变量时,就首先从栈内存中读取地址,拿到地址后,在根据地址去堆地址找到数据。这种方式一般称为按“引用访问”,因为我们操作的不是实际值,而是被那个值所引用的对象。

    两者的本质区别:在内存中分配的大小是否固定,如果内存大小固定则分配在栈内存中,如果不固定则把指针地址放在栈内存,真实数据放在堆内存。

    堆内存和栈内存的示例图:

     

    五、复制变量值

           变量的复制就要考虑变量的类型,如果是基础类型,直接就是在栈内存里开辟一个新的空间,两者的变量完全独立,进行任何操作也不会相互影响。如果是引用类型,复制的其实是栈内存的指针,而这个指针指向堆内存中的唯一对象。所以复制操作结束后,两个变量实际引用同一个对象,因此改变其中一个变量,将会影响到另一个变量。

    六、垃圾收集

            将不再继续使用的变量释放器占用的内存。垃圾收集器会按照 固定的时间间隔周期性 地执行这一操作。局部变量很容易判断是否还需继续使用,但有的变量的判断却不是那么容易。垃圾收集器必须跟踪那个变量有用那个变量没用,对不再使用的变量打上标记,以备将来收回其占用的内存。标识无用变量的方法用实现而有所不同,通常有标记清除引用计数。

    标记清除:给当前不使用的值加上标记,然后在回收其内存;

    引用计数:跟踪记录所有值被引用的次数,但次数为0时会被回收,但在代码中存在循环引用现象时,会导致问题

    一旦数据不再使用,最好通过将其值设置为null来释放其引用——解除引用,这个做法适用于大多数全局变量和全局对象的属性。局部变量会在它们离开执行环境时自动被解除引用

     

     

    展开全文
  • C语言中变量声明和变量定义的区别

    千次阅读 2019-04-24 10:11:49
    变量声明和变量定义 变量定义:用于为变量分配存储空间,还可为变量指定初始值。程序中,变量有且仅有一个定义。 变量声明:用于向程序表明变量的类型和名字。 定义也是声明,extern声明不是定义 定义...

    本文转载至CSDN博客JeanCheng

    变量声明和变量定义


    • 变量定义:用于为变量分配存储空间,还可为变量指定初始值。程序中,变量有且仅有一个定义。

    • 变量声明:用于向程序表明变量的类型和名字。

    • 定义也是声明,extern声明不是定义

    • 定义也是声明:当定义变量时我们声明了它的类型和名字。
    • extern声明不是定义:通过使用extern关键字声明变量名而不定义它。
      [注意]
      变量在使用前就要被定义或者声明。
      在一个程序中,变量只能定义一次,却可以声明多次。
      定义分配存储空间,而声明不会。
       C++程序通常由许多文件组成,为了让多个文件访问相同的变量,C++区分了声明和定义。
    
        变量的定义(definition)用于为变量分配存储空间,还可以为变量指定初始值。在程序中,变量有且仅有一个定义。
    
        声明(declaration)用于向程序表明变量的类型和名字。定义也是声明:当定义变量的时候我们声明了它的类型和名字。可以通过使用extern声明变量名而不定义它。不定义变量的声明包括对象名、对象类型和对象类型前的关键字extern。
    
        extern声明不是定义,也不分配存储空间。事实上它只是说明变量定义在程序的其他地方。程序中变量可以声明多次,但只能定义一次。
    
        只有当声明也是定义时,声明才可以有初始化式,因为只有定义才分配存储空间。初始化式必须要有存储空间来进行初始化。如果声明有初始化式,那么它可被当作是定义,即使声明标记为extern。
    
        任何在多文件中使用的变量都需要有与定义分离的声明。在这种情况下,一个文件含有变量的定义,使用该变量的其他文件则包含该变量的声明(而不是定义)。
    

    如何清晰的区分变量声明和定义


    extern通知编译器变量在其他地方被定义


    1.extern告诉编译器变量在其他地方定义了。

    例如:

    extern int i;       //声明,不是定义
    int i;              //声明,也是定义,未初始化
      
    • 1
    • 2

    带有初始化式的声明必定式定义


    2.如果声明有初始化式,就被当作定义,即使前面加了extern。
    只有当extern声明位于函数外部时,才可以被初始化。

    例如:

    extern double pi=3.141592654;  //定义
     
    • 1

    函数的声明和定义


    3.函数的声明和定义区别比较简单,带有{ }的就是定义,否则就是声明。

    例如:

    extern double max(double d1,double d2);  //声明
     
    • 1

    除非有extern关键字,否则都是变量的定义。


    4.除非有extern关键字,否则都是变量的定义。

    例如:

    extern int i; //声明
    int i; //定义          
     
    • 1
    • 2

    程序模块化设计风格


    概要


    1. 不要把变量定义放入.h文件,这样容易导致重复定义错误。
    
    永远不要在.h文件中定义变量。定义变量和声明变量的区别在于定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量
    
    2. 尽量使用static关键字把变量定义限制于该源文件作用域,除非变量被设计成全局的。
    
    3. 可以在头文件中声明一个变量,在用的时候包含这个头文件就声明了这个变量。
    
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    模块化要点


     (1) 模块即是一个.c文件和一个.h文件的结合,头文件(.h)中是对于该模块接口的声明;
    
    (2) 某模块提供给其它模块调用的外部函数及数据需在.h中文件中冠以extern关键字声明;
    
    (3) 模块内的函数和全局变量需在.c文件开头冠以static关键字声明;
    
    (4) 永远不要在.h文件中定义变量!定义变量和声明变量的区别在于定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量。
    
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    一般情况下头文件中只放变量的声明,因为头文件要被其他文件包含(即#include),如果把定义放到头文件的话,就不能避免多次定义变量,C++不允许多次定义变量,一个程序中对指定变量的定义只有一次,声明可以无数次。

    不过有三个例外,一下三中实体的定义也可放到头文件中。

    1.值在编译时就已知的const 变量的定义可以放到头文件中
    如:const int num(10);
    2.类的定义可以放到头文件中
    3.inline 函数
    
     
    • 1
    • 2
    • 3
    • 4

    这三个实体可以定义在多个源文件中,只要在每个源文件中的定义相同。

    示例程序


    #include <stdio.h>
    #include <stdlib.h>
    
    //  是定义,定义了A为整型的外部变量
    //  C中定义的变量默认就是extern的,
    //  因此一般来说int a = 10 <==> extern int a = 10;
    /*extern */int a = 10;
    //如果声明有初始化式,就被当作定义,即使前面加了extern。
    //只有当extern声明位于函数外部时,才可以被初始化。
    
    int main(void)
    {
        extern int  a;           //  声明一个外部extern的int型变量a
        //  这是个声明而不是定义,声明A是一个已经定义了的外部变量
        //  注意:声明外部变量时可以把变量类型去掉如:extern a;
        printf("a = %d\n", a);
        return EXIT_SUCCESS;
    }
    

    这里写图片描述

    在这个程序中,我们再函数外部定义了一个变量
    注extern int a = 10;只有当extern声明位于函数外部时,才可以被初始化。
    我们后面还会提到这个问题

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
        int a;                  //  定义一个变量, 不初始化
        int b = 10;             //  定义一个变量, 同时进行初始化
        extern int  c;          //  声明一个外部extern的int型变量a
    
        printf("a = %d\n", a);
        printf("b = %d\n", b);
        printf("c = %d\n", c);
    
        extern int d = 10;
    
    
        return EXIT_SUCCESS;
    }
    

    在这个程序中,

    int a;是个定义,但是未初始化,打印他的值式不确定的,因此编译时会报未初始化的异常。

    int b = 10; 是个定义,并且被正确初始化,打印b的值没有问题。

    这里写图片描述

    但是是个声明,如果要对c进行读写操作,而我们并没有对c进行定义,因此语法检查没有问题,但是在链接时,连接器会找不到c的地址。

    这里写图片描述

    对于d再明显不过了,前面我们提到过如果声明有初始化式,就被当作定义,即使前面加了extern。但是只有当extern声明位于函数外部时,才可以被初始化。

    现在这个定义很明显被gcc编译器认为是错误的。

    这里写图片描述

    展开全文
  • TypeScript 变量声明

    千次阅读 2017-06-21 16:17:54
    一、变量声明  let和const是JavaScript里相对比较新的变量声明方式。推荐使用let 方式声明变量,来代替使用var。 TypeScript是JavaScript 的超集,所以它本身就支持let、const,同时还有var。 let 方式声明解决...
  • ES6系列之变量声明

    万次阅读 2020-07-01 09:40:30
    在之前的js版本中,声明变量一直都是用var, 这个估计大家都很熟悉了。 那么它的作用呢就是用来声明一个变量,比如像这样子: var a = 10 在这里我们就用var声明了一个变量a 并且在声明的同时给这个变量a赋值了 也...
  • js 函数声明提升和变量声明提升

    千次阅读 2018-08-06 18:16:42
    1. 函数声明提升 func() function func () { } 上例不会报错,正是因为 ‘函数声明提升’,即将函数声明提升(整体)到...2. 变量声明提升(只有var声明的变量才有变量提升,let、const无;变量赋值无提升) ...
  • 变量声明系列之ES5(变量提升)

    千次阅读 2018-02-25 23:08:33
    变量声明恐怕是我们日常开发中最最经常遇到的了,那今天我们就来总结下现在js一共有哪几种变量声明的方式以及各个声明方式的特点。这个变量声明系列分两篇文章,一篇写ES5(var,function)的变量声明方式及其特点,下...
  • go变量声明的几种方式

    千次阅读 2019-07-24 00:08:35
    (1)指定变量类型,如果没有初始化,则变量默认为零值。 var v_name v_type v_name = value 布尔类型为false 字符串为""(空字符串) 以下几种类型为nil: var a *int var a []int var a map[string] in...
  • Python的类变量和对象变量声明解析

    千次阅读 2018-02-14 23:43:57
    只要是声明在类的语句块中,且没有”self.”前缀的变量都是类变量,且类变量是被所有对象共享的。  注意加粗部分,如果声明在类的方法的语句块中,那么就是局部变量了!比如下面这个例子: 复制代码 ...
  • Oracle数据库(一):变量声明

    千次阅读 2019-07-06 14:54:03
    变量声明的基本语法 锚定声明 自定义子类型 变量声明的基本语法 name [CONSTANT] datatype [NOT NULL] [:= | DEFAULT default_assignment] name为要声明的变量或常量的名字; CONSTANT声明的是一个常量,如果...
  • 1、成员变量(全局变量)是可以不经初始化的,在类加载过程的准备阶段即可给它赋予默认值,但局部变量使用前需要显示赋予初始值,javac不是推断不出不可以这样做,而是没有这么做,对于成员变量而言,其赋值和取值...
  • C++中变量声明和定义

    千次阅读 2019-02-25 13:55:13
    2、同一个变量声明可以有多处,但定义只能有一处 extern int i; //声明i而非定义i int j;//声明并定义j extern关键字就是告诉编译器,这个变量i定义在其他文件中 3、任何显式初始化的声明也会成为定义 extern int i...
  • 深入理解变量声明提升和函数声明提升

    万次阅读 多人点赞 2016-03-05 20:51:02
    变量声明提升 1、变量定义 可以使用var定义变量,变量如果没有赋值,那变量的初始值为undefined。 2、变量作用域 变量作用域指变量起作用的范围。变量分为全局变量和局部变量。全局变量在全局都拥有定义;而...
  • 好久没有写这玩意儿了;今天在工作中遇到了这个问题,就拿出来分享下,希望对需要的朋友有所帮助,若...2、变量声明提前  就是当我们声明一个变量的时候,我们可以在其前方访问到它:这里就要提到一个函数作用域的...
  • 在typescript中,变量使用前必需先声明 (1) 声明变量的类以及初始值 var [变量名]:[类型]=值 例如: var uname:string='Runoob' (2)声明变量的类型及但没有初识值,变量会被设置为undefined var [变量名]:[类型...
  • Python(2)变量声明、变量类型

    万次阅读 2017-10-08 18:13:39
    1、变量声明声明变量的时候,直接使用例如a = 1这样的方式即可。既不用像js语言那样使用一个通用的var或者let之类方式表示声明,也不需要像c++语言那样使用更严格的int或char方式进行声明。注意,变量声明的时候必须...
  • 关于数组能否用变量声明长度

    千次阅读 2020-04-13 21:53:51
    第一行照例本鸽子精咕咕咕。...不是不允许变量声明数组长度吗(这个人没听课+C语言被强调多次记忆太深)。 先说结论:Java是可以变量声明数组长度的。 C语言: 我们学习C语言的时候,都会被强调不能定义一个...
  • 上几张图:第一个:声明了变量a,当预解析时,它的值为undefined,再声明了函数a,然后输出一下,发现a 输出为...变量声明优先级高于函数声明?再看这个,换了个顺序,还是一样的,说明如果a变量赋值了话就会覆盖...
  • static 以及变量声明和定义

    千次阅读 2019-02-12 19:34:43
    #include &lt;iostream&gt; #include&lt;string&gt; #include&lt;cstring&gt; using namespace std;...//只声明未定义 执行默认初值 ctr=0; //赋值 return ++ctr; } i...
  • C++ 类静态成员变量声明和定义

    千次阅读 2018-11-04 16:45:36
    这是在学习C++类中的一个简单总结 静态成员变量声明和定义的
  • oracle-变量声明的三种方式

    万次阅读 2019-03-10 12:42:24
    在sqlplus 环境中,声明变量的关键字:define variable declare 一、define关键字(host变量) host变量的作用是一个替换作用,是主机环境与oracle进行交互的变量,定义host变量时必须同时指定变量名和变量的值,...
  • Dim a As Integer -说明a为整型变量 Dim b As String -说明b为可变长字符型变量 Dim c As String~~1 0 ’说明c为长度是10个字符的固定长字符型变量 Dim d(10)As Integer -说明d为一维整型数组 Dim e()As Single ...
  • 变量声明和定义的区别

    千次阅读 2017-10-24 16:55:35
    我们在程序设计中,时时刻刻都用到变量的定义和变量声明,可有些时候我们对这个概念不是很清楚,知道它是怎么用,但却不知是怎么一会事,下面我就简单的把他们的区别介绍如下:(望我的指点对你受益) 变量声明有...
  • 变量定义和变量声明的区别

    千次阅读 2016-01-19 13:34:05
    变量声明 变量定义 区别
  • JS中变量声明分显式声明和隐式声明。 var name = 'muzidigbig';//显示声明 name = 'muzidigbig';//隐式声明(为全局变量的一个属性) 在函数中使用var关键字进行显式声明的变量是做为局部变量,在全局范围内声明...
  • 然后根据具体的例子比较变量声明提升和函数声明提升的不同。 第一部分:函数的声明方式 函数声明有三种方式:函数声明,函数表达式(又称函数字面量声明),函数对象的声明(使用率很低)  方式一:函数声明 ...
  • JavaScript 变量声明和初始化

    千次阅读 2018-07-21 11:27:25
    JavaScript 变量声明的方式 怎么说呢?变量声明或许是写JavaScript中做的第一件事情,无论有意无意总是会产生一些变量. 或许你觉得这个没有什么价值,声明变量吧,谁不会呀 看看声明变量有哪些方式 直接使用,就成了...
  • Python学习笔记——全局变量声明

    万次阅读 多人点赞 2018-06-06 16:43:33
    最近在编写python程序时发现,在函数中可以对函数外面的全局变量进行读取、打印等操作,但是不能赋值,否则会报错:UnboundLocalError: local variable 'a' referenced before assignment示例代码如下:a = 0 ...
  • JAVA 全局变量 声明与定义

    千次阅读 2019-05-10 17:20:31
    一、成员变量不能在类体中先声明(定义)后赋值,但静态变量可以先在类体中声明,然后在方法中赋值(当然实例变量是不行的); 1)如以下程序会出问题: public class Test { static int a; //在类体中声明整型...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,602,805
精华内容 641,122
关键字:

变量的声明

友情链接: lcm_t.rar