Javascript 知识扫盲(一)

      本文仅记录一些必知的知识理论概念,用以在模棱两可的时候快速回忆相关内容。希望随着知识的应用熟练度提升,尽快将所有必知知识融入到常识之中。废话不多说,详细内容如下:

一、堆、栈、队列

堆:堆数据结构通常是一种树状结构。存取数据类似于key/value,依据属性名获取对应的值。

栈:栈是一种数据在内存中的存储区域,它表达的是一种存取方式,遵循先进后出,后进先出的基本原则。同时它可以用来规定代码的执行顺序,在JavaScript中我们称之为函数调用栈[call stack]。

队列:为了了解事件循环机制是我们需要明白队列的数据结构,一言概之就是先进先出,后进后出,这一点不要混淆。

思考:浏览器运行javascript脚本代码时都进行了哪些操作?(相关内容会在变量提升部分详解)



二、基本数据类型和引用数据类型

基本数据类型

类型
Booleantrue/false
Number所有的数字
String所有的字符串
Nullnull
Undefinedundefined

由于目前常用的浏览器版本还不支持Symbol,故而主观从数据类型中摘除。

引用数据类型

类型
Object{key: value}

引用类型的创建和引用其实是分离开来的:

	var user_table = {
		userName: 'suka',
		userAge: '3.165'
	}
	
	var user_copy = user_table;
	user_copy.userName = 'maka';
	
	console.log(user_table); //object {userName: 'maka', userAge: '3.165'}

结果显而易见,并不是 user_table 赋值给了 user_copy ,与之相反的是 user_table 和 user_copy 都是堆内存对象的引用,而修改操作是通过它们直接作用于堆内存数据。


三、执行上下文

执行上下文可以理解为当前代码的运行环境,多个上下文先后有序执行就是JavaScript的执行本质。

全局环境: 代码运行起来后会首先进入全局环境。
函数环境: 函数执行时,进入当前函数执行代码。
JavaScript引擎会以栈的方式来处理他们,这个栈就是前面提到过的函数调用栈。函数调用栈规定了JavaScript代码的执行顺序。栈底永远是全局上下文,栈顶则是正在执行的上下文。

为了清晰的理解函数调用栈的执行过程,我们通过一个实例来了解一下:

	function fn() {
		var random = 800;
		function fn2() {
			random += 200;
			console.error(random)
		}
		return fn2;
	}
	var result = fn();
	result(); // 1000

这是一个简单的闭包例子,我们现在跟据函数执行时才会创建上下文这一原则来分析它的调用栈顺序。

step1:全局上下文入栈,位于栈底。

step2:全局代码执行过程中遇到了fn()函数,执行 var result = fn() 因此fn会创建对应的执行上下文并入栈。

step3:在fn的可执行代码中虽然有fn2函数,但是未触发执行操作,所以fn代码执行完毕后出栈,回到上级上下文也就是全局上下文之中。

step4:继续执行全局上下文的代码,这个时候遇到了result(),调用栈创建了一个新的上下文result并入栈。

step5:这时候result()实际上就是fn中声明的函数fn2,因此会执行fn2之中的代码,执行完毕后,由于fn2没有新的执行操作result出栈,回到上级上下文也就是全局上下文之中。

全局上下文入栈fn入栈fn出栈result入栈result出栈
fn ECresult EC
Global ContextGlobal ContextGlobal ContextGlobal ContextGlobal Context

执行上下文的生命周期

一个执行上下文的生命周期大致可以分为两个阶段即:

1.创建阶段:执行上下文在创建阶段会创建变量对象确认作用域链,以及确定this的指向

2.执行阶段:执行上下文在执行阶段会完成变量赋值函数引用、以及执行其他可执行代码


四、变量对象

变量对象包括那些?

  • 在JavaScript中声明的所有变量
  • 当前上下文中所有的函数声明 (通过functio声明的函数)
  • 当前上下文中的所有变量声明 (通过var声明的变量)
  • 所有函数的参数 (函数参数又分为形参和实参)

变量对象的创建过程?

变量对象的创建,依次经历了以下几个过程:

1.变量对象首先会获得函数的参数变量以及其值。

2.依次获取当前上下文中所有的函数声明(function关键字声明的函数)

在变量对象中会以函数名建立一个属性,属性值为指向该函数内存地址的引用,如果函数名属性已经存在,执行覆盖操作。

3.依次获取当前上下文中所有的变量声明(var关键字声明的变量)

在变量对象中以变量名建立一个属性,属性值为undefined,如果变量名已经存在,则会直接跳过(防止变量属性被无效化)。注意这里说的是创建过程,这就引申出了变量创建优先级的问题

	var num = 20;
	function fn() { console.log('fn') }
	function fn() { console.log('cover fn') }
	function a() { console.log('cover a') }
	
	console.log(a);
	fn();

	var fn = 'I want cover it';
	console.log(fn);

在这里具体分析一下上述代码的创建过程和执行过程。

	//创建阶段!!
	function fn() { console.log('fn') }
	function fn() { console.log('cover fn') }
	function a() { console.log('cover a') }
	var a = undefined
	var fn = undefined

	//执行阶段!!
	a = 20;
	console.log(a);   //20
	
	fn();             //cover fn
	
	fn = 'I want cover it';
	console.log(fn)   //I want cover it'

跟据输出结果来看,在创建阶段函数 fn 执行了同名函数的覆盖,变量 fn 并不与函数 fn 冲突,所以提升为 undefined 。在执行阶段 a 与 fn 的重新赋值导致他们发生了变化。

变量提升问题

接上述代码,函数fn在创建的过程中先是被定义为undefined,而后在执行阶段被具体赋值,这种现象我们称之为变量提升

优先级问题 仅了解

var声明并赋值 > function > 形参 > 预定义变量 > var声明不赋值



五、作用域与作用域链

全局作用域和函数作用域 是作用域中最常见的两种

全局作用域

全局作用域中声明的变量和方法可以在代码的任何地方访问

1.全局对象下拥有的属性和方法

window.top
window.location

2.在 全局上下文 声明的变量和方法

在全局上下文中声明的对象实际上就是在window变量对象上操作。所以他们会成为window对象的属性和方法,也拥有全局作用域。

函数作用域

函数作用域中声明的变量与方法,不能被平级及上级作用域访问,只能被作用域内及其子级作用域访问

以message为例

	function fn() {
		var message = 'you can not find me'
		function fn_son() {
			/* 可以访问 */
		}
		return function() {
			/* 可以访问 */
		}
	}
	
	console.error(message); /* 报错!不可以访问 */
	
	function fn_borther() {
		/* 不可以访问 */
	}

块级作用域与函数自执行

ES6新引入了块级作用域概念,在这之前的程序采用函数自执行来模拟块级作用域的功能,学习它便于理解之前的老程序。

对比两个for循环 ‘i’ 变量

	var list = ['A','B','C','D'];
	
	/* 通过函数自执行创建函数作用域 */
	(function() {
		for(var i = 0; i < list.length; i++) {
			console.log('Rule number' + arr[i])
		}
	})();
	console.log(i); // i is not defined 防止了变量污染
	
	/* 仍在全局作用域 */
	for(var i = 0; i < list.length; i++) {
		console.log('Rule number' + arr[i])
	}
	console.log(i); // 4 如果出现同名变量则变量污染
	

通过这种方式,我们可以开辟函数作用域来避免对其他代码造成干扰,限定变量 i 只在 for 循环中生效。
ES6一些语法糖后续作者会再整理笔记,ES7,8,9一些有趣的新特性也会一并整合。不过那并不属于 ‘知识扫盲’ 范畴,优先级较低。

作用域链

作用域链是由当前执行环境与上层执行环境的一系列变量对象组成的,它保证了符合访问权限的变量和函数再当前执行环境中进行有序的访问。

	var global = 'my name is ';
	
	function tellMe() {
		var message = global + 'coco ball ';
		function relName() {
			var hint = 'is music';
			return message + hint;
		}
		return relName();
	}

	tellMe();

初次接受作用域链的概念可能会有些难以理解,参照上述代码去理解符合访问权限的变量和函数再当前执行环境中进行有序的访问这句话。作用域链概念至关重要,更多的知识需要结合闭包理解。




总结 : 本篇内容简单梳理了数据结构、内存空间、执行上下文、变量对象、作用域、作用域链相关的内容。下一篇结合作用域链相关知识梳理闭包相关知识。

上一篇文章: 预留,本店打烊

下一篇文章: Javascript 知识扫盲(二)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值