饥人谷学习进阶第 6 天
作用域 & 作用域链
作用域:变量与函数的可访问范围(即作用域控制着变量和函数的生命周期)。
JS变量的作用域有:全局作用域和局部作用域两种。
全局作用域
在代码中任何地方都能访问到的对象拥有全局作用域
一般来说以下几种情形拥有全局作用域:
1.最外层函数和在最外层函数外面定义的变量拥有全局作用域;
2.所有未定义直接赋值的变量自动声明为拥有全局作用域;
3.所有window对象的属性拥有全局作用域(一般情况下,window对象内置属性都拥有全局作用域)。局部作用域
一般只在固定的代码片段内可访问到,最常见的如:函数内部。
作用域链:
在JS中,函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。
当一个函数创建后,它的作用域链会被创建此函数的作用域中可访问的数据对象填充。
function a (x,y) {
var b = x + y;
return b;
}
在函数a创建时,它的作用域链中会填入一个全局对象,该全局对象包含了所有全局变量。
函数a的作用域将会在执行时要用到,例如
var total = a (5, 10);
执行此函数时会创建一个称为“运行期上下文(execution context)”的内部对象,运行期上下文定义了函数执行时的环境。每个运行期上下文都有自己的作用域链,用于标识符解析,当运行期上下文被创建时,而它的作用域链初始化为当前运行函数的[[Scope]]所包含的对象。
这些值按照它们出现在函数中的顺序被复制到运行期上下文的作用域链中。它们共同组成了一个新的对象,叫“活动对象(activation object)”,该对象包含了函数的所有局部变量、命名参数、参数集合以及this,然后此对象会被推入作用域链的前端,当运行期上下文被销毁,活动对象也随之销毁。
在函数执行过程中,每遇到一个变量,都会经历一次标识符解析过程以决定从哪里获取和存储数据。该过程从作用域链头部,也就是从活动对象开始搜索,查找同名的标识符,如果找到了就使用这个标识符对应的变量,如果没找到继续搜索作用域链中的下一个对象,如果搜索完所有对象都未找到,则认为该标识符未定义。函数执行过程中,每个标识符都要经历这样的搜索过程。
1.函数在执行的过程中,先从自己内部找变量
2.如果找不到,再从创建当前函数所在的作用域去找, 以此往上
3.注意找的是变量的当前的状态
基本类型、引用类型
- 基本类型值(数值、布尔值、字符串、null和undefined):指的是保存在栈内存中的简单数据段;
- 引用类型值(对象、数组、函数、正则):指的是那些保存在堆内存中的对象,变量中保存的实际上只是一个指针,这个指针指向内存中的另一个位置,由该位置保存对象
参数传递
- 值传递
针对基本类型值,传递的是储存在栈中的值,实参和形参是属于不同地址的 - 引用传递
针对引用类型值,传递的是储存在栈中的地址(指针),实参和形参都是指向同一个地址堆
引用传递在栈中保存指针,值传递是在栈中保存具体的值
浅拷贝 & 深拷贝
- 浅拷贝
function shallowCopy(oldObj) {
var newObj = {};
for(var i in oldObj) {
if(oldObj.hasOwnProperty(i)) {
// hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是是否有指定的键)
newObj[i] = oldObj[i];
}
}
return newObj;
}
- 深拷贝
function deepCopy(oldObj) {
var newObj = {};
for(var key in oldObj) {
if(typeof oldObj[key] === 'object') {
newObj[key] = deepCopy(oldObj[key]);
}else{
newObj[key] = oldObj[key];
}
}
return newObj;
}