js作用域

声明修饰符

let具有块级作用域

let声明的变量具有块级作用域,for循环流程控制部分(小括号内),循环体部分(大括号内)、以及函数体、大括号包裹的代码块、中使用let声明的变量仅在该块状作用域内可见.如果在全局环境中使用let声明则是全局变量.

在chrome控制台使用let声明变量虽然在当前控制台属于全局变量,但是并不会添加到window对象上

for循环流程控制部分(小括号内)使用let声明的变量循环体内也可见.并且每次循环都会声明一个新的循环变量,js引擎会'记住'上次循环变量的值,并为新声明的循环变量赋值.看下面的代码

let logfuns=[];
let addfuns=[];
for(let i=0;i<10;i++){
    logfuns[i]=function(){
        console.log(i);
    }
    addfuns[i]=function(){
        i++;
    }

}
logfuns[8]();//输出8
addfuns[8]();//此时i的值变为9
logfuns[8]();//输出9
console.log(i);//报错 ReferenceError: i is not defined

由于for中用let声明的变量i,那么每次循环时都会新声明一个同名变量i(同名但不同),由于logfuns和addfuns是全局变量,且对循环中声明的变量i有引用,所以循环结束后i并没有释放,但是由于作用域限制外部无法直接访问变量i,但是函数可以访问到(这里就是js的闭包,可以理解成java的private修饰的变量用get/set访问)

const具有块级作用域

const和let一样具有块级作用域,不过const修饰的变量无法修改变量的值,而且必须声明时同时赋值,无法先声明然后再赋值.

const j=10;
const i;//会报错
i=100;

var具有函数级作用域

var声明的变量具有函数级作用域,且有声明提前(let/const没有)

var在函数内的任意位置声明,声明前的代码依然可以访问该变量,就相当于把声明提到了函数的开头.在赋值前此值都是undefined.

在chrome控用var声明变量会添加到window对象上

无修饰符声明的变量为全局变量

无论在任何位置,如果不就修饰符声明一个变量,则该变量为全局变量.

闭包 

js闭包可以实现外部访问内部变量或函数.此时的内部声明的变量依然在内存中没有释放,但是外部无法直接访问,需要通过内部函数来间接访问.

注意大量使用闭包可能导致内存占用高

上面的for循环就是一个例子,接下来再来看下面的例子,思考一下输出结果

let name="张三";
let person={
    name:"李四",
    age:"18",
    sayHello:function(){
        console.log("hello "+this.name);
    },
    sayHi:function(){
        return function(){
            console.log("hi "+this.name);
        }
    },
    sayNihao:function(){
        let that=this;
        return function(){
            console.log("ni hao "+that.name);
        }
    }
}
person.sayHello();
person.sayHi()();
person.sayNihao()();

sayHello输出'hello 李四' 这个主要看的是this指向,this指向调用者,这里是person

sayHi输出"hi undefined" 首先person.sayHi()会返回一个函数对象,这个函数对象再执行,返回函数并没有指定调用者,这时会使用全局环境.this会指向window(浏览器环境),但是由于name是let修饰,所以window.name并不存在.如果改成var修饰这里的输出结果为"hi 张三"

sanNihao输出结果为'ni hao 李四',这里是一个闭包,在外部访问内部变量that,根据上面同样的分析that指向person对象

你可能感兴趣的:(javascript,前端,开发语言)