本文已参与『新人创作礼』活动,一起开启掘金创作之路
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
介绍
官方解释:javascript(简称"JS")是一种具有函数优先的轻量级,解释性或即时编译型的编程语言javaScript是2012年,所有浏览器都完整的支持ECMScript5.1,旧版本的浏览器至少支持ECMAScript 3 标准。ECMA国际组织发布了ECMAScript的第六版,该版本正式名称为ECMAScript 2015,但通常被称为es6或者ES2015。
1.计算机栈
栈者,存储货物或供旅客住宿的地方,可引申为仓库。计算机领域里,就是指数据暂时存储的地方,所以才有进栈、出栈的说法。
1.1 数据结构中的栈
- 栈是一组数据的存放方式,特点是先进后出,后进先出
| 方法名 | 操作 |
|---|---|
| push() | 添加新元素到栈顶 |
| pop() | 移除栈顶的元素,同时返回被移除的元素 |
class Stack {
private items: number[] = [];
// 添加元素到栈顶,也就是栈的末尾
push(element: number) {
this.items.push(element);
}
// 栈的后进先出原则,从栈顶出栈
pop(): number {
return this.items.pop();
}
}
let stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
console.log(stack.pop());
1.2 代码的运行方式
- 表示函数的一层层调用
function one() {
function two() {
function three() {
debugger;
}
three();
}
two();
}
one();
1.3 内存区域
-
栈也是是存放数据的一种内存区域
-
程序运行的时候,需要内存空间存放数据。一般来说,系统会划分出两种不同的内存空间:一种叫做
stack(栈),另一种叫做heap(堆)stack是有结构的,每个区块按照一定次序存放,可以明确知道每个区块的大小heap是没有结构的,数据可以任意存放。因此,stack的寻址速度要快于heap
-
只要是局部的、占用空间确定的数据,一般都存放在stack里面,否则就放在heap里面,所有的对象都存放在
heap
function task() {
var a = 1;
var b = 2;
var c = {
name: 'zhufeng',
age: 10
}
}
task();
2. 队列
- 队列是一种操作受限制的线性表
- 特殊之处在于它只允许在表的前端进行删除操作,而在表的后端进行插入操作
- 进行插入操作的端称为队尾,进行删除操作的端称为队头
- 因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出线性表
class Queue {
private items: number[] = [];
// 添加元素到栈顶,也就是栈的末尾
enqueue(element: number) {
this.items.push(element);
}
dequeue() {
return this.items.shift();
}
}
let queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
console.log(queue.dequeue());//1
3. 数据类型
-
JS中有七种基本数据类型
- 六种基本数据类型
BooleanNullUndefinedNumberStringSymbol - 一种引用类型
object{}[]/^$/new Date()Math
- 六种基本数据类型
| 类型 | 值 |
|---|---|
| Boolean | true或false |
| Null | null |
| Undefined | undefined |
| Number | 数字 |
| String | 字符串 |
| Symbol | 符号类型 |
4. 执行上下文
4.1 如何存储
- 当函数运行时,会创建一个执行环境,这个执行环境就叫执行上下文(
Execution Context) - 执行上下文中会创建一个对象叫作变量对象(
Value Object),基础数据类型都保存在变量对象中 - 引用数据类型的值保存在堆里,我们通过操作对象的引用地址来操作对象
function task(){
var a = 1;
var b = {
name:'zhufeng'
}
var c = [1,2,3];
}
let ExecuteContext = {
VO:{
a:1,
b:'XO1',
c:'XA1'
}
};
4.2 如何复制
4.2.1 基本数据
- 基本数据类型复制的是值本身
var a = 1;
var b = a;
b = 2;
console.log(a);
var ExecuteContext = {
VO: { a: 1 }
};
ExecuteContext.VO.b = ExecuteContext.VO.a;
ExecuteContext.VO.b = 2;
console.log(ExecuteContext.VO.a);
4.2.2 引用数据
- 引用数据类型复制的是引用地址指针
var m = { a: 1, b: 2 };
var n = m;
n.a = 10;
console.log(m.a);
var ExecuteContext = {
VO: { m: { a: 1, b: 2 } }
};
ExecuteContext.VO.b = ExecuteContext.VO.a;
ExecuteContext.VO.a = 10;
console.log(ExecuteContext.VO.a);
5. 多个执行上下文栈
5.1 执行上下文分类
-
JS代码在执行的时候会进入一个执行上下文,可以理解为当前代码的运行环境 -
在
JS中运行环境主要分为全局执行上下文环境和函数环执行上下文环境- 全局执行上下文只有一个,在客户端中一般由浏览器创建,也就是我们熟知的
window对象,我们能通过this直接访问到它 window对象还是var声明的全局变量的载体。我们通过var创建的全局对象,都可以通过window直接访问
- 全局执行上下文只有一个,在客户端中一般由浏览器创建,也就是我们熟知的
5.2 多个执行上下文
- 在JS执行过程会产出多个执行上下文,
JS引擎会有栈来管理这些执行上下文 - 执行上下文栈(下文简称执行栈)也叫调用栈,执行栈用于存储代码执行期间创建的所有上下文,具有
LIFO(Last In First Out后进先出,也就是先进后出)的特性 - 栈底永远是全局上下文,栈顶为当前正在执行的上下文
- 当开启一个函数执行时会生成一个新的执行上下文并放入调用栈,执行完毕后会自动出栈
function one() {
var a = 1;
debugger;
function two() {
var b = 1;
debugger;
function three() {
var c = 1;
debugger;
}
three();
debugger;
}
two();
debugger;
}
one();
var globalExecuteContext = {
VO: { setTimeout: 'setTimeout' }
}
var executeContextStack = [globalExecuteContext];
var oneExecuteContext = {
VO: { a: 1 }
}
executeContextStack.push(oneExecuteContext);
var twoExecuteContext = {
VO: { b: 2 }
}
executeContextStack.push(twoExecuteContext);
var threeExecuteContext = {
VO: { c: 3 }
}
executeContextStack.push(threeExecuteContext);
console.log(executeContextStack);
executeContextStack.pop();
executeContextStack.pop();
executeContextStack.pop();
6. 执行上下文生命周期
6.1 生命周期有两个阶段
-
一个新的执行上下文的生命周期有两个阶段
-
创建阶段
- 创建变量对象
- 确定作用域链
- 确定
this指向
-
执行阶段
- 变量赋值
- 函数赋值
- 代码执行
-
6.2 变量对象
-
变量对象会保存变量声明(
var)、函数参数(arguments)、函数定义(function)- 变量对象会首先获得函数的参数变量和值
- 获取所有用
function进行的函数声明,函数名为变量对象的属性名,值为函数对象,如果属性已经存在,值会用新值覆盖 - 再依次所有的var关键字进行的变量声明,每找到一个变量声明,就会在变量对象上建一个属性,值为
undefined,如果变量名已经存在,则会跳过,并不会修改原属性值,let声明的变量并不会在此阶段进行处理
-
函数声明优先级更高,同名的函数会覆盖函数和变量,但同名
var变量并不会覆盖函数.执行阶段重新赋值可以改变原有的值
6.2.1 基本类型
console.log(a);
var a = 1;
var a = undefined;//变量提升
console.log(a);
a = 1;
6.2.2 变量提升
- 正常编写
var a = 1;
function fn(m) { console.log('fn'); }
function fn(m) { console.log('new_fn'); }
function a() { console.log('fn_a'); }
console.log(a);
fn(1);
var fn = 'var_fn';
console.log(fn);
//1
//new_fn
//var_fn
- 真正执行
// 创建阶段
function fn(m) { console.log('fn'); }
function fn(m) { console.log('new_fn'); }
function a() { console.log('fn_a'); }
var a = undefined;
var fn = undefined;
//执行阶段
a = 1;
console.log(a);
fn();
fn = 'var_fn';
console.log(fn);
- 上下文
// 创建阶段
var globalEC = {
VO: {
...arguments,
a: () => { console.log('fn_a'); },
fn: () => { console.log('new_fn'); }
}
}
var ECStack = [globalEC];
//执行阶段
globalEC.VO.a = 1;
console.log(globalEC.VO.a);
globalEC.VO.fn();
globalEC.VO.fn = 'var_fn';
console.log(globalEC.VO.fn);
6.2.3 激活对象
- 在函数的调用栈中,如果当前执行上下文处于函数调用栈的顶端,则意味着当前上下文处于激活状态,此时变量对象称为活动对象
(AO,Activation Object) VO=>AO - 活动变量包含变量对象所有的属性,并有包含
this指针
function one(m) {
function two() {
console.log('two');
}
}
one(1);
//执行阶段 VO=>AO
let VO = AO = {
m:1,
two: () => { console.log('two'); },
}
let oneEC={
VO,
this: window,
scopeChain:[VO,globalVO]
}
6.2.4 全局上下文的变量对象
- 在浏览器里,全局对象为
window - 全局上下文的变量对象为
window,而且这个变量对象不能激活变成活动对象 - 只在窗口打开,全局上下文会一直存在,所有的上下文都可以直接访问全局上下文变量对象上的属性
- 只有全局上下文的变量对象允许通过VO的属性名称来间接访问,在函数上下文中是不能直接访问VO对象的
- 未进入执行阶段前,变量对象中的属性都不能访问!但是进入到执行阶段之后,变量对象转变成了活动对象,里面的属性都能被访问了,对于函数上下文来讲,活动对象与变量对象其实都是同一个对象,只是处于执行上下文的不同生命周期
原型链面试题
function Foo() {
getName = function () {
console.log(1);
}
return this;
}
Foo.getName = function () {
console.log(2);
}
Foo.prototype.getName = function () {
console.log(3);
}
var getName = function () {
console.log(4);
}
function getName() {
console.log(5);
}
Foo.getName();
getName();
Foo().getName();
getName();//1
new Foo.getName();
new Foo().getName();
new new Foo().getName();
知道答案或者文章有遗漏地方的小伙伴可以在评论区评论哦~~