Javascript底层原理总结
⽂档持续更新~
⽬录
基础
数据结构
栈:⼀种遵从先进后出 (LIFO) 原则的有序集合;新添加的或待删除的元素都保存在栈的末尾,称作栈顶,另⼀端为栈底。在栈⾥,新元素都靠近栈顶,旧元素都接近栈底。栈只是对原有数据进⾏了⼀次封装⽽已。⽽封装的结果是:并不去关⼼其内部的元素是什么,只是去操作栈顶元素,这样的话,在编码中会更可控⼀些
// 定义⼀个栈
class Stack {
constructor() {
this.items = []
}
push(element) { // ⼊栈
this.items.push(element)
}
pop() { // 出栈
return this.items.pop()
}
get peek() { // 末位
return this.items[this.items.length - 1]
}
get isEmpty() { // 是否为空栈
return !this.items.length
}
get size() { // 尺⼨
return this.items.length
}
clear() { // 清空栈
this.items = []
}
print() { // 打印栈数据
console.log(String())
}
}
const stack = new Stack()
console.log(stack.isEmpty) // true
// 添加元素
stack.push(5)
stack.push(8)
// 读取属性再添加
console.log(stack.peek) // 8
stack.push(11)
console.log(stack.size) // 3
console.log(stack.isEmpty) // false
队列:与上相反,⼀种遵循先进先出 (FIFO / First In First Out) 原则的⼀组有序的项;队列在尾部添加新元素,并从头部移除元素。最新添加的元素必须排在队列的末尾。例如⽇常⽣活中的购物排队。与栈类⽐,栈仅能操作其头部,队列则⾸尾均能操作,但仅能在头部出尾部进
class Queue {
constructor(items) {
this.items = items || []
}
enqueue(element){ // 向队列尾部添加⼀个(或多个)新的项
this.items.push(element)
}
dequeue(){ // 移除队列的第⼀(即排在队列最前⾯的)项,并返回被移除的元素
return this.items.shift()
}
head(){ // 返回队列第⼀个元素,队列不做任何变动
return this.items[0]
}
clear(){ // 清空队列
this.items = []
}
get size(){ // 返回队列内元素个数
return this.items.length
}
get isEmpty(){ // 队列内⽆元素返回 true,否则返回 false
return !this.items.length
}
print() {
console.log(String())
}
}
链表:存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的;每个元素由⼀个存储元素本⾝的节点和⼀个指向下⼀个元素的引⽤(指针/链接)组成。
集合:由⼀组⽆序且唯⼀(即不能重复)的项组成;这个数据结构使⽤了与有限集合相同的数学概念,但应⽤在计算机科学的数据结构中。
字典:以 [键,值] 对为数据形态的数据结构,其中键名⽤来查询特定元素,类似于 Javascript 中的Ob
ject。
散列:根据关键码值(Key value)直接进⾏访问的数据结构;它通过把关键码值映射到表中⼀个位置来访问记录,以加快查的速度;这个映射函数叫做散列函数,存放记录的数组叫做散列表。
树:由 n(n>=1)个有限节点组成⼀个具有层次关系的集合;把它叫做“树”是因为它看起来像⼀棵倒挂的树,也就是说它是根朝上,⽽叶朝下的,基本呈⼀对多关系,树也可以看做是图的特殊形式。
图:图是⽹络结构的抽象模型;图是⼀组由边连接的节点(顶点);任何⼆元关系都可以⽤图来表⽰,常见的⽐如:道路图、关系图,呈多对多关系。
JS堆栈的概念
堆(heap)⽤于复杂数据类型(引⽤类型)分配空间,例如数组对象、object对象;它是运⾏时动态分配内存的,因此存取速度较慢。
栈(stack)中主要存放⼀些基本类型的变量和对象的引⽤,(包含池,池存放常量),其优势是存取速度⽐堆要快,并且栈内的数据可以共享,但缺点是存在栈中的数据⼤⼩与⽣存期必须是确定的,缺乏灵活性,先进后出,后进先出原则,所以 push 优于 unshift。
js的数据类型主要分为两种:基本类型值和引⽤类型值。
基本类型值 有6种:undefined,null,boolean,number,string,symbol。这六种数据类型是按值访问的,是存放在栈内存中的简单数据段,数据⼤⼩确定,内存空间⼤⼩可以分配。基本类型值的复制是值的传递,赋值以后⼆者再⽆关联,修改其中⼀个不会影响另⼀个。
引⽤类型值: 5种基本类型值以外的数据类型都可以看做是引⽤类型值,⽐如array,object等,是保存在堆内存中的对象。js不允许直接访问堆内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际是在操作对象的引⽤⽽不是实际的对象,是按地址访问的。
可以看到,直接传递引⽤类性值的时候,传递的只是引⽤,⼆者指向同⼀块内存,所以修改其中⼀个,必然会引起另⼀个变量的变化。 在⽇常的使⽤中,我们把对象赋值给⼀个变量时,通常希望得到的是⼀个跟原对象⽆关的副本,修改新的变量不影响原对象,因此就有了浅拷贝和深拷贝。
作⽤域链的理解
作⽤域链是由于js的变量都是对象的属性,⽽该对象可能⼜是其它对象的属性,⽽所有的对象都是window对象的属性,所以这些对象的关系可以看作是⼀条链
当javascript查与变量相关联的值时,会遵循⼀定的规则,也就是沿着作⽤域链从当前函数作⽤域内逐级的向上查,直到顶层全局作⽤域结束,若到则返回该值,若⽆则返回undefined,这个链条是基于作⽤域的层次结构的,⼀旦当代码在坏境中执⾏时,会⾃动的创建⼀个变量对象的作⽤域链,其作⽤域链的⽤途也就是保证对执⾏坏境的全局变量和具有访问权限函数内的局部变量定制特殊的规则,由内到外有序的对变量或者函数进⾏访问,作⽤域链包含了在坏境栈中的每个执⾏坏境对应的变量对象,通过作⽤域链可以决定变量的访问与标识符的解析
作⽤域就是变量与函数的可访问范围,即作⽤域控制着变量与函数的可见性和⽣命周期。在JavaScript中,变量的作⽤域有全局作⽤域和局部作⽤域两种。
全局作⽤域:可以在代码的任何地⽅访问,⼀般来说,下⾯情况的对象会在全局作⽤域中:
最外层函数和在最外层函数外⾯定义的变量。
没有通过关键字"var"声明的变量。
浏览器中,window对象的属性。
局部作⽤域:函数作⽤域(Function scope),所有的变量和函数只能在作⽤域内部使⽤
变量提升、函数提升、浏览器解析变量的机制
JS预解析:
1.当浏览器加载html页⾯的时候,⾸先会提供⼀个全局JS代码执⾏的环境(全局作⽤域)
2.预解析(变量提升,浏览器的加载机制)
在当前的作⽤域中,js代码执⾏之前,浏览器⾸先会默认把所有带var和function的进⾏提前的声明或者定义
注意:对于变量只是进⾏了变量提前声明,⽽函数是提前声明并且定义
// 理解声明和定义
// 声明(declare):var num; --> 告诉浏览器在全局作⽤域中有⼀个num的变量了,如果⼀个变量只是声明了但是没有赋值,默认的值是undefined。
// 定义(defined):num = 1; --> 给变量进⾏赋值
// var ->在预解释的时候只是提前的声明
// function ->在预解释的时候提前的声明+定义都完成了
console.log(number); // num is not defined
console.log(num); // undefined
var num = 1;
console.log(num); // 1
console.log(fn); // 打印出函数体
function fn (){
console.log('fn')
};
console.log(fn); // 打印出函数体
变量和函数重名时:变量只是提前声明了,函数声明并且定义了,所以先打印的fn,然后开始执⾏时,变量开始赋值,函数不进⾏赋值。
console.log(fn) // fn(){console.log(4)}
function fn(){
console.log(2)
}
console.log(fn) // // fn(){console.log(4)}
var fn = 3
console.log(fn) // 3
function fn(){
console.log(4)
}
console.log(fn) // 3
函数表达式调⽤必须写到函数表达式的下⾯
fun(); // fun is not a function
var fun = function () {
console.log(22);
}
相当于执⾏
var fun;
fun();
fun = function () {
console.log(22);
}
3.预解析只发⽣在当前的作⽤域(全局作⽤域/局部作⽤域)下,例如:开始只对window下的进⾏预解释,只有函数执⾏的时候才会对函数中的进⾏预解析
function fn (){
console.log(a); // undefined
var a = 11;
console.log(a); // 11
}
fn()
console.log(a); // 10
相当于
var a = 10; // 全局变量
function fn (){
var a; // 局部变量
console.log(a); // undefined;
var a = 11;
console.log(a); // 11;
}
fn()
console.log(a); // 10
理解上下⽂和作⽤域
上下⽂与作⽤域是两个不同的概念,有时我⾃⼰也经常混淆,把它们视为是同⼀个东西,我们知道函
数的每次调⽤都会有与之紧密相连的作⽤域和上下⽂,从本质上说,作⽤域其实是基于函数的,⽽上下⽂是基于对象的,也就是说作⽤域是涉及到它所被调⽤函数中的变量访问,⽽调⽤⽅法和访问属性⼜存在着不同的调⽤场景(4种调⽤场景,函数调⽤,⽅法调⽤,构造器函数调⽤,call(),apply()间接调⽤),⽽上下⽂始终是this所代表的值,它是拥有控制当前执⾏代码的对象的引⽤
定义⼀个变量到这个变量被回收做了什么
进程与线程、什么是单线程?和异步有何关系
理解MVVM、MVC
理解AMD、commonjs
虚拟内存及缓冲区溢出
null和undefined的区别
ajax/axios/fetch的区别
Promise的原理
instanceof的原理
typeof的原理
数组的扁平化
xhr对象
ES6 Proxy的概念
闭包?运⾏时上下⽂⾥⾯包括什么?
结合作⽤域链看闭包
三栏布局javascript全局数组
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论