js
js执行上下文:
概念:抽象概念,包含当前代码执行的环境信息
全局 --> js 引擎 默认创建 <script>全局</script>
函数(局部) --> function
Eval --> eval函数中创建
应用场景:在作用域和程序执行顺序的程序中,都会有执行上下文
全局执行上下文:
js在执行任何代码前都会创建一个新的执行上下文,新的执行上下文--> 全局执行上下文
两个阶段:
创建阶段:全局对象和this被创建,为全局变量和函数分配内存,允许有“未定义”的值
执行阶段:代码的执行开始,为变量赋值 且 定义函数
函数执行上下文:
执行函数的时候被创建
eval执行上下文:
执行eval函数的时候创建,可能执行恶意的字符串,引起安全隐患
作用域和作用域链:
概念:作用域就是在运行时代码中的某些特定部分中变量,函数和对象的可访问性
作用域作用:
Jquery zepto函数库 自执行函数中
箭头函数 this指向问题
DOM事件中定义的变量都是局部函数,有可能在外部调用,比如:定时器
作用域
全局:任何地方都可以访问
局部:又叫函数作用域,在函数内部可以访问
全局作用域:
最外层的函数和最外层的变量都是全局作用域
所有未定义直接赋值的变量直接声明为全局作用域
所有window对象的属性拥有全局作用域
注意:全局变量容易污染命名空间
局部作用域(函数作用域):
声明在函数内部的变量
注意:快语句{},{}之内的 比如if、switch、for、while不会创建作用域
作用域链:
变量取值是在当前作用域中取值,如果没有就向外层作用域查找,直到找到全局作用域,查找的过程就是作用域链
闭包
概念:就是说一个函数访问他的父级或者父级以上的变量
作用:
访问函数的变量
让变量保持在内存中
闭包执行顺序:
闭包中的内存变量是驻留内存的****
闭包应用场景:
典型 jquery zepto库 (function (){属性 方法})()
防抖节流
Vue事件处理中
Methods:{
方法名(){
}
}
}
闭包优缺点:
优点:
减少全局变量的定义,减少全局污染
能够读取函数内部的变量
内存中驻留变量可以当作缓存使用
缺点:
内存泄露
因变量内存驻留可能影响性能
建议:闭包会跨作用域访问,会导致性能下降,尽量把变量定义为局部变量
原型和原型链
概念:
原型目的:共享内存,节省资源
原型:
每一个js对象创建(null除外),同时会创建一个原型对象,js对象会继承原型对象的内容
原型分为 显式原型和隐式原型
显式原型:显式原型是prototype 创建对象的时候会被创建
隐式原型:实例对象都有的__proto__属性,指向对应构造函数的原型对象
prototype :指向函数的原型对象,属于一个地址引用
对象下的prototype引用 对象.prototype
__proto__:在谷歌90之前显示的是__proto__,新版本中__proto__变成了 [[prototype]]
注意:任何构造函数中的方法和属性,执行过程中是实例化对象调用, 先查找构造函数本身有没有属性和方法,有则调用没有就去原型上查找, 要是也没有,就报错
Constructor:__proto__下的Constructor,是指向构造函数本身的
原型链:
实例对象有__proto__,而__proto__又指向构造函数的prototype属性 ,prototype 呢也有__proto__,就这样一层一层往上找,最后会找到object.protype上, 这就是原型链
原型链呢主要是一种继承的方式
this指向
this是一个指针变量,动态的指向当前函数的运行环境,指向其所在函数的调用者
如果没有人调用,就指向全局变量window
全局环境:全局环境下的this指向window
函数内部:指向的依然是window
对象中的this:对象内部方法的this指向调用这些方法的对象
一层对象:谁调用指向谁
二层对象:在多层对象中,this是离谁近指向谁
箭头函数:没有this和arguments(阿给门磁),捕捉外层的执行环境,继承 外层的this
构造函数:this指向构造函数创建的实例
注意:在js继承机制中,this指向构造函数创建的实例,非原型链
高阶函数
函数式编程:把现实世界的事物和事物之间的联系抽象到编程世界(对运算 过程进行抽象)
高阶函数:是函数式编程的一个重要概念,函数可以作为参数或者返回的 存在 函数可以作为参数被传递 函数可以作为返回值输出
高阶函数应用:
在数组函数中常用:filter map reduce every....,另外在防抖节流,程序设计模式中都有类似的应用
设计模式
单例模式:一个类 只能有一个实例对象 提供一个全局访问点
应用场景:弹窗 axios封装
工厂模式:用固定的方式批量创建对象
应用场景:权限和角色进行判断
观察者模式:多个观察者,对象发生变化是,通知所有观察者让他们更新 自己的状态
Object.defineProperty()
Setter --> set()设置
Getter --> get()取值
对象由键值对 组成的无序集合,对象中的每个属性值:任意类型
Object.defineProperty(obj,prop,desc)
Obj 目标对象
Prop 属性名称
Desc 属性特征
返回函数对象,返回obj
发表订阅模式:发布者内容变化,通过中间层接收并通知订阅者,订阅者收 到通知,更新对应的属性和其他模式
观察者模式和发布订阅模式有什么区别
观察者模式:只有两个元素,观察者和被观察者
发布订阅模式: 他有一个中间层。发布-->中间层-->订阅者
继承
面向对象:继承 封装 多态
继承:父类的属性和方法 可以被子类继承
// 手动实现call/apply
var obj = {
getInfo:function(){
console.log('this',this);
return this.name
}
}
var obj2 = {
name:"张三"
}
// Function 原型上定义的方法,所有对象都可用
Function.prototype.testcall = function(val){
console.log(1,val); // obj2
console.log(2,this); // getInfo
// this判断
if(typeof this !== "function"){
throw new Error("this错误")
}
context = val || window
// 处理参数
// console.log(3,val,[arguments]);
let arrs = [...arguments].slice(1)
// console.log(4,arrs);
val.fn = this
var res = val.fn(...arrs)
console.log(5,res,this); // this指向obj2
return 1
}
console.log('外层',obj.getInfo.testcall(obj2,11,22,333));
子类就可以调用父类的属性和方法
原型和原型链继承:
优点:实例是子类的实例同时也是父类的实例(他是将父类实例绑定 在子类原型上)
父类新增的属性和方法子类都可以访问
缺点:原型的属性是共享的,修改一个属性,所有的属性都修改了
创建子类实例,无法向父类传递参数
构造函数继承:子类构造函数内部调用父类的构造函数
使用call/apply 将父对象的构造函数绑定在子类上
优点:子类实例共享引用属性的问题
子类可以向父类传参
缺点:每个子类对象都有自己的执行函数,如果对象多了,内存消 耗大
组合式继承:原型链继承和构造函数 组合
优点:不存在引用属性共享问题
子类可向父类传参
缺点:子类原型上多余了一份父类的实例
原型式继承:有一个封装 继承过程的函数,是用来增强对象的
优点:不需要单独创建构造函数
缺点:属性会在对象中共享,子类实例不能向父类传参
寄生式继承:创建对象的方法,封装创建过程的函数,实际的对象
优点:不需要单独创建构造函数
缺点:代码复用性差,编写起来相对复杂
ES6继承:extends
Call/apply的作用以及手动实现
Call和apply的作用
是原型中方法,所有的函数都是function实例
调用一个对象的方法,另一个对象替换当前对象,this发生改变-->call/apply
Apply:
方法A.apply(方法B,[数组参数]) 方法A的this指向方法B
前面方法的this指向后面方法
Call:
方法A.call(方法B,参数,参数) 方法A的this指向方法B
Call不像apply参数写在数组内传,他是一个一个的传(参数,参数,参数)
实际使用:
Js中构造函数继承
判断js的某些数据类型
typeof {} []
伪数组--》数组
手动实现call/apply
在Function原型上定义要实现的call方法
获取程序执行的上下文
转换this的指向
// 手动实现call/apply
var obj = {
getInfo:function(){
console.log('this',this);
return this.name
}
}
var obj2 = {
name:"张三"
}
// Function 原型上定义的方法,所有对象都可用
Function.prototype.testcall = function(val){
console.log(1,val); // obj2
console.log(2,this); // getInfo
// this判断
if(typeof this !== "function"){
throw new Error("this错误")
}
context = val || window
// 处理参数
// console.log(3,val,[arguments]);
let arrs = [...arguments].slice(1)
// console.log(4,arrs);
val.fn = this
var res = val.fn(...arrs)
console.log(5,res,this); // this指向obj2
return 1
}
console.log('外层',obj.getInfo.testcall(obj2,11,22,333));
深拷贝浅拷贝
- 回顾数据类型和储存方式
基本数据类型 储存在栈内存中string number boolean null undefined Symbol
引用数据类型(复杂) 将其地址存在栈内存中, 真实数据存在堆内存中
object( function arrya data..... )
注意:基本数据类型在栈内存中 引用数据类型在堆内存中
浅拷贝:
在栈内存开辟一个新空间,并且将数据完全拷贝
基本数据类型
引用:复制地址的时候,两个变量指向同一片内存空间
浅拷贝的常用方法:
复制:
扩展运算符 对象的属性 数组 浅拷贝***
for ...in 对对象进行拷贝:二级是浅拷贝一级不是
深拷贝:
在栈内存中会开辟新空间,若对象的引用类型,堆内存中也会开辟空间用 来 存储值
JSON.parse()
缺点:对象属性的函数,无法拷贝
原型链上的属性值,无法拷贝
不能处理正则
会忽略Symbol和undefined
递归拷贝
深拷贝使用场景:
对象修改---》深拷贝
框架获取接口数据的时候,需要拷贝一份
小结:
浅拷贝:栈中开辟一块空间,并将对象的栈内存数据完全拷贝到该空间中。
对简单数据类型就是拷贝值,
对复杂数据,引用(复杂)类型时,实际复制的是其引用地址
深拷贝: 会在栈中开辟另一块空间,若被拷贝对象中有引用类型,则 还会在内存中开辟另一块空间储存引用类型的真实数据
共同点:都是用来复制数据
不同点:复制的对象不同,分为对地址操作(浅拷贝)和对地址里发值进 行操作
防抖节流
节流:n秒内只运行一次,若n秒内 反复的出发,只有一次生效
防抖:n秒后执行事件,如果n秒内重新出发,则重新计时
应用场景:
防抖:搜索框连续输入onchenge事件,窗口改变相关的事件
节流:懒加载,加载更多,防止高频点击,防止表单重复提交
浏览器如何渲染页面
cssom
css object model
采用css代码,选择器呈现树状结构,是css对象化表示,提供了api 操作css
分为两部分:
model:描述样式和规则的模型部分
view: 和元素视图相关的api部分
mode是cssom的本地,用style或link标签创建
<style></style>
<link rel=”” href=””>
model
构建DOM结构:
通过网络获取字节流和字符
分词--》得到一个一个的词组序(token) div span p
根据token分析语法,得到node
根据node 构建DOM树
cssom是依赖于DOM,在构建cssom树的时候,为元素添加样式, 浏览器会从父节点开始,递归向下,直到每个元素都添加上为止
view
窗口:moveTo moveBy resizeTo resizeBy
滚动:scrollx/y scroll() scrollBy()
布局:位置 尺寸 innerHeight/width screen....
浏览器如何渲染页面
从服务器拿到htm后
根据html构建DOM和cssom树
构建渲染树
页面重排和重绘
重绘:渲染节点发生改变时,不影响空间和位置
重排:也称回流,对位置( 比如说,宽高 内外边距 定位 )进行 操作,所以页面就会重排,重新渲染
减少重排
多个样式尽量合并
DomcumtFragment 进行缓存操作,引发一次重排
重排频率高的元素,采用绝对定位
小结:
1根据html文件构建DOM树和cssom树,构建DOM树期间,如 果遇到js,堵塞DOM树及cssom树,优先加载js文件,加载完毕,再继续构建DOM树以及cssom树
2渲染树:渲染树由DOM树、cssom树合并而成,构建渲染树,根 据渲染树计算每个可见元素的布局,并输出到绘制流程,将元 素渲染到屏幕上
3页面的重绘与重排,页面渲染完成后,js操作的DOM节点,根据js对DOM操作动作的大小,浏览器对页面进行重绘和重排
同步异步
Js单线程,不能同时进行多个任务
同步:发出功能,没得到结果之前,调用就不返回
过程:提交请求--》等待服务器处理
--》处理完结果为止(浏览器静止)
异步:异步调用发出请求,不能立刻得到结果通过状态值或回 调通知调用者
过程:提交请求--》服务器处理( 浏览器可以做其他任务 )
--》得到状态/通知--》处理完毕
同步任务:栈 异步任务:队列
同步
缺点:容易代码堵塞
优点:容易理解
异步
缺点:不按照顺序,不容易理解
优点:解决代码堵塞
小结:
同步是再执行栈中排队执行,顺序执行( 前一个执行完后面 才执行 )
异步是任务队列,等执行栈的任务执行完毕了,任务队列进 入执行栈,再按顺序执行
事件循环机制
Js执行顺序:
逐行执行,遇到报错,就停止,先同步后异步
执行步骤:
1把代码按照顺序放入js主线程的执行栈中,依次执行,执行完毕清空执行栈
2遇到异步代码,会放到event table中,同步执行完毕,把异步代码放入回调队列
3通过event loop轮询机制,把回调队列中的代码,放入执行栈中执行
4 Event loop 继续轮询回调队列,直到回调队列为空
宏任务和微任务:
在event table中区分执行时机
宏任务:setTimeout setInterval ajax dom 事件
微任务:promise async await
微任务比宏任务执行早
宏任务和微任务区别:
宏任务:浏览器定义的,DOM渲染后触发
微任务:es6定义的,DOM渲染前触发