JavaScript 变量作用域和声明提升

本博文参考
文章
《javascript设计模式和开发实践》

变量作用域相信大家能理解,但是,命名提升?What is this?

虽然有javascript开发经验,但是有些概念还是需要不断复习,特别是不常见的。

一、变量作用域
说到这个概念,不有自主的想到this,scope 这两个关键字。

JavaScript的this总是指向一个明确的对象,这个对象是在执行的时候动态绑定的。通俗的说就是谁调用我,我的this就是谁。
除去不常用的with和eval,具体的实际应用中,this的指向分为以下四种情况:

    ·作为对象的调用;
    ·作为普通函数的调用;
    ·构造器的调用;
    ·Function.propotype.call 和 Functio.propotype.apply 调用;

下面我们分贝进行介绍。

1、作为对象的调用
当函数作为对象的方法被调用时,this指向这个对象:

var obj={
    a:1,
    getA:function(){
        alert(this===obj);
        alert(this.a);
    }
};

obj.getA();//执行结果:true、 1

在getA函数中,this指向obj对象。

2、作为普通函数的调用
当函数不作为对象的属性被调用时,也就是我们常说的普通函数调用方式,此时的this总是指向全局对象,在浏览器中,javascripta的全局对象是window对象。

window.name='globalname';
var getName=function(){
    return this.name;
}
alert(getName());//'globalname'
//或者

window.name='globalname';
var obj={
    name:'scopename',
    getName:function(){
        return this.name;
    }
};
var myObjName=obj.getName;
alert(myObjName());//'globalname'

严格模式下,函数内部的this不会指向window,而是underfind。

var funF=function(){
    alert(this);
};
var funStrictF=function(){
    'use strict'
    alert(this);
};
funF();//window
funStrictF();//underfind

3、构造器的使用
javascript没有类,但是可以从构造器中创建对象,同事也提供了new运算符的,是的构造器更像一个类。
除了宿主提供的一些内置函数,发部分javascript函数都可以单做构造器使用。构造器的外表和普通函数一抹一样,他们的区别在于调用的方式。当用new运算符调用一个函数时,改函数总会返回一个对象,通常情况下,构造器里的this就指向了这个对象,案例如下:

var createClass=function(){
    this.name='sven';
    return {
        name:'anne'
    };
};
var obj=new createClass();
alert(obj.name);//输出 anne

4、Function.propotype.call 和 Function.propotype.apply 的使用
跟普通的函数调用相比,用Function.propotype.call 和 Function.propotype.apply 可以动态的改变传入函数的this。

var obj1={
    name:'sven',
    getName:function(){
        return this.name;
    }
};
var obj2 ={
    name:'anne'
};
alert(obj1.getName());//输出 sven
alert(obj2.name);//输出 anne
alert(obj1.getName.call(obj2));//输出 anne

javascript中,变量的作用域不是块级的,而是以function为单位。所谓块级,就是{}花括号括起来为一块,以function为单位,就是指变量的作用域上限就是当前所在的函数。看个例子:

var a=100;
var fun=function(){
    var a=6;
    alert(a);
};
alert(a);//100
fun();//6

for(var i=0;i<5;i++){
    var a =i;
}
alert(a);//4

虽然在for{} 中重新声明的a变量,实际上,a的作用域仍是全局环境,所以也只是改变了最初a的值。而在fun(){}中,作用域变成了fun函数内部,所以输出 5

二、接下来就是本篇的重点:变量声明提升
当我们的声明在同作用域靠后的位置,变量的声明会被自动提升到作用域的开头。
当我们如下声明一个变量

var a=100;

其实做了三件事:声明变量、执行变量的数据类型、赋值
如果是下面的代码呢?

alert(a);
var a=100;

实际上等同于

var a;
alert(a);//underfind
a=100;

来个复杂的例子

var a = 100;
alert(a);//100
var a = 200;
alert(a);//100
function fun2() {
   alert(a);//underfind;
   var a = 3;
}
fun2();
alert(typeof a);//unmber

var a = function() {}
alert(typeof a);//function

上述例子中,对a进行了多次声明,我们要区分个a的作用域,将声明做一次提升。其实等同于

var a;
var a ;
var a ;//多次声明会合并为一个对象

a=100;
alert(a);
a=200;
alert(a);

function fun2() {
    var a;
    alert(a);//underfind;
    a = 3;//确定变量的数据类型,赋值
}
fun2();
alert(typeof a);//unmber

a=function(){};
alert(typeof a);//function

变量提升还有隐式提升?
看下面的例子

function foo;
var foo;
alert(typeof foo);
foo = function(){}

输出function?这是为什么?再看看下面的例子

alert(fun1);//fucntin
alert(fun2);//underfind

function fun1(){}
var fun2=function(){}

没有的对比就没有伤害,上述例子中应该就能知道一点答案。
普通的通过var声明一个变量,只做声明,没有定义变量的数据类型。而通过function声明一个变量,在声明的同时会定义变量的数据类型为funtion。上面我们有说过,在同级作用域内,相同名称的变量会合并为一个变量,也就是后者会覆盖前者,这里也是一样的。
前者定义一个foo变量,数据类型为function;
后者有顶一个foo变量,覆盖前者,然而没有定义变量类型。所以foo的数据类型仍然是function。这个道理和下面的例子是一样的

var a=100;
var a;
alert(a);//输出100

这也是我们在写javascript的时候,变量要写在被使用的前面,而函数可以写在后面的解释。

变量的声明也有优先级?
优先级如下:

  1. 语言内置:作用域内的this和arguments关键字
  2. 形式参数:函数的参数在作用域内总是有效的
  3. 函数声明:例如function(){}
  4. 变量声明:例如 var a

举个例子:

function fun(a){
  var b = 100; 
  function go() {}
}

声明的顺序是:fun->this,arguments->go->a
声明归声明,该覆盖的还是会覆盖,不举例子了。

–完–

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值