一、变量的类型

Javascript和Java、C这些语言不同,它是一种无类型、弱检测的语言。它对变量的定义并不需要声明变量类型,我们只要通过赋值的形式,可以将各种类型的数据赋值给同一个变量。例如:

1
2
3
4
i=100;//Number类型
i="variable";//String类型
i={x:4};//Object类型
i=[1,2,3];//Array类型

二、变量的声明

JS中变量申明分显式申明和隐式申明。

1
2
var i=100;//显式申明
i=100;//隐式申明

在函数中使用var关键字进行显式申明的变量是做为局部变量,而没有用var关键字,使用直接赋值方式声明的是全局变量。  

当我们使用访问一个没有声明的变量时,JS会报错。而当我们给一个没有声明的变量赋值时,JS不会报错,相反它会认为我们是要隐式申明一个全局变量,这一点一定要注意。

三、变量是如何被覆盖的

在一般情况下,js代码都是自上而下执行的,对于同一个变量,我们可以通过如下方式来修改:

1
2
3
4
5
var a = 1;
a = 2;
console.log(a) // 2
a = function(){};
console.log(a) // function(){};

四、变量提升

  • JavaScript中,函数声明和变量会被提升
  • 变量提升是JavaScript将声明移至作用域 scope (全局域或者当前函数作用域) 顶部的行为。
  • 这意味着你可以在声明一个函数或变量之前引用它,或者可以说:一个变量或函数可以在它被引用之后声明。

变量:

1
2
3
4
5
6
7
foo = 2
var foo;

// 被隐式地解释为:

var foo;
foo = 2;

函数:

1
2
3
4
5
hoisted(); //"foo"

function hoisted() {
console.log("foo");
}
1
2
3
4
console.log(a);
var a = 1;
console.log(b);
var b = function(){};

这个时候console.log()都会输出undefined而不会报错,这里就是变量提升起到的作用。我们在用var或者函数声明的方式定义一个变量时,这个变量的定义会提升到方法体的最顶端。

所有上面代码相当于:

1
2
3
4
5
6
var a
var b
console.log(a);
console.log(b);
a = 1;
b = function(){};

如果代码变为:

1
2
3
4
5
6
console.log(a);
var a = 1;
console.log(b);
var b = function(){};
console.log(a);
console.log(b);

那麽相当于:

1
2
3
4
5
6
7
8
var a
var b
console.log(a); //undefined
console.log(b); //undefined
a = 1;
b = function(){};
console.log(a); //1
console.log(b); //函数f

五、变量提升的优先级

1
2
3
4
5
var a = 1;
function a(){
console.log(a)
}
console.log(a) //1

这里隐藏着一个概念:函数声明提升的优先级高于变量声明的提升。浏览器底层的实现过程是这样的:当js解析器在遇到函数声明时,会优先将其提升到定义体顶部,其次再是var声明的变量,这样就导致函数a被变量a给覆盖的情况,所以最终将打印1。

所以代码相当于:

1
2
3
var = function a(){ console.log(a) }
a = 1
console.log(a) //1

例子1:

1
2
3
4
5
6
7
var getName = function() {
console.log(1)
}
function getName() {
console.log(2)
}
getName()

首先按照个人理解,代码相当于:

1
2
3
4
5
6
7
function getName() {
console.log(2)
}
getName = function() {
console.log(1)
}
getName() //1

这就是因为函数提升 优于变量

解析器在向执行环境中加载数据时会率先读取函数声明,并使其在执行任何代码之前可用(可访问),即函数声明提升

例子2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 
* 函数声明优于变量声明
* 函数表达式会将变量提升,但是代码在执行的时候才会被赋值
*/

test();
console.log(test);
function test() {
console.log('我是函数');
}
console.log(test);
var test = '我是变量';
console.log(test);
var test = function (params) {
console.log('我是函数表达式');
}
console.log(test);
test();

按照个人理解 上面代码相当于:

1
2
3
4
5
6
7
8
9
10
function test() { console.log('我是函数') }
test(); //我是函数
console.log(test); //函数test f test() { console.log('我是函数') }
console.log(test); //函数test f test() { console.log('我是函数') }

test = '我是变量';
console.log(test); //我是变量
test = function (params) { console.log('我是函数表达式') }
console.log(test); //函数test f(params) { console.log('我是函数表达式') }
test(); //我是函数表达式

运行结果和我们分析的是一样的

例子3:

1
2
3
4
5
6
7
8
9
10
11
12
13
test();
console.log(test);
var test = '我是变量';
console.log(test);
var test = function (params) {
console.log('我是函数表达式');
}
console.log(test);
function test() {
console.log('我是函数');
}
console.log(test);
test();

按照理解,代码相当于:

1
2
3
4
5
6
7
8
9
function test() { console.log('我是函数') }
test(); //我是函数
console.log(test); //函数f test() { console.log('我是函数') }
test = '我是变量';
console.log(test); //我是变量
test = function (params) { console.log('我是函数表达式') }
console.log(test); //函数f (params) { console.log('我是函数表达式') }
console.log(test); //函数f (params) { console.log('我是函数表达式') }
test(); //我是函数表达式

运行结果没有毛病:

mark

注意:如果两个函数名一样,后面的会覆盖前面的(认为是在函数解析的时候覆盖,即提升的时候就覆盖了)

1
2
3
4
5
6
7
8
9
10
11
test()
console.log(test);
function test() {
console.log('我是函数');
}
console.log(test);
function test() {
console.log('我是函数表达式');
}
console.log(test);
test();

代码相当于:

1
2
3
4
5
6
7
function test() {console.log('我是函数');}
function test() {console.log('我是函数表达式');}
test() //我是函数表达式
console.log(test); //f test() {console.log('我是函数表达式');}
console.log(test); //f test() {console.log('我是函数表达式');}
console.log(test); //f test() {console.log('我是函数表达式');}
test(); //我是函数表达式