执行上下文-作用域链-this-闭包:我觉得这几个概念应该不能独立分开讲,他们是有联系的。
- 函数
如何定义一个函数:
1.1 函数声明:
function 函数名称(参数) {方法体}
1.2 函数表达式
var x = function 函数名称(可省) (参数){函数体}
var x = new Function(’ ');
1.3 另一个概念:变量提升:
虽然javascript 是解释语言,边解释边运行,但是实质上它会预解释,也就是所谓的变量提升。
console.log(x); //undefined
console.log(x2); //undefined
x3(); //1
x2(); //not a function
var x = 1;
var x2 = function() {
}
function x3() {
console.log(1);
}
为啥会这样,看下去。
- 执行上下文(Execution Contexts)或者叫执行环境。
它是一个抽象的概念。
一个函数执行前后的两个阶段。
分为全局上下文,函数上下文,eval(略)。
可以想象它为一个栈,javascript 刚开始运作,把全局上下文压入栈底,运行全局上下文的步骤,遇到运行新的函数执行,创建新的函数执行上下文,压入栈中,运行后弹出。
一个函数的执行(函数上下文)分为两个阶段。
2.1. 创建阶段(上文)代码执行前
函数环境会初始化创建Arguments对象(并赋值)
函数声明(并赋值)
变量声明,(未赋值)
函数表达式声明(未赋值)
2.2 执行阶段
变量对象赋值
变量赋值函数表达式赋值 调用函数
顺序执行其它代码
这能解决变量提升问题。(为下面作用域链做铺垫)
那么如何抽象一个函数上下文呢?
2.3 函数上下文的抽象结构。
可以抽象为如下结构:把它当作一个对象
图源:https://www.cnblogs.com/TomXu/archive/2012/01/12/2308594.html
- 2.3.1 variable object 变量对象 简称VO
A variable object is a scope of data related with the execution context.
It’s a special object associated with the context and which stores variables and function declarations are being defined within the context.
变量对象(variable object) 是与执行上下文相关的 数据作用域(scope of data) 。
它是与上下文关联的特殊对象,用于存储被定义在上下文中的 变量(variables) 和 函数声明(function declarations) 。
2.3.2 作用域链
A scope chain is a list of objects that are searched for identifiers appear in the code of the context.
作用域链是一个 对象列表(list of objects) ,用以检索上下文代码中出现的 标识符(identifiers) 。
说白了先在自己作用域找变量找不到就找父亲的。
2.3.3 this
在一个函数上下文中,this由调用者提供,由调用函数的方式来决定。如果调用括号()的左边是引用类型的值,this将设为引用类型值的base对象,在其他情况下(与引用类型不同的任何其它属性),这个值为null。不过,实际不存在this的值为null的情况,因为当this的值为null的时候,其值会被隐式转换为全局对象。注:第5版的ECMAScript中,已经不强迫转换成全局变量了,而是赋值为undefined。
- 闭包,一个磨人的小妖精。(ps,这个我尝试性解释它的原理)
网上互联网对于闭包的定义乱七八糟,
说一下个人的定义:“定义在一个函数内部的函数”
说白了所有的函数都是全局的闭包.
function fn() {
var i = 1;
return function() {
console.log(i);
i++;
}
}
var fn1 = fn();
fn1(); //1
fn1(); //2
var fn2 = fn();
fn2(); //1
特点:
3.1 函数外部不能访问内部的属性,内部可以访问外部属性.
3.2 个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
第一点十分好解释,用上面的函数作用域链可以解释.
第二点:我尝试解释一下.
我上一篇的垃圾回收篇中:
假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和收集所有不能获得的对象。
var fn1 = fn(); 这行执行时 导致了存在了引用,导致fn()产生的scope 保留了下来,变量也没有被垃圾回收给清掉.
这个解释目前我决定还是有点牵强但是能解决挺多问题.
来个算法题:
var num = 10;
var obj = {
num: 20
};
obj.fn = (function(num) {
this.num = num * 3;
num++;
return function(n) {
this.num += n;
num++;
console.log(num);
}
})(obj.num)
// var fn = obj.fn; //w-num = 60 a-num = 21
// fn(5); // w-num = 65 a-num = 22 log 22
// obj.fn(10); //obj-num = 30 a-num =23 log 23
// console.log(num, obj.num); //65 30
结果 22 23 65 30
注意一点:只用函数执行才有函数上下文.