一、闭包
一句话概括专有名词
闭包函数:声明在一个函数中的函数叫做闭包函数。
闭包:内部函数总是可以访问其所在的外部函数中声明的参数或变量。
闭包的几个特点
- 函数嵌套函数
- 内部函数可以引用外部函数的参数和变量
- 外部函数的变量会常驻内存中不销毁
例子
function external(){
var i = 0;
function inside(){
i++;
console.log(i);
}
return inside;
}
var run = external();
run();
run();
run();
var run2 = external();
run2();
run2();
run2();
执行结果:1 2 3 1 2 3
闭包的应用场景
function Person(value){
var name = value;
this.getName = function(){
return name;
}
this.setName = function(value){
name = value;
}
}
var person = new Person('张三');
console.log(person.getName()); // 张三
person.setName('李四');
console.log(person.getName()); // 李四
以上代码的构造函数中定义了两个特权方法 getName()
setName()
这两个方法可以通过对象访问,而且都有权访问私有变量 name
但是在 Person
外部是无法访问到 name
的
二、堆栈
栈内存主要用于存储基本类型的变量,包括Boolean、Number、String、undefined、null以及对象变量的指针
堆内存主要用于存储对象(引用类型值 Object Function)
全局对象(GO)
var globalObject = {
Math: {},
String: {},
document: {}
...
window: this
}
执行上下文栈(ECStack)
执行上下文(EC)
. 值存储区(变量对象VO)
. 活动对象(AO)
Scope:作用域,创建函数的时候赋值
Scope Chain:作用域链
全局执行上下文
- 在执行全局代码前将window确定为全局执行上下文
- 对全局数据进行预处理
var定义的全局变量赋值为undefined,添加为window的属性
function声明的全局函数赋值fun,添加为window的方法
this赋值window - 开始执行全局代码
函数执行上下文
- 在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象
- 对局部数据进行预处理
形参变量赋值实参,添加为执行上下文的属性
arguments赋值实参列表,添加为执行上下文的属性
var定义的局部变量赋值undefined,添加为执行上下文的属性
function声明的函数赋值fun,添加为执行上下文的方法
this赋值调用函数的对象 - 开始执行函数体代码
1、var 和function声明创建在全局对象中,而let const class声明的变量创建在全局scope中
2、先到全局scope中找变量,查找不到再到全局对象中查找
三、深浅克隆
浅克隆只是克隆了内存地址,实际操作的还是同一块内存空间,所以在克隆后改变值会影响到被克隆原有的值。
案例
let a = {b: 1}
let c = a
c.b = 3
console.log(a) // {b: 3}
执行过程
那如何才能使变量c新开辟一块儿空间不和a公用呢,这个时候就要用深克隆的方式了
let a = {b: 1}
let c = {...a}
c.b = 3
console.log(a) // {b: 1}
console.log(c) // {b: 3}
执行过程
但要注意,上面这只是简单的一层对象的深克隆,如果我写成多层对象,深克隆就失效了。
let a = {b: {c: 1}}
let d = {...a}
d.b.c = 3
console.log(a) // {b: {c: 3}}
那像这种该如何深克隆呢,也有办法,先把对象转换成字符串,然后再转换成对象。
let a = {b: {c: 1}}
let d = JSON.parse(JSON.stringify(a))
d.b.c = 3
console.log(a) // {b: {c: 1}}
console.log(d) // {b: {c: 3}}
但是这里要注意,JSON.stringify是无法将function,Data等类型转换成字符串的,如果对象中包含了这样的类型值,JSON.stringify会过滤掉这些属性。要想实现对含有特殊类型值的对象实现深拷贝,就得使用递归。
function deepClone(obj){
// 过滤特殊情况
if (obj === null) return null;
if (typeof obj !== 'object') return obj;
if (obj instanceof RegExp) return new RegExp(obj);
if (obj instanceof Date) return new Date(obj);
if (obj instanceof Function) return new Function(obj);
// 不直接创建空对象的目的是克隆的结果和之前保持相同的所属类
let newObj = new obj.constructor;
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key]);
}
}
return newObj;
}
let a = {b: {c: 1},d: function(){}}
let e = deepClone(a)
e.b.c = 3
console.log(a) // {b: {c: 1},d: f()}
console.log(e) // {b: {c: 3},d: f()}
网友评论