作用域
- 全局作用域
- 私有作用域
- es6块级作用域
全局作用域
当打开页面的时候,会提供一个供js代码执行的环境全局作用域,会提供一个最大的window对象
- 全局变量:在全局作用域中声明的变量
- 判断一个对象有没有某个属性:
'属性名' in 对象名==返回true就是包含,返回false就是不包含==
var obj={name:li};
console.log("name" in obj);//true
全局变量和window的关系
- 在全局作用域下声明的变量,相当于给window添加了一个属性,属性名是变量名,属性值是变量值
- 在window身上的方法,我们可以直接省去window,如
window.alert(1);
alert(1);
//两者相等
带var与不带var的区别
- 都是在window添加对象
- 但是带var的先声明,到赋值代码行才赋值,不带var的是到代码行才声明赋值
- 带var的用
delete window.属性名删不掉,不带var的可以删掉
console.log(a);//undefined
var a=12;
console.log(a);//12
console.log(b);//报错
b=13;
console.log(b);//如果不算报错不执行的话,13
"a" in window;//true
"b" in window;//true
delete window.a;//false删不掉
delete window.b;//true 删除了
//
function fn(x){
var n=2;
console.log(n);
m=9;
}
fn(1);//2
console.log(n);//报错
console.log(m);//9因为m不是var会向上查找,如果一直没有会给window
私有作用域
函数执行的时候形成的作用域就是私有的,保护里面的变量不受外界干扰
私有变量:在私有作用域中声明的变量就是私有变量,函数的形参也是私有变量形参是在进入函数的时候先var一个形参
function fn(x){
//形参x也是私有变量
var m=3;//私有变量m
console.log(m);
console.log(x);
}
fn(2);
作用域链
查找变量的时候,先看自己私有作用域内有没有,如果有就是自己私有的,如果没有,就想上一级作用域继续查找,如果上一级也没有,会继续向上查找,知道找到window为止,如果说window也没有,报错:变量名 is not defined(ES5)
function fn(b){
var x=2;
console.log(a);
}
fn(1);//报错
赋值的时候(没带var的时候),先看自己私有作用域内有没有,如果有就是自己私有的,如果没有,就想上一级作用域继续查找,如果上一级也没有,会继续向上查找,直到找到window为止,如果说window也没有,赋值给window
function fn(x){
var n=2;
m=9;
}
fn(1);//2
console.log(m);//9因为m不是var会向上查找,如果一直没有会给window
function fn(x){
x=3;
}
fn(2);
console.log(x);//x is not defined 因为先var了一个形参x,后面的成为赋值了,就是私有变量
块级作用域
es6中用大括号包起来的, 外面无法访问
{
let a=12;
console.log(a);//12
}
console.log(a);//undefined
堆栈内存
栈内存
- 供js执行的环境
- 存储基本数据类型的值
堆内存
- 存储引用数据类型的值
对象,存的是键值对;函数存的是字符串
变量提升
代码执行的时候,会首先形成一个供js执行的环境,接下来在代码自上而下执行之前会有一步操作"变量提升";会先把var的变量声明一下,把function函数声明加定义(赋值),不过此时的函数是存储在堆内存中的字符串,后面var的变量没到赋值代码行前输出都为undefined
console.log(a);//undefined
console.log(fn);//fn(x){y=2;console.log(x)}
var a =2;
function fn(x){y=2;console.log(x);};
console.log(a);//2
console.log(fn);//fn(x){y=2;console.log(x)}
- 函数执行时开辟一个
新的私有作用域(栈内存 ),会先进行形参赋值,然后进行变量提升
- 只对等号左边的进行变量提升
console.log(fn);//undefined
console.log(fn(1,2));//报错
var fn=function (n,m){
return n+m;
}
console.log(fn(3,4));
- 函数里面的return,
return下面的代码本身是不执行的,但是可以进行变量提升,return的代码不能进行变量提升
function fn(){
console.log(f1);
return function f1(){
}
function f2(){
console.log("f2");
}
}
fn();//报错
function fn(){
console.log(f2);
return function f1(){
}
function f2(){
console.log("f2");
}
}
fn();//f2(){console.log("f2")}
- 对于变量名重复的,不进行二次变量提升,但会进行赋值覆盖
console.log(a);//a(){console.log(1)};
function a(){
console.log(1)};
var a=2;
console.log(a);//2
两个并排的括号代表什么
function fn(x){
return function(y){
return x+y;
}
}
fn(1)(2);//3
//第一个括号是x的值,第二个括号内是y的值,且都执行
变量提升的特殊性
判断语句中,无论条件是否成立,都会进行变量提升
- var 还是之前理解的只声明不定义
- function 在
老版本中声明+定义在新版本中只声明不定义(前提条件-->在判断语句中)
console.log(a); //undefined
//条件不成立,走不进去,没有进行赋值
if(1==2){
var a=12;
}
console.log(a);//undefined
console.log(fn);// 在新版本浏览器中,判断条件中的function相当于只是声明(跟var一样),所以undefined
if(1==2){
function fn(){
console.log(1)
}
}
console.log(fn); // undefined 条件不成立,只声明了,没赋值,(新版本浏览器)
//函数在老版本还是函数,新版本就是undefined
console.log(fn());//报错,因为此时的fn没有赋值,不是函数,不能执行,所以报错
console.log(a);//undefined
if(a in window){
var a=100;
}
console.log(a);//100
自执行函数,在全局作用域中不进行变量提升,
是私有作用域,所以内部进行的变量提升是私有变量
f=function(){
return true;
};
g=function(){
return false;
};
~function(){
//![]=false []==false 0==0
if(g()&&[]==![]){ //报错g()is not defined
f=function(){return false;};
function g(){
return true;
}
}
}();
console.log(f());
console.log(g());
//新版本判断时报错,老版本false false
新版本图解
老版本图解
console.log(fn);//undefined
if(1==1){
console.log(fn);//fn(){console.log("ok")}
function fn(){
console.log("ok");
}
}
console.log(fn)//fn(){console.log("ok")}
特殊情况,在判断语句中,如果出现function,后面再对此变量进行改值,改变的是私有的
var a=0;
if(true){
console.log(a);//a(){}
a=1;
function a(){}
a=21;
console.log(a);//21
}
console.log(a);//1