1、作用域和作用域链
作用域:函数内变量的可用性的代码范围
作用域链:当你要访问一个变量时,首先会在当前作用域下查找,如果当前作用域下没有查找到,则向上一级作用域进行查找,直到找到全局作用域,这个查找过程形成的链条叫做作用域链。
多个上下级关系的作用域形成的链,它的方向是从下向上的(从内到外)查找变量时就是沿着作用域链来查找的。
作用:起到隔离变量,防止冲突
2、原型和原型链
原型:是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。
该function对象可以通过“.prototype”访问原型。构造函数实例化对象通过传递参数,让不同的实例化对象拥有不同的值。构造函数内部定义的属性是可以直接看到的,而“.prototype”的属性和值需要访问才能看到。利用这一特性,可以将公共部分提取出来,用“.prototype”的方式来定义,需要不同值的属性交由构造函数来定义。
Person. prototype = {
height : 1400,
lang : 4900
}
function Person (width){
this.width = width
}
var person1 = new Persion(200)
var person2 = new Persion(300)
Person.prototype === person1.__proto__
原型链:构造函数通过“.prototype”可以修改所属原型,构造函数产生的对象可以通过“.__ proto__”来访问他的原型,逐层往上找
3、同步和异步
同步是阻塞模式,异步是非阻塞模式。
同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;
异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
4、闭包
当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄露。
作用:可以读取函数内部的变量,让这些变量的值始终保持在内存中。
function text(){
var a = 1
return function log(){
console.log(a)
}
}
text()//如此,便通过return的方式把log函数就被保存下来了
5、== 和 ===
"==" 只判断等号两边的值是否相等,而不判断类型是否相同。值相同则返回 true
"===" 既要判断值是否相等,也要判断类型是否相同,即全等才能返回 true
"==" 相等运算符
相等运算符用来比较相同类型的数据时,与严格相等运算符完全一样。相等运算符隐藏的类型转换,会带来一些违反直觉的结果。推荐使用严格相等运算符
1、原始类型的值(string,number等基础类型) 原始类型的数据会转换成数值类型再进行比较
2、对象与原始类型值比较 对象(这里指广义的对象,包括数组和函数)与原始类型的值比较时,对象转化成原始类型的值,再进行比较
3、undefined 和 null undefined 和 null 与其他类型的值比较时,结果都为 false,它们互相比较时结果为 true
"===" 严格相等运算符
1、不同类型的值 如果两个值的类型不同,直接返回false
2、同一类的原始类型值 同一类型的原始类型的值(数值、字符串、布尔值)比较时,值相同就返回true,值不同就返回false
3、复合类型值 两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个地址
4、undefined 和 null undefined和null与自身严格相等
6、虚拟Dom的实现
当用原生 js 或 jquery 等库去操作 DOM时,浏览器会从构建 DOM 树开始讲整个流程执行一遍,所以频繁操作 DOM 会引起不需要的计算,导致页面卡顿,影响用户体验。
浏览器内核拿到html文件后,大致分为一下5个步骤:
1、解析html元素,构建dom 树
2、解析CSS,生成页面css规则树(Style Rules)
3、将dom树 和 css规则树关联起来,生成render树
4、布局(layout/ reflow),浏览器会为Render树上的每个节点确定在屏幕上的尺寸、位置
5、绘制Render树,绘制页面像素信息到屏幕上,这个过程叫paint
虚拟DOM:是由普通的js对象来描述DOM对象(浏览器执行js很快)
当数据改变后,diff算法重新比较新老DOM树(Vue优化diff算法,同层级比较,标签名比较,key值比较),计算出最小的变更,在操作DOM,更新视图。
7、this 关键字
this:指向一个对象,该对象被称为函数执行时的上下文对象
1、在函数中,this 会指向当前调用函数的元素 2、如果没有元素调用函数,this会指向window
8、同步、异步、宏、微
JS 是单线程,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
问题: 如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制。于是,JS 中出现了同步任务和异步任务。
同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
异步任务:不进入主线程、而进入“任务队列”的任务,当主线程中的任务运行完了,才会从“任务队列”取出异步任务放入主线程执行。异步任务又分为宏任务和微任务。
宏任务:定时器 setTimeout、计时器setInterval、事件绑定、ajax、回调函数、异步的I/O操作等
微任务:
-
promise:promise 的 then 方法中执行的代码就属于微任务。具体来说,当 promise 的状态变为 resolved(即调用了 resolve 函数),且有 then 方法注册时,then 方法中的函数就会被放入微任务队列中,等待 javascript 引擎执行。
-
async/await:async/await 实际上是基于 promise 实现的语法糖。使用 async 关键字定义的异步函数内部可以使用 await 关键字来等待 promise 对象返回结果,并且 async 函数的返回值也是一个 promise 对象。因此,async/await 语法中涉及到的代码都属于微任务。
-
mutationobserver:mutationobserver api 可以监听 dom 节点的变化,并在发生变化时执行回调函数。回调函数中的代码也属于微任务。
同步任务——>异步任务(微任务——>宏任务)
console.log("window同步任务1"); //1
function asyn(mac) {
console.log("function同步任务2"); //2、7
if(mac){ console.log(mac) } //8
return new Promise((resolve, reject) => {
console.log("function-Promise中的同步任务"); //3、9
resolve("function-Promise中回调的异步微任务")
})
}
setTimeout(() => {
console.log("setTimeout异步任务中的宏任务 宏+同"); //6
setTimeout(() => {
console.log("定时器中的定时器 宏+宏+同"); //11
}, 0)
asyn("定时器传递任务").then(res => {
console.log('setTimeout-asyn定时器中的:', res); //10
})
}, 0)
asyn().then(res => {
console.log(res); //5
})
console.log("window同步任务3") //4
•window同步任务1
•function同步任务2
•function-Promise中的同步任务
•window同步任务3
•function-Promise中回调的异步微任务
•setTimeout异步任务中的宏任务 宏+同
•function同步任务2
•定时器传递任务
•function-Promise中的同步任务
•setTimeout-asyn定时器中的: function-Promise中回调的异步微任务
•定时器中的定时器 宏+宏+同
9、异步编程
JS 是被设计用来操作网页 DOM 元素,如果多个JS线程同时操作 DOM 元素必然出乱子,所以 JS 被设计为单线程,但单线程又会导致代码阻塞。
通过异步解决阻塞问题,异步借助消息队列和 EventLoop(事件循环) 来实现
Promise 之前,回调套回调,容易形成回调地狱,代码向右增长,可读性差
fn1(function(){
setTimeout (function(){
setTimeout (function(){
xxx
},3000)
},1000)
})
Promise 之后,可以通过 then 方法,链式调用,代码向下增长
function p1(){
return new Promise(()=>})
}
function p2(){
return new Promise(()=>{})
}
function p3() {
return new Promise(()=>{})
}
p1().then(p3).then(p2) //then表示之后执行的意思
在 Promise 中,当一个错误被抛出时,Promise 链会自动跳过剩余的then()
方法,并直接进入到下面的catch()
或finally()
方法。
fetch("https://jsonplaceholder.typicode.com/ posts/1")
.then((response) =>response.json())
.then((json) =>{
console.log(json);
})
.catch ((error) =>{
console.error ( error);
})
.finally(()=> { });
async、await ,提供了一种更简洁的方法来处理异步操作
async声明异步函数。异步操作可以在该函数中被调用和执行,随着异步任务被处理,返回一个Promise对象。
async function foo() {
// 异步操作
}
await 等待一个异步任务完成。在此期间,JS 引擎可以去其他地方做一些任务,当异步操作完成时,在回到这个await表达式的位置,继续执行后面的逻辑。
async function foo() {
const result = await someAsyncOperation();
console.log(result);
}
async function f(){
const promiseA = fetch( "http://.../post/1" );
const promiseB = fetch ( "http://.../post/2" );
const [a, b] = await Promise.all([promiseA, promiseB]);
}
async function f(){
fer (let i of [1,2,31){
await someAsyncOperation();
}
console.log(" done");
}
f();
在上面的代码片段中,someAsyncOperation()
函数是一个异步操作,如果没有await
关键字修饰,程序将不会阻塞,在这个函数上继续执行。通过采用await关键字并暂停该函数的执行,等待异步函数完成,这样就可以利用回调函数的方式处理异步操作。
10、promise
Promise 是 ES6 引入的异步编程的新解决方案(回调地狱)。语法上 Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
Promise 是异步编程的一种解决方案,其实是一个构造函数,自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法。
把异步任务封装在Promise的对象里面,通过 resolve 和 reject 两个参数来改变状态,再通过 then 方法接收回调,一般通过 value 接收成功的回调,season 接收失败的回调。通过 then 方法的链式调用避免回调地狱。
11、箭头函数和function函数
function 函数,this 的指向随着调用环境的变化而变化的,指向其直接调用者,一般来说最通常的是window对象;
箭头函数中的 this 指向是固定不变的,一直指向的是定义函数的环境,并且this 的指向无法通过其他方式改变。
由于箭头函数没有自己的 this,所以当然也就不能用call( )、apply( )、bind( )这些方法去改变 this 的指向。箭头函数不能使用new生成构造函数,因为箭头函数没有 prototype,而 construct 在 prototype 里面。
function 存在变量提升,可以定义在调用语句后;function存在变量提升,可以定义在调用语句后;
12、数组常用方法
1、实现数组的增删改的方法 push: 向数组末尾增加内容 unshift: 向数组开始位置增加内容 pop: 删除数组最后一项 shift: 删除数组中的第一项 splice: 实现数组的删除、替换、增加 replace:字符串替换方法
2、数组的查询、拼接 includes: 检测数组中是否包含某一项 slice: 实现数组的截取 concat: 数组拼接 join: 把数组中的所有元素放入一个字符串
3、数组的排列或者排序 reverse:倒叙 sort: 小到大排序(大于10需要传参)
push做方法的时候是给从数组最后一个数据开始增加新的数据,但是做为返回值的时候输出的是该数据的长度;
pop做方法的时候是删除数组最后一个数据,做为返回值的时候是输出当前删除数据的值;
13、模拟数据的入栈和出栈
栈是一个先入后出的有序列表,可以看成特殊的队列,它限制了数据的插入和删除操作只能在线性表的同一端(栈顶)。
插入:在栈中插入操作称为入栈,最先放入栈中的元素在栈底,最后放入的元素在栈顶。
删除:在栈中删除操作称为出栈,最先删除的元素在栈顶,最后删除的元素在栈底。
14、获取 dom 元素
getElement选择器
1、通过ID获取 ——document.getElementById('id')
2、通过name属性 ——document.getElementsByName('name')
3、通过标签名 ——document.getElementsByTagName('div') 参数是是获取元素的标签名属性,不区分大小写 返回值是一个类数组,没有找到返回空数组
4、通过类名 ——document.getElementsByClassName('content') 返回值是一个类数组,没有找到返回空数组
querySelector选择器
1、获取第一个元素 ——document.querySelector('.animated')
可获取标签【div】,类【.content】,ID【#content】
2、获取一组元素 ——document.querySelectorAll('.animated') 返回类数组
15、Math常用方法
向上取整:Math.ceil(1.4) ==> 2
向下取整:Math.floor(1.4) ==> 1
随机数: Math.random( ) ==> [0,1)
16、...
...