ES5中只有全局作用域和函数作用域,我们都知道他没有块级作用域。ES6中多了一个let,他可以保证外层块不受内层块的影响。即内层块形成了一个块级作用域,这是let的一个特点。
函数的内部环境可以通过作用域链访问到所有的外部环境,但是外部环境却不可以访问外部环境,这就是作用域的关键。但是我们要知道,作用域是在一个函数创建时就已经形成的,而不是调用时。
作用域链正是内部上下文所有变量对象(包括父变量对象)的列表
“闭包,允许使用内部函数(即函数定义和函数表达式位于另一个函数的函数体内),而且,这些内部函数可以访问他们所在的外部函数中的声明的所有局部变量丶参数和声明的其他内部函数,当其中一个这样的内部函数在包含他们的外部函数之外被调用时,就会形成闭包。即内部函数会在外部函数返回后被执行。而当这个内部函数执行时,它仍然必须访问其外部函数的局部变量丶参数以及其他内部函数。这些局部变量丶参数和函数声明(最初时)的值是外部函数返回时的值,但也会受到内部函数的影响。”
从定义上来看,所有的函数都可以是闭包。当一个函数在调用时,引用了不是自己作用域内定义的变量(通常称其为自由变量),则形成了闭包;闭包是代码块和创建该代码块的上下文中数据的结合。
// 执行上下文,代码的先后顺序不同,执行的结果也各不相同、
console.log(a);
var a = 100;
function fn(name){
age = 20;
console.log(age);
var age;
}
// 作用域和闭包,this
var a = {
name : 'A',
fn : function(){
console.log(this.name)
}
}
a.fn();
a.fn.call({ name : "B" });
var fn1 = a.fn;
fn1()
console.log(fn1);
console.log(fn);
console.log(this === window);
// this
// 作为构造函数执行,作为对象属性执行,作为普通函数执行,call,apply,bind。
// 作用域和闭包,this代码演示。
// 构造函数
function arr(){
this.name = name;
}
let f = new arr("liebo")
f.name;
console.log(f);
// 作为一个对象的属性
let obj = {
name : "A",
frintName : function(){
console.log(this.name);
}
}
obj.frintName();
// 普通函数
function fn(){
console.log(this)
}
fn();
// call,apply,bind,
function fn1(name,age){
console.log(name);
console.log(this);
}
fn1.call({ x : 1 },'liebao',20);
fn1.apply({ x : 200},['liebao',20]);
let fn2 = function(name,age){
console.log(name);
console.log(this);
}.bind({ x : 300 })
fn2('liebao',20)
// 无块级作用域
if(true){
var name = '7';
}
console.log(name);
// 函数和全局作用域
var a = 100;
function fn() {
var a = 200;
console.log('fn',a);
}
console.log('Global',a);
console.log(fn);
// 作用域链
var a = 100;
function fn(){
var b = 200;
console.log(a);
console.log(b);
}fn();
var a = 100;
function F1(){
var b = 200;
function fn1(){
var c = 300;
console.log(a);
console.log(b);
console.log(c);
}
fn1();
}F1();
// js 没有块级作用域
// ES6 有块级作用域
// 作用域和闭包,函数作为返回值
function F1(){
var a = 100
return function(){ //返回一个函数,函数作为返回值
console.log(a);
}
}
var f1 = F1();
var a = 200;
console.log(f1)
// 作用域闭包,函数作为参数传递
// 闭包 2,函数作为参数传递
function F1() {
var a = 100
return function() {
console.log(a)
}
}
var f1 = F1()
function F2(fn){
var a = 300;
fn();
}
F2(f1)
// 如何理解作用域,自由变量,作用域链,即自由变量的查找
// 实际开发中闭包的应用
function isFirstLoad(){
var _list = [];
return function(id){
if(_list.indexOf(id)>=0){
return false
}else{
_list.push(id)
return true
}
}
}
// 使用,在isFirstLoad函数外,根本不可能修改掉_list的值。
var fristLoad = isFinite();
console.log(isFinite(10));
console.log(isFinite(10));
console.log(isFinite(20))
每次当控制器转到ECMAScript可执行代码的时候,即会进入到一个执行上下文。执行上下文(简称-EC)是ECMA-262标准里的一个抽象概念,用于同可执行代码(executable code)概念进行区分。活动的执行上下文组在逻辑上组成一个堆栈。堆栈底部永远都是全局上下文(global context),而顶部就是当前(活动的)执行上下文。堆栈在EC类型进入和退出上下文的时候被修改(进栈或出栈)。
变量对象(Variable Object)、活动对象(Activation Object) 浏览器在对代码进行解析时,会先进行变量声明和函数声明以及函数形参声明;(全局作用域下)那么这些优先声明的变量储存在哪里呢? 没错,就在变量对象中(抽象概念);活动对象怎么理解呢? 抽象变量对象VO (变量初始化过程的一般行为), --> 全局上下文变量对象GlobalContextVO, (VO === this === global),函数上下文变量对象FunctionContextVO, (VO === AO, 并且添加了<arguments>(形参类数组)和<formal parameters>(形参的值)),在函数执行上下文中,VO是不能直接访问的,此时由活动对象扮演VO的角色。Arguments对象是活动对象的一个属性,它包括如下属性:
-
callee — 指向当前函数的引用
-
length — 真正传递的参数个数
- properties-indexes (字符串类型的整数) 属性的值就是函数的参数值(按参数列表从左到右排列)。 properties-indexes内部元素的个数等于arguments.length. properties-indexes 的值和实际传递进来的参数之间是共享的。