javascript学习系列(一)变量、作用域
1、变量是保存数据的容器;
2、变量的命名规则:
(1)包含$、_、字母、数字,其中竖子不能用作开头;
(2)区分大小写,不能是保留字和关键字。
3、变量的声明:
一般用var/let(ES6),如果没有var则默认为全局变量,且预解析时不能被解析,且容易造成变量污染。
4、数据类型:
分为基本类型和引用类型
(1)基本类型变量直接放在栈中,调用时访问栈内内容就行;而引用类型变量实际是存放在堆中,然后在栈中存放对应的地址,访问时通过调用栈中的引用名,其内容对应的就是堆中的地址。
(2)数据类型判断
typeof(a); //返回a的数据类型
eg:typeof(1)->number;
typeof('a')->string;
typeof(true)->boolean;
typeof(undefined)->undefined;
typeof(null)、typeof({})、typeof([])->Object;
typeof(function(){})->function;
但是对于null、[ ]、{}等类型怎只能判断为object,所以需要用instanceof来判断。
instanceof: object instanceof VarType;
eg:[] instanceof Array -> true;
5、作用域
(1)分为全局作用域和局部作用域(主要指函数作用域,因为js中无块级作用域),对应的变量为全局作用域和局部作用域。
(2)作用域链时用来查询变量的(从里层到外层查询,最外层为window)。
6、js解析机制
过程:
(1)预解析:找到var声明的变量并赋值为undefined;然后再找function后面的函数名(函数参数作为局部变量处理),直接提前声明函数;
(2)逐行解读代码:读取每一行代码并赋值(有关函数声明的部分都跳过,因为预解析时已经声明了。)
命名冲突:
(1)变量名和函数名冲突时预解析只保留函数名,剔除变量名(函数权重大);
(2)函数名与函数名冲突,后面的覆盖前面的;
预解析问题:
(1)问题1 (注:不通过var声明的变量都是全局变量)
A. console.log(a); var a = 1; ->undefined
B. console.log(a); a=1; ->a is not defined
(2)问题2
console.log(a);
var a=1;
console.log(a);
function a(){ console.log(2);}
console.log(a);
var a=3;
console.log(a);
function a(){console.log(4);}
console.log(a);
a();
预解析为:function a(){console.log(4);}
输出结果为:
a(); //a函数
1
1
3
3
a is not a function //a已经被变为一个变量
(3)问题3
A.
var a=1;
function fn(){ console.log(a); var a=2;}
fn();
console.log(a);
预解析为:
a:undefined;
fn{ a:undefined;}
输出结果为:
undefined; //先读取fn里面的局部变量a,但是a还未被赋值,所以输出结果为fn里面的预解析a
B.
var a=2;
function fn(){
console.log(a);
a=2; //全局变量
}
fn();
console.log(a);
预解析为:
a:undefined;
fn{ } //fn里面没变量a
输出结果为:
1; //先在fn里面找变量a,没找到就往外继续找a,所以输出的结果为1
2; //逐行解析后a被赋值为2
C.
var a=2;
function fn(a){
console.log(a);
a=2; //全局变量
}
fn();
console.log(a);
预解析为:
a:undefined;
fn{ a:undefined; } //fn里面参数a,预解析时等同于局部变量
输出结果为:
undefined; //先在fn里面找变量a,没找到就往外继续找a,所以输出的结果为1
1; //逐行解析后a被赋值为2
7、内存问题
(1)离开作用域的值将被标记为可收回,将在垃圾收集期间删除;
(2)标记清除是目前主流的垃圾收集算法(另一种引用计数法存在循环引用等缺陷被摒弃);
(3)标记清除就是给不用的值加标记,然后回收其内存;
(4)引用计数法可能因为循环引用的问题而得不到释放(解决办法就是在使用完后赋值为null);
(5)当变量不用的时候,可以手动解除它的引用(赋值为null)。