以上是淘宝团队所译的JS高程对于执行环境和作用域的定义.
这里就是说每个函数都有一个属于自己的执行圈子,这个圈子里有抽象的对象对于我们是不可访问的对象,对象上带着当前执行环境中变量和函数的有访问权限的数据,就是所有国家的黄金都存在美国,名义上他是你的,自己国家作为一个主体,又包含了每个公民可以访问的信息,就是你的软妹币啊.这些.每个国家都是独立,不能被老美看到具体信息,国家作为一个执行环境,保护了你的信息,使其不可被外部访问,同理,并列的国家也是不能访问你的.
这里起到作用的就是函数this的指向,每个函数在定义之初就已经确定了作用域,除非是使用apply,call,这些强行改变this的指向,这是执行环境会发生变化(毕竟,这世界还是有不少日韩这样的狗腿子的╮(╯﹏╰)╭).
1. 执行环境是基于对象的,当函数被调用的时候创建,执行环境包含了函数的声明,变量,参数等,所谓的变量提升其实就是进入执行环境后,进入了执行环境的创建阶段,扫描所有被包含的函数,变量都被赋值了一个vo值,执行环境包含的代码还未真正开始执行. 创建后所有被包含的函数,变量,参数都在一个抽象的活动对象(变量对象上,底层代码可访问),活动对象进入执行的环境栈,这是作用域链也被初始化.笼统地说,此时的执行环境就是正在被执行的代码
2. 作用域: 基于函数,比如函数内部的参数,函数外部是不能访问的(闭包改变了执行环境的this指向),这是什么原因呢,函数外部的函数已经入栈,且被定义,他没有扫描到函数内部的变量,所以在上一个执行环境中是访问不到当前执行环境的变量的.
在一个函数中访问一个变量,如果当前的执行环境中没有,那么被包含的执行环境是有权访问包含它的父执行环境的,这样串联起来的关系,就是作用域链,即是作用域链就是用来限制拥有执行环境访问权限的变量函数访问执行环境的顺序的.
3. 当进入执行环境的第二阶段,也就是执行阶段,那么之前被扫描的变量就会被根据代码赋值,
function fn1() {
var a = 1;
function fn2() {
a = 123;
}
fn2();
console.log(a);// 123
}
这里就说明,根据作用域链进行访问,得到变量的使用权限,进行修改并不会改变这个变量的位置,所以在后续代码打印的时候,打印语句所在的执行环境中的变量,值已经改变了.
4. 当我们在执行某一个函数的时候,把变量值作为实参传递给函数,简单数据类型会出现复制一份,复杂数据类型会发生改变,这是为什么呢?
通俗的答案是:复杂数据类型传入函数的是它的指针,所以发生变化后,所有执行该对象的指针都发生了变化.
那么从执行环境的角度出发,这是为什么.
参数的传递是按值传入,(有种说法是两种:按值传递和引用传递),其实都是按值传入.
按值传入后,当参数传入后被调用的函数会创建新的执行环境,它的活动对象上,参数包含参数对象arguments(包含了传入的参数和callee指向函数自身),参数会被重新声明一个变量挂在当前的执行环境当中,因为所谓的引用传值,如果一致操作是指针指向的对象的值,所以给人一种是引用传值的假象,如果我们改变参数的值,就是改变了指向,就不会对父执行环境中的变量进行操作了,也就是在作用域链上失去对变量的操作权限.
function Person() {
}
var obj = new Person();
obj.name = "凌";
function fn1(obj) {
obj.name = "志";
obj = {};
obj.name = "婵"
}
fn1(obj);
console.log(obj.name);// 志
这个例子大概还是可以说明的,当指针的值,而不是存储数据值发生变化,还是可以操作对象的所以变成了志,当指针的值改变不在指向外部执行环境中变量对象的时候,就不能进行这个操作了.
写这个例子写出了忧伤的感觉,心塞