课题
常见重要
需要有印象的
原始类型/基本类型:null、undefined、boolean、number、string、symbol、bigint
对象类型/引用类型/复杂/复合类型:
可调用/执行对象「函数」:function
标准普通对象:object
标准特殊对象:Array、Date、Math、RegExp、Error……
非标准特殊对象:Number、String、Boolean……
可调用/执行对象「函数」:function
标准特殊对象:Array、Date、Math、RegExp、Error……
非标准特殊对象:Number、String、Boolean……
说明:面试概率低、工作概率高
语法:parseInt、parseFloat、Number
null、undefined、0、NaN、空字符串结果是false、其他都是true
都字符串
String(内容)、内容.toString()
Number(数据)
数据 结果
null 0
undefined NaN
true/false 1/0
123/NaN 123/NaN
‘’/‘123’/‘123a’ 0/123/NaN
var result = 100 + true + 21.2 + null + undefined + “Tencent” + [] + null + 9 + false;
[] + [] // “”
[] + {} // “[object object]”
{} + [] // 0 对于编译器而言,代码块不会返回任何的值, 接着+[]就变成了一个强制转number的过程
true+true+true===3 // true
true-true // 0
true==1 // true
true===1 // false
9+“1” // 91
91-“1” // 90
[]==0 // true Number([]) 0
undefined 表示未定义,新定义的变量没有赋值就是undefined
null表示清空,当一个变量不用的时候,除了等待网页关闭销毁,也可以把它赋值为null。此时游览器会进行一个回收也就是清空内存。
了解1
typeof(null)会是一个object。最初这么设计的原因为:通常用作一个空引用一个空对象的预期,就像一个占位符。typeof的这种行为已经被确认为一个错误,虽然提出了修正,出于后兼容的目的,这一点已经保持不变。
了解2
null == undefined true
null === undefined false
因为 0.1 这样的数值用二进制表示你就会发现无法整除,
最后算下来会是0.0001100110011001…由于存储空间有限,位数是无限的,只能取近似。
代码:0.1 + 0.2 == 0.3 // false
代码:0.625 + 0.625 == 1.25 // true
进制转换规则:https://www.runoob.com/w3cnote/decimal-decimals-are-converted-to-binary-fractions.html
十进制整数 -> 转二进制
举例:(5).toString(2) // 结果 101
转换:除2反向取余
2| 5 --- 余1
------
2| 2 --- 余0
------
1
举例:(13).toString(2) // 结果 1101
转换:除2反向取余
2| 13 --- 余1
------
2| 6 --- 余0
------
2| 3 --- 余1
------
1
核对:https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fwww.pianshen.com%2Fimages%2F732%2Faf802c5be33b4934e6a6fec52a208734.png&refer=http%3A%2F%2Fwww.pianshen.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1642634303&t=293eccde3cc561a3ed2d38f8cf57eea9
十进制小数 -> 转二进制
举例:(0.625).toString(2) // 结果 0.101
转换:乘2正向取整
0.625*2=1.25======取出整数部分1
0.25*2=0.5========取出整数部分0
0.5*2=1==========取出整数部分1
举例:(0.7).toString(2) // 结果 0.101100110011001100110011 0011 ...
0.7*2=1.4========取出整数部分1
0.4*2=0.8========取出整数部分0
0.8*2=1.6========取出整数部分1
0.6*2=1.2========取出整数部分1
0.2*2=0.4========取出整数部分0
0.4*2=0.8========取出整数部分0
0.8*2=1.6========取出整数部分1
0.6*2=1.2========取出整数部分1
0.2*2=0.4========取出整数部分0
...
推荐解决: 浮点数转化成整数
实战使用:购物车结算时,商品价格(33.01)-优惠券价格(5),本应该是28.01,但是实际的结果是28.009999999999998
33.01 - 5 = 28.009999999999998
(33.01*100 - 5*100) / 100 = 28.01
es6 解决办法
es6 提供了 Number.EPSILON,这个值等于 2^-52 ,这个值非常非常小,在底层计算机已经帮我们运算好,并且无限接近 0,但不等于 0,这个时候我们只要判断误差值在这个范围内就可以说明是相等的。
function numbersequal(a,b){
return Math.abs(a-b)
四舍五入
采用四舍五入方法,取了一个 10 位小数
function numTofixed(num) {
if (typeof num == 'number') {
num = parseFloat(num.toFixed(10))
}
return num;
}
numTofixed(0.1 + 0.2);
// 0.3
使用第三方库
math.js
bignumber.js
....
栈:存 原始类型【名字】&【数据】、对象类型的【名字】&【堆地址】
堆:对象类型数据
原始类型:变量赋值,栈开辟内存直接存数据 -> 数据互补影响
对象类型:变量赋值,栈开辟内存,存放堆地址 -> 数据相互影响
切记切记切记:上课讲的形参也会出现传值、传地址问题
var num = 100
function fn(n) {
// var 形参 = 实参
// var n = num也就是100 原始类型 传值 不影响
n = 200
console.log(n) // 打印: 200
}
fn(num)
console.log(num) // 打印: 100
// --------------------------
var obj = { name: 'Jack' }
function fn(o) {
// var 形参 = 实参
// var o = obj 对象类型赋值 传地址 相互影响
o.name = 'Rose' //
console.log(o.name) // 打印:Rose
}
fn(obj)
console.log(obj.name) // 打印:Rose
栈 堆
obj:地址1 地址1:{ name: 'Jack改成Rose' }
fn函数里面的o变量:地址1 函数调用完毕o变量就销毁了
方法1:通过语法 Array.isArray()
方法2:instanceof
方法3:原型链(constructor)
…
Array.isArray([1, 2 ,3]) // true
[1, 2 ,3] instanceof Array // true
[1, 2 ,3].constructor === Array // true
Array.isArray('aa') // false
'aa' instanceof Array // false
'aa'.constructor === Array // false
临时变量法
var a = 1
var b = 2;
var temp = a
a = b
b = temp
加减法
var a = 1
var b = 2;
a = a+b // a = 1+2 = 3
b = a-b // b = 3-2 = 1
a = a-b // a = 3-1 = 2
解构赋值法 week3
var a = 1
var b = 2;
[a, b] = [b, a]
数组法
var a = 1
var b = 2
a = [a, b]
b = a[0]
a = a[1]
对象法
var a = 1
var b = 2
a = { a: b, b: a }
b = a.b
a = a.a
等等太多了
·数据操作:shift/unshift/pop/push
遍历数据: forEach/map/filter/find/findIndex
工作常用:concat/join/indexOf/includes
学习常用:reverse/splice
了解:sort
数据操作:shift/unshift/pop/push
学习常用:reverse/splice
了解:sort
方法1:通过es6新增的Set数据结构、和展开运算符去重 […new Set(重复数组)] week3
方法2:通过filter配合indexOf实现数组去重
方法3:定义空数组,通过forEach遍历重复的数组,通过indexOf判断当前值是否在数组中,不在就push
方法4:定义空数组,通过forEach遍历重复的数组,通过includes判断当前值是否在数组中,不在就push
方法5:利用对象的属性去重
方法6:…
// 方法1:通过es6新增的Set数据结构结果 [...new Set(重复数组)]
var arr = [1,2,3,2,3]
console.log([...new Set(arr)])
// 方法2:通过filter配合indexOf实现数组去重
var arr = [1,2,3,2,3]
var newarr = arr.filter(function(item, i) {
// i=0 item值是1 arr.indexOf(item) 返回0 真
// i=1 item值是2 arr.indexOf(item) 返回1 真
// i=2 item值是3 arr.indexOf(item) 返回2 真
// i=3 item值是2 arr.indexOf(item) 返回1 假
// i=4 item值是3 arr.indexOf(item) 返回2 假
return i === arr.indexOf(item);
})
console.log(newarr)
// 方法3:定义空数组,通过forEach遍历重复的数组,通过indexOf判断当前值是否在数组中,不在就push
var newarr = []
var arr = [1,2,3,2,3]
arr.forEach(function(item, i) {
if (newarr.indexOf(item) === -1)
{
newarr.push(item)
}
})
console.log(newarr)
// 方法4:定义空数组,通过forEach遍历重复的数组,通过includes判断当前值是否在数组中,不在就push
var newarr = []
var arr = [1,2,3,2,3]
arr.forEach(function(item, i) {
if (!newarr.includes(item))
{
newarr.push(item)
}
})
console.log(newarr)
// 方法5:利用对象的属性去重
var arr = [1,2,3,2,3]
var newarr = []
var obj = {}
arr.forEach(function(item) {
if (!obj[item]) {
obj[item] = item // 出现记录
newarr.push(item)
}
})
工作常用:数组、查找、替换、截取、大小、空格
split、replace、substr、toUpperCase/toLowerCase、trim、slice
学习:.length、lastIndexOf、repeat
说明: 通过 trim 去左右空格
留心:中间不能去
追问:JS如何去所有空格
解决:通过2021年 ES12新增语法replaceAll来解决
’ a b c ‘.replaceAll(’ ', ‘’)
解决:通过正则
’ a b c '.replace(/\s/g, ‘’)
长得像数组,能用少部分的属性 例如length 但是所有方法都不可以使用,在js中常见的伪数组有
document.qsa、arguments等等
面试: 调用当前方法的对象 也就是谁调用的打印的就是谁
举例:
function fn1() {}
fn1() // 最大全局变量window调用的
标签对象.事件类型 = 处理函数 // 标签对象触发后调用的
学习
普通函数 window
对象函数 对象本身
事件函数 事件源
定时器函数 window
箭头函数 父function中的this 父没有function就是window
自定义 call/apply/bind
构造函数 this === 实例化对象 === 公共空间/原型对象上方法中的this
其他:事件、构造函数、公共空间/原型对象上方法-优先使用普通函数写法,当需要明确改变this指向的时候再换箭头函数,
其他全不优先使用箭头函数。
好处:减少HTTP请求
原理:
1、监控滚动条滚动
2、获取总可视内容高度(可见视口高度+滚动条滚动高度)
3、获取所有图片
4、遍历步骤3(或这说:遍历伪数组)
5、在步骤4中判断,图片.offsetTop <= 步骤2 成立-修改src属性为data-src、失败-不管
6、节流防抖优化
留心:有时候图片高度获取怎么不到?
function lazyload()
{
// 1 每次获取最新的 可见视口 + 滚动的高度
// 2 获取所有图片
// 3 遍历
// 4 判断:当前图片的.offsetTop < 步骤1(可见视口 + 滚动的高度)
// 不成立-不管
// 成立-修改图片的src地址 改成真是的
// 1 每次获取最新的 可见视口 + 滚动的高度
var temp1 = window.innerHeight || document.documentElement.clientHeight // 兼容ie
var temp2 = document.body.scrollTop || document.documentElement.scrollTop // 兼容doctype
var maxShowHeight = temp1 + temp2
// 2 获取所有图片
var imgs = document.querySelectorAll('img')
// 3 遍历
imgs.forEach(function(item, index) { // item就是每一个图片标签对象
// 4 判断:当前图片的.offsetTop < 步骤1(可见视口 + 滚动的高度)
// 不成立-不管
// 成立-修改图片的src地址 改成真是的
// console.log(item, item.offsetTop , maxShowHeight)
if (item.offsetTop < maxShowHeight)
{
// item.src = item.src-real 切记非标签默认属性 不能直接点
item.src = item.getAttribute('src-real')
}
})
}
// TODO: 待后续进一步优化
// 首次
lazyload()
// 后续
// window.onscroll = lazyload() 错误 undefined
window.onscroll = lazyload
术语:事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流
学习:多个标签嵌套,事件触发-先检查当前标签,然后继续向上挨个检查祖先标签有没有事件 有就触发的现象就是事件流
DOM事件流分为三个阶段,分别为:
捕获阶段:事件从Document节点自上而下向目标节点传播的阶段;
目标阶段:真正的目标节点正在处理事件的阶段;
冒泡阶段:事件从目标节点自下而上向Document节点传播的阶段。
概念:利用事件冒泡机制处理指定一个事件处理程序,来管理某一类型的所有事件。
事件委托的好处:
匹配手机号:/^1\d{10}$/.test(数据)
匹配邮箱:/^\w{2,20}@\w{2, 20}.(com|org|cn|edu)$/.test(数据)
匹配中文:/1+$/.test(数据)
去所有空格:str.replaceAll(’ ', ‘’) 或 str.replace(/\s/g, ‘’)
购物车去非数字:str.replace(/\D/g, ‘’)
https://github.com/tc39/proposals/blob/main/finished-proposals.md
# 2015年 ES6
修饰符:let、const
解构赋值:let {键:变量名=默认值,...,键:变量名=默认值} = {键:值,....,键:值} !!!
字符串相关扩展:模板字符串
函数相关扩展:箭头函数!!!、函数形参默认值、rest参数 arguments
对象相关扩展:对象简写!!!、链判断操作符 ?. 空判断操作符??
数组相关扩展:find/findIndex/indexOf/includes
新增原始类型:symbol、bigint
-----------
// 重要 中等 中等 重要 重要
其他新增:展开运算符、新增数据结构、循环forof、module模块化、class类等等
# 2016年 ES7:Array.prototype.includes
# 2017年 ES8:Async functions
# 2018年 ES9:Promise.prototype.finally
# 2019年 ES10
# 2020年 ES11:String.prototype.matchAll、import()、BigInt、Promise.allSettled、
# 2021年 ES12:String.prototype.replaceAll、Promise.any、
# 2022年 ES13:
新增了哪些数据类型:symbol、bigInt
新增的数据类型属于哪一类:原始类型
数组如何去重:Set、filter+indexOf、forEach+indexOf、forEach+includes、对象
新增数据类型有哪些分别有什么用
Set数组去重
Map对象的键不限于字符串
面试题1:let和const区别
相同点-都是块级作用域/不能重复定义
不同点-let修改/const不能修改
面试题2:const真的不能修改吗
原始类型:不可以
对象类型:可以
原理:const不可以修改栈里面的地址,但是可以修改堆里面的数据
面试题3:暂时性死区
let前面的区域就是暂时性死区,必须先定义再使用
面试题4:let和const如何选
普遍用let、明确后期不改用const 例如Date、xhr、promise对象等
const d = new Date()
面试题5:var和let区别
var 函数作用域 可以重复定义 可以修改
let 块级作用域 不能重复定义 可以修改
fun.call(thisArg, param1, param2, ...)
fun.apply(thisArg, [param1,param2,...])
fun.bind(thisArg, param1, param2, ...)
是否立刻执行
返回值的区别
call
apply
bind
原型:js给每个函数分配的公共空间,减少内存占用
原型链:多个原型的集合,当调用对象的属性或方法时,先自身找,找不到去原型链上找,一直找到Object构造函数得原型链
1 搞obj空对象
2 给obj增加proto属性 并且指向 构造函数的.prototype
3 将 构造函数this中的数据 全部放到obj上
4 返回obj对象
// let Sym = fn
function Sym() {
this.a = 1;
this.b = 2;
this.c = 3;
}
// 第一种使用方式:当变量赋值用
let data1 = Sym;
console.log(data1); // 打印:函数本身
// 第二种使用方法:当函数用
let data2 = Sym();
console.log(data2); // 打印:undefined 但是window多了a、b、c三个属性
// 第三种使用方法:new关键词
let data3 = new Sym();
console.log("new关键词:", data3); // 打印:对象
/*
栈 堆
data1: 地址1 地址1:function Sym() { this.a = 1; this.b = 2; this.c = 3; }
data2:undefined 地址2:{a:1,b:2,c:3, __proto__:}
data3:地址2
*/
function _new(当前new的构造函数) {
// let obj = {};
// obj.__proto__ = 当前new的构造函数.prototype;
let obj = Object.create(当前new的构造函数.prototype); // !!!!
// obj.a = 1;
// obj.b = 2;
// obj.c = 3;
当前new的构造函数.call(obj);
return obj;
}
let data4 = _new(Sym);
// 需求:封装_new普通函数仿写new关键词效果
// 分析:_new普通函数里面做哪些时
// - 必须返回一个对象
console.log("通过_new仿写new:", data4);
https://juejin.cn/post/6844903805960585224##heading-2
// typeof 原始类型:除了null是object其他都是自身
// console.log(typeof null); // object
// console.log(typeof undefined);
// console.log(typeof true);
// console.log(typeof 123);
// console.log(typeof "sdafadsf");
// console.log(typeof Symbol());
// console.log(typeof 1111n);
// typeof 对象类型:除了function是function其他都是object
// 问题:你就无法根据不同类型做不同逻辑处理
// 比如:判断是数组 判断是null
// 解决:Array.isArray() 等等单独处理
// 然后:也可以统一处理
// 语法:Object.prototype.toString.call(数据)
// console.log(Object.prototype.toString.call(null));
// console.log(Object.prototype.toString.call(undefined));
// console.log(Object.prototype.toString.call(true));
// console.log(Object.prototype.toString.call(123));
// console.log(Object.prototype.toString.call("sdfdsf"));
// console.log(Object.prototype.toString.call(Symbol()));
// console.log(Object.prototype.toString.call(1111n));
// console.log(Object.prototype.toString.call(function () {}));
// console.log(Object.prototype.toString.call({}));
// console.log(Object.prototype.toString.call([]));
function getType(data) {
let result = Object.prototype.toString.call(data);
// console.log(result.substr(8, result.length - 1 - 8));
console.log(result.slice(8, -1));
}
getType(null);
getType(undefined);
getType(true);
getType(123);
getType("sdfdsf");
getType(Symbol());
getType(1111n);
getType(function () {});
getType({});
getType([]);
重要
Object.defineProperty() 三阶段才详细讲 现在【禁止】百度这个
Object.keys() 获取对象的所有键 返回数组
Object.values() 获取对象的所有值 返回数据
Object.create() 创建对象(特色 基于指定原型造对象 场景1:new原理优化,场景2:三阶段vue )
Object.assign() 合并对象
Object.prototype.constructor 所属构造函数
Object.prototype.toString() 转字符串
打开浏览器输入网址回车 -> 去DNS服务器找网址对应的IP地址 -> 找不到【无法访问此网站】 找到了【根据ip地址去请求服务器】 -> 服务器返回数据 -> 【浏览器解析】
脚下留心:浏览器其实返回的是index.html的数据,然后在解析的过程中,遇到link、script、img等 再次发送请求拿数据然后解析
面试:HTTP是超文本传输协议,规定了客户端和服务端如何通信,然后由两个部分组成分别是请求、响应
学习:就是一个规则,你必须按照这个规则才可以和后端交互拿数据
概念:超文本传输协议
作用:规定客户端和服务端通信技术
场景:网页、APP等
请求组成:请求行(地址/状态吗/请求方式)、请求头(ua、content-type、token、cookie)、请求体(接口参数)
响应组成:响应行(同上)、响应头(暂略)、响应体(接口数据调试错误)
HTTP动词(请求方式 method)
明确:form标签也就是w3c就是遵循http规则设计的 但是它的请求方式仅仅只有两种get、post 其他不支持
但是:我们目前也用get、post 等到三阶段就用n多
然后:面试就问你有哪些
种类:常用的get查询、post增加数据、put修改数据、delete删除数据
实际:java攻城狮就用get查询 增删改都用post
状态码
2xx 200 成功 201 成功并且服务器创建了新数据
3xx 301 站内跳转 302 站外跳转 304 浏览器缓存
4xx 400 你传递给后端的参数 401 密码错误 403 没有权限 404文件不存在 405 请求方式有误
5xx 500 服务器有误
请求头参数
ua、content-type、token、cookie
强制缓存:就是文件直接从本地缓存中获取,不需要发送请求。
响应头 Cache-Control : 86400
expires
协商缓存/对比缓存
在响应头部 Response Headers 中,有两种资源标识:
Last-Modified 和 Etag
https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fupload-images.jianshu.io%2Fupload_images%2F5727550-fd8811d7defb8956.png&refer=http%3A%2F%2Fupload-images.jianshu.io&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1650391279&t=0419cce2bf932f3c0e83594c4d177742
请求行/响应行:method、url、status
请求头headers、cookie、content-type、ua、if-none-match、if-modified-since
请求体:请求参数
响应头:content-type 告诉浏览器如何解析数 、etag、last-modified、 Cache-Control...
响应体:响应的数据
安全角度:post相对比get安全 原因get会在地址栏 因为在地址栏就有访问历史记录 post请求体不会存在来留痕
数据角度:post相对传输的数据比get多 get会受到不同浏览器地址栏长度限制,post服务器配置
上传图片:2M 一般只能上传png、jpg gif不允许
http超文本通讯协议 80
https也是超文本通讯协议 相对http更加安全 443
-day2
骨架屏就是在页面数据尚未加载前先给用户展示出页面的大致结构,直到请求数据返回后再渲染页面,补充进需要显示的数据内容。常用于文章列表、动态列表页等相对比较规则的列表页面。 很多项目中都有应用:ex:饿了么h5版本,知乎,facebook等网站中都有应用。
参考
https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/10/15/16676db12e4232bb~tplv-t2oaga2asx-watermark.awebp
回答:节流防抖都是用来进行项目优化,减少代码触发的频率,同时又不影响实际效果
节流:一段时间内只执行一次
防抖:一段时间内可重复执行,但是要把之前的取消掉
思路:事件中代码用定时器包起来,然后再前面加判断
问题:输入教主会执行 jiaozhu教主 这么多次
标签对象.oninput = function() {
console.log(1)
}
节流:一段时间内 1次
let t
标签对象.oninput = function() {
if (t) return
t = setTimeout(() => {
console.log(1)
t = null
}, 3000)
}
防抖:一段时间内可重复执行,但是要把之前的取消掉
let t
标签对象.oninput = function() {
if (t) clearTimeout(t)
t = setTimeout(() => {
console.log(1)
t = null
}, 3000)
}
简而言之,高阶函数是那些将其他函数作为参数或返回其他函数的函数。在高阶函数中作为参数传递的函数被称为回调。
高阶函数的优势:
现在 JavaScript 有一些内置的高阶函数,你可能已经在不知不觉中就使用它们了,例如 filter()、reduce()、sort() 和 forEach()。
/**
* 发送get异步请求
* @param {stirng} url 请求地址
* @param {object} params 请求参数,格式 {参数名:数据,....,参数名:数据}
* @param {function} callback 回调函数
* @param {object} headers 自定义请求头 {键:值, token: '数据', 'content-type': '数据'}
*/
function get(url, params, callback, headers = {}) {
// 一、创建xhr对象
const xhr = new XMLHttpRequest();
// 二、设置请求方式、请求地址
let temp = [];
for (let key in params) {
temp.push(`${key}=${params[key]}`);
}
xhr.open("get", `${url}?${temp.join("&")}`);
// 三、监听请求状态
xhr.onreadystatechange = () => {
// 判断返回
if (xhr.readyState === 4) {
// 判断状态
if (xhr.status === 200) {
// 获取数据
let res = JSON.parse(xhr.responseText);
// 逻辑处理
// console.log(res);
callback(res);
} else {
console.log(xhr.status);
}
}
};
// 四、发送
for (let key in headers) {
xhr.setRequestHeader(key, headers[key]);
}
xhr.send();
}
/**
* 发送get请求
* @param {stirng} url 请求地址
* @param {object} params 请求参数
* @param {object} headers 自定义请求头
* @returns
*/
function get(url, params, headers = {})
{
const p = new Promise((resolve, reject) => {
// 一、创建xhr对象
const xhr = new XMLHttpRequest
// 二、设置请求方式、请求地址
let temp = []
for (let key in params) {
temp.push(`${key}=${params[key]}`)
}
xhr.open('get', `${url}?${temp.join('&')}`)
// 三、监控请求状态
xhr.onreadystatechange = () => {
// 返回
if (xhr.readyState === 4)
{
// 状态码
if (xhr.status === 200)
{
// 获取数据
let res = JSON.parse(xhr.responseText)
// 逻辑处理
// console.log(res)
resolve(res)
} else {
// console.log(xhr.status)
reject(xhr.status)
}
}
}
// 四、发送请求
for (let key in headers) {
xhr.setRequestHeader(key, headers[key])
}
xhr.send()
})
return p // 后期谁调用get就会拿到promise对象 通过then就可以获取数据 实现不同的业务逻辑
}
-day3
概念:ES6异步编程解决方案
作用:常用于封装ajax异步请求
底层创建了Promise构造函数,并且给该构造函数绑定了then、catch、finally等原型方法,和reject、resolve、all、race等静态方法。
进行中、成功了、失败了
const p = new Promise((resolve, reject) => {
// 发送异步请求
// 默认触发的 所以是进行中状态
})
追问:为什么状态不可逆
回答:底层Promise构造函数中会判断当前是否是pending进行中状态,不是就会终止代码执行 所以不可逆
// 明确:底层Promise源码大概是这么写的
function Promise(callback) {
this.PromiseState = 'pending'
this.PromiseResult = undefined
const resolve = data => {
if (this.PromiseState != 'pending') return
this.PromiseState = 'fulfilled'
this.PromiseResult = data
}
const reject = error => {
if (this.PromiseState != 'pending') return
this.PromiseState = 'rejected'
this.PromiseResult = error
}
try {
// callback(参数1, 参数2)
// callback(() => {}, () => {})
callback(resolve, reject)
} catch(error) {
reject(error.toString())
}
}
// 然后:你写
const p = new Promise((resolve, reject) => {
resolve(数据1)
reject(数据2)
})
追问:状态之间怎么转换?
回答:通过promise的then机制,来实现状态切换
const p = new Promise((resolve, reject) => {
resolve(数据1)
})
p
.then(res => {
return Promise.resolve('失败的')
})
Promise.all( 数组里面是一个个Promise对象 ) 有一个失败就走失败
Promise.allSettled( 数组里面是一个个Promise对象 ) 没有失败
Promise.race( 数组里面是一个个Promise对象 ) 根据第一个最快返回的决定状态
Promise.any( 数组里面是一个个Promise对象 ) 有一个成功就是then 都失败 才是catch
跨域导致原因
概念:当请求一个url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
原因:浏览器安全策略/同源策略
后果:不能跨网站操作发送ajax请求、WEB存储等
跨域解决方案
前端
前端代理 http-proxy-middleware
谷歌插件
谷歌命令
jsonp
websocket
postMessage
留心:上述不管哪种方式仅自己可以使用
最终:开发就选上面其中一种,最终后端:cors法 原理响应头告诉浏览器任何人都可以使用;nginx 反向代理
会被加入到浏览器队列的代码称之为异步代码,例如 ajax、setTimeout/setInterval、Promise.then 等等,他们不按书写✍顺序执行打印结果的代码
按照书写顺序执行打印的代码称之为同步代码
async、await是generator的语法糖,通过async修饰function、await修饰promise,
底层将await后面的表达式会先执行一遍,再将await下一行代码加入到微任务中。
es6新增的语法,通过*号修饰函数,当调用函数的时候返回一个generator对象,通过next函数迭代获取函数内部的数据,当遇到yield就会暂停,再次写next才会继续
浏览器主进程,负责创建和销毁tab进程、负责交互前进后退、负责网页文件下载等
渲染进程:每个tab对应一个渲染进程,下面有GUI渲染线程、JS引擎线程、事件线程、定时器线程、异步请求线程
GPU进程:负责3D图绘制
第三方插件进程:负责第三方插件处理,例如跨域、广告拦截插件等
浏览器输入网址回车
去DNS服务器找网址对应的IP地址
根据IP地址加端口访问服务器软件
服务器返回数据
浏览器通过renderer是渲染进程处理,
其中GUI线程负责页面渲染、JS引擎线程负责解析JS代码
先看一个比喻
进程就是一个公司,每个公司都有自己的资源可以调度;公司之间是相互独立的;而线程就是公司中的每个员工(你,我,他),多个员工一起合作,完成任务,公司可以有一名员工或多个,员工之间共享公司的空间
什么是进程?
进程:是cpu分配资源的最小单位;(是能拥有资源和独立运行的最小单位)
什么是线程?
线程:是cpu调度的最小单位;(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程)
浏览器是多进程的
放在浏览器中,每打开一个tab页面,其实就是新开了一个进程,在这个进程中,还有ui渲染线程,js引擎线程,http请求线程等。 所以,浏览器是一个多进程的。
大家都在说js是单线程的,但是为什么要设计成单线程?
这主要和js的用途有关,js是作为浏览器的脚本语言,主要是实现用户与浏览器的交互,以及操作dom;这决定了它只能是单线程,否则会带来很复杂的同步问题。 举个例子:如果js被设计了多线程,如果有一个线程要修改一个dom元素,另一个线程要删除这个dom元素,此时浏览器就会一脸茫然,不知所措。所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变.
为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
JS是单线程执行程序代码,形成一个执行栈,挨个处理;
但是遇到特别耗费时间的代码 ,例如异步请求,事件等,
不会堵塞等待执行,而是交给浏览器其他线程处理后,再丢到执行栈中处理,从而保证还行效率
Event Loop即事件循环,
是指浏览器或Node的一种确保javaScript单线程运行时不会阻塞的一种机制,
也就是我们经常使用异步的原理。
种类:浏览器的Event Loop、Node.js中的Event Loop
浏览器输入网址服务器响应数据后,
浏览器会通过render进程开始解析工作
GUI线程负责页面渲染
JS引擎线程负责执行JS代码
遇到异步代码会交给其他线程处理,然后放到队列中,
事件循环主要是从队列中取出代码放到执行栈中交给js引擎线程处理
单词含义:I input 输入、 O output 输出
用户角度IO操作:鼠标键盘-是计算机输入信息,显示器-是输出设备
电脑角度IO操作:CPU、内存与其他设备之间数据转移过程就是IO操作,例如数据从磁盘读到内存,或者内存写到磁盘
编程角度IO操作:进程读取数据操作
宏任务:
整体代码(script)
定时器(setTimeout、setInterval)
I/O操作(DOM事件、AJAX异步请求)
setImmediate(node环境)
requestAnimationFrame(浏览器环境)
微任务
Promise.then catch finally
async/await(底层还是promise)
process.nextTick(node环境)
MutationObserver(浏览器环境)
算整体代码script:1宏n微
不算整体代码script:先n微,再1宏 -> n微,再1宏 -> n微
回答1:常用的2种,主要是cookie、h5存储;浏览器其实还有web sql、indexdb
H5
cookie
web sql
indexdb
回答2:答案在下面
性能角度:相对而言H5存储性能比COOKIE高
存储空间:H5单条数据5M左右、COOKIE单条数据4KB
生命周期:
cookie 自己设置,如果不设置浏览器关闭销毁
h5 localStorage 永久
h5 sessionStorage 窗口
回答3:
cookie. 1*1000*60*60*24*7
login.html
步骤1:存数据的时候 也额外存一个键叫 expires 记录时间
localStorage.setItem('uname', 'value')
localStorage.setItem('expires', (new Date).getTime() + 1*1000*60*60*24*7 )
member.html
步骤2:使用的时候增加判断
// 公式:当前使用时间 > 存储时间 (过期了)
// 举例:比如你是2月1号存
// 然后:你加了7天 2月8号过期
// 最后: 2月5号 > 2月8号 不成立 没过期
// 最后: 2月11号 > 2月8号 成立 过期
1、登录的时候存一个时间 当前时间+7天时间戳
localStorage.setItem('uname', 'value')
localStorage.setItem('expires', (new Date).getTime() + 1*1000*60*60*24*7 )
2、后期用的时候判断是否过期
会员中心
步骤1:登录按钮绑定点击事件
步骤2:事件处理函数中 获取表单数据,请求接口
步骤3:失败-弹框提示,成功-提示、存储、跳转
…
问题1:XMLHttprequest和JQ代码的区别
回答1:
相同点:都可以发送异步请求
不同点:JQ是基于XMLHttprequest封装了 1 更简单、 2 解决兼容问题等
问题2:$.get、$.ajax区别
回答2:一样,唯一区别$.ajax比$.get语法上更强
获取标签:CSS选择器、筛选选择器、过滤选择器等
操作标签:样式css方法、类addClass、属性attr/prop、内容val/html、节点append/remove
事件:$().事件类型() $().on(事件类型,子,处理函数)
异步请求:$.get、$.post、$.ajax
动画:$.animate()
网页加载完毕:$(function(){}) (留心:和window.onload区别
对象相互转换:$()[索引] 或者 $(JS标签对象
配置SSH ssh-keygen
用户配置 git config global user.name/email ''
获取代码
git clone 仓库地址
git init
git remote add origin 仓库地址
git remote remove origin 仓库地址
增删改:git add . / commit / push
周边(忽略文件) .gitignore
周边(代码冲突) git pull git status
周边(日志回滚 git log 、 git reset
周边(分支) git branch / git checkout ...
周边(其他) git stash、git tag、git pull、git fetc、git merge
git flow
功能分支 -> 合并到dev分支
dev分支 -> 合并到release分支
release分支 -> 合并到master分支
1 pull
2 status、手动解决
3 重新add/commit/push
基于master创建hotfix分支,
解决了再 push 并 merge 到 master、dev 上
git checkout -b 分支名 分支名
编辑器
sourcetruee
等等
git是分布式、svn是集中式
svn相对容易冲突
等
\u4e00-\u9fa5 ↩︎