学习《JavaScript高级程序设计》----day02

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

提示

基础篇的前两节

三、引用值、原始值的复制

1.引用值

引用值就是保存在内存中的对象,使用instanceof判断其类型,亦可用toString()方法来进行粗略判断(函数和数组除外);原始值和函数一般使用typeof 判断其类型。
代码如下(示例):

let set = new Set();
let map = new Map();
let obj = {};
let sum = (a, b) => a + b;
let arr = [1, 2, 3, 4];

console.log(set instanceof Set); // true
console.log(map instanceof Map); // true
console.log(obj instanceof Object); // true
console.log(sum instanceof Object); // true
console.log(arr instanceof Array); // true

console.log(set.toString()); // [object Set]
console.log(map.toString()); // [object Map]
console.log(obj.toString()); // [object Object]
console.log(sum.toString()); // (a, b) => a + b
console.log(arr.toString()); //	1,2,3,4

2.复制。

  • 原始值复制
    通过变量把一个原始值赋值给另一个变量时,原始值会被复制到新变量的位置。
    代码如下(示例):

    let a = 23;
    let b = a; //将a复制给b
    console.log(a === b); //true
    b = 22; //修改b
    console.log(b === a); //false
    
  • 引用值复制
    通过变量把一个引用值赋值给另一个变量时,其实是复制的指向引用值的指针,因此会出现一个变量对对象进行修改,会反映在另一个变量上,即多个变量对同一个对象进行操作现象。对于引用值的复制往往有两种,即浅复制深复制
    代码如下(示例):
    浅复制

    let arr1 = [1, 2, 3, 4];
    let arr2 = arr1; //将arr1复制给arr2
    console.log(arr1 === arr2); //true
    arr1[0] = 0; // 修改arr1的第一个元素
    console.log(arr2[0]); // 0
    

    深复制

    let arr1 = [1, 2, 3, 4];
    let arr2 = []; 
    for	(let i = 0; i < arr1.length; i++){
    	arr2[i] = arr1[i];
    }
    console.log(arr1 === arr2); // false
    arr1[0] = 0; // 修改arr1的第一个元素
    console.log(arr2[0]); // 1
    
  • 原始值引用值深度复制例子:

    function deepCopy(target) { //深度复制
    	if (target === null) { //复制null
        	return target;
    	}
    	const primitiveTypes = [
        	"undefined", "number", "boolean", "symbol", "string", "function"
    	]; //原始类型和function
    	const targetType = typeof target;
    	for (const type of primitiveTypes) { //复制原始值和function
        	if (targetType === type) {
            	return target;
        	}
    	}
    	delete primitiveTypes;
    
    	//复制引用值
    	if (target instanceof Array) {//数组
        	const tem = [];
        	for (let i = 0; i < target.length; i++) {
            	tem[i] = deepCopy(target[i]);
        	}
        	return tem;
    	} else if (target instanceof Set) {// 集合
        	const set = new Set();
        	for (const e of target) {
            	set.add(deepCopy(e));
        	}
        	return set;
    	} else if (target instanceof WeakSet) {// WeakSet
        	const set = new WeakSet();
        	for (const e of target) {
            	set.add(deepCopy(e));
        	}
        	return set;
    	} else if (target instanceof Map) {
        	const map = new Map();
        	for (const [key, e] of target.entries()) {
            	map.set(key, deepCopy(e));
        	}
        	return map;
    	} else if (target instanceof WeakMap) {
        	const map = new WeakMap();
        	for (const [key, e] of target.entries()) {
            	map.set(key, deepCopy(e));
        	}
        	return map;
    	} else if (target instanceof Object) {
        	const obj = {};
        	for (let key in target) {
            	obj[key] = deepCopy(target[key]);
        	}
        	return obj;
    	}
    }
    let obj1 = {
    	map: new Map(),
    	set: new Set([1, 2, 3, 4, 4]),
    	arr: [1, 2, 3, {
        		name: 12,
        		print: function() {
            		console.log(this.name);
       			}
    	}],
    	flag: true,
    	print() {
        	console.log(this.arr);
    	},
    	nullObj: null
    };
    let obj2 = deepCopy(obj1);
    

四、执行上下文和作用域以及垃圾回收机制

1.执行上下文和作用域:

  • 变量和函数的上下文决定了它们可以访问哪些数据,以及它们的行为。每个上下文都有一个关联的对象,同时这个上下文中定义的所有变量和函数都存在于这个对象上。

  • 全局上下文就是最外层的上下文,在浏览器中通过var定义的变量和函数都会成为window对象的属性和方法

  • 上下文会在其所有代码都执行完毕后销毁,包括定义在该上下文上的变量和函数。

  • 函数作用域,当代码执行流进入函数时,函数的上下文会被推到一个上下文栈中。在函数执行完毕后,该函数上下文会弹出上下文栈,并且将控制权交还给之前的执行上下文。

  • 上下文中的代码在执行的时候,会创建变量对象的一个作用域链。这个作用域链决定了各级上下文中的代码在访问变量和函数的顺序。全局对象始终处于作用域链的末端,当前执行上下文处于作用域链的前端。访问顺序是从作用域链的前端逐级向末端访问。

  • 函数参数也遵循上述原则。

  • 作用域增强:某些语句会导致在作用域链的前端临时添加一个上下文,这个上下文会在代码执行后删除。以下两种情况会出现作用域链增强:
    try/catch 的catch块with语句
    代码如下(示例):

    //全局作用域
    let a = 10;
    function fun1(){//函数作用域1
    	let b = 11;
    	console.log(a);//10, 查找a的顺序为:函数作用域1---->全局作用域
    	function fun2(){//函数作用域2
    		let c = 12;
    		console.log(a);//10, 查找a的顺序为:函数作用域2---->函数作用域1---->全局作用域
    	}
    	fun2();
    }
    fun1();
    

2.垃圾回收机制

  • 垃圾回收机制
    JavaScript是使用垃圾回收的语言,也就是执行负责在代码执行时管理内存。基本思路是,确定哪个变量不在使用,然后释放它所占用的内存。常见的策略有:标记清理计数引用

  • 内存泄漏
    JavaScript中的内存泄漏大部分是由于不合理的引用导致的。
    1.意外声明全局变量导致内存泄漏,如:

    function func(){
    	//未使用关键字进行声明,默认使用var,即函数执行后name是一个全局变量
    	name = 23;
    }
    

    2.定时器会悄悄的导致内存泄漏,如下列代码,只要定时器一直运行,name就会一直占用内存:

    let name = “Jack”;
    setInterval(()=>{
    	console.log(name);
    }, 100);
    

    3.JavaScript闭包会导致内存泄漏,如下列代码:

    function outer(){
    	let name = "Jack";
    	return function inner(){
        	return name;
    	}
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值