1.重绘和回流 1.浏览器是如何进行界面渲染的 解析 (Parser) HTML, 生成DOM树(DOM Tree) 同时解析 (Parser) CSS, 生成样式规则(Style Rules) 根据DOM树和样式规则, 生成渲染树(Render Tree) 进行布局 Layout(回流/重排):根据生成的渲染树, 得到节点的几何信息 (位置,大小) 进行绘制 Painting(重绘): 根据计算和获取的信息进行整个页面的绘制 Display: 展示在页面上 2.重绘和回流(重排) 回流(重排): 当 Render Tree 中部分或者全部元素的尺寸、结构、布局等发生改变时,浏览器就会重新渲染部分或全部文档的过 程称为 回流 重绘: 由于节点(元素)的样式的改变并不影响它在文档流中的位置和文档布局时(比如:color、background-color、 outline等), 称为重绘 重绘不一定引起回流,而回流一定会引起重绘 3.会导致回流(重排)的操作: 页面的首次刷新 浏览器的窗口大小发生改变 元素的大小或位置发生改变 改变字体的大小 内容的变化(如:input框的输入,图片的大小) 激活css伪类 (如::hover) 脚本操作DOM(添加或者删除可见的DOM元素) 简单理解影响到布局了,就会有回流 1 2 3 4 5 6 let a = document .body .style a.padding = '2px' a.border = '1px solid red' a.color = 'red' a.backgroundColor = 'red' a.fontSize = '16px'
2.两个定时器对比 间歇函数 重复执行 首次延迟执行 延迟函数 只执行1次 3.JS执行机制 同步: 前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的
异步: 你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情
同步: 同步任务都在主线程上执行,形成一个执行栈 异步: 任务/消息队列, JS异步通过回调函数实现, (事件、资源加载load、error、定时器) 异步任务相关添加到任务队列中 (任务队列也称为消息队列) 先执行执行栈的同步任务 异步任务放入任务队列中 同步任务执行完 系统按次序读取异步任务, 异步任务结束等待状态, 进入执行栈执行 主线程重复获得任务、执行任务、再获取任务、再执行 这种机制称事件循环event loop 4.作用域链 作用域链本质上是底层的变量查找机制 在函数被执行时,会优先查找当前函数作用域中查找变量 如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域 嵌套关系的作用域串联起来形成了作用域链 相同作用域链中按着从小到大的规则查找变量 子作用域能够访问父作用域,父级作用域无法访问子级作用域 1 2 3 4 5 6 7 8 9 10 11 12 13 14 let a = 1 let b = 2 function say ( ) { let a = 11 console .log (a) function say1 ( ) { a = 22 console .log (a) } say1 () } say ()
5.闭包 一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域 简单理解:闭包 = 内层函数 + 外层函数的变量 闭包作用:封闭数据,提供操作,外部也可以访问函数内部的变量 闭包应用:实现数据的私有: 比如统计函数调用次数, 函数调用一次, 就++ 闭包可能引起内存泄漏 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function say ( ) { let num2 = 10 function say1 ( ) { console .log (num2) } return say1 } const fun = say ()fun () function fnn ( ) { let num4 = 0 function fnn1 ( ) { num4++ console .log (num4) } return fnn1 } const funs = fnn ()funs ()funs ()
6.this指向 普通函数this指向调用者 window 箭头函数不会创建this 是上一层作用域链的this 7.构造函数实例化过程 构造函数是创建对象的函数 可快速创建多个类似对象 命名大写开头 通过new操作 通过new关键字调用行为叫: 实例化 构造函数无需return 如果返回就是新对象 且返回的值无效 new Object new Data 都是实例化构造函数 没有参数时可省略() 不建议 创建空对象
this指向空对象
执行函数代码 修改this 添加属性
返回新对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function Pig (uname, age ) { this .name = uname this .ages = age } console .log (new Pig ('佩奇' , 18 ))console .log (new Pig ('乔治' , 15 ))function Fn (name, age ) { this .n = name this .a = age } console .log ('小城' , 18 )
8.基本包装类型也有属性和方法 为啥? 为什么字符串可以使用length 为什么有属性? 不是简单数据类型吗?
因为JS底层把基本数据类型包装成了复杂数据类型
1 2 3 4 5 6 7 let num1 = '你好' console .log (num1.length )let num2 = 10 console .log (num2.toFixed (2 )) let num3 = new String ('你好啊' ) console .log (num3.length )
9.内置构造函数方法 keys(获取所有属性名) values(获取所有属性值) assign(对象的拷贝) reduce(reduce累计器 返回累计处理的结果) 10.数组常见方法 join(‘/‘) 把数组根据分隔符转换为字符串 find(查找对象符合条件的数据返回其对象) every(每个是否符合条件 都符合返回true 不符合返回false) some(只要有一个符合 就返回true) 静态方法from() 伪数组转换真数组 11.字符串常见方法 split(,) 把字符串转换为数组 和join()相反
字符串的截取: n2.substring(开始, 的索引号 不包含想要截取的部分)
startsWith判断是否以某个字符开头, 返回布尔值
includes 判断字符串是否包含在字符串里, 返回布尔值
Number数字 保留小数方法: toFixed(), 不写会四舍五入, 写则保留小数
数字转换为字符串方法: String(1)、1.toString()
12. 原型对象prototype 公共的方法写在原型对象里 原型是一个对象 把不变的方法定义在prototype对象上 可挂载函数 实例化不会多次创建原型的函数 节约内存 构造函数的this指向实例化对象 原型对象的this也指向实例化的对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function fn (a, b ) { this .a = a this .b = b this .c = function ( ) { console .log ('你好' ) } } let n1 = new fn ('小城' , 10 )let n2 = new fn ('小东' , 20 )console .log (n1 == n2) console .log (n1.c == n2.c ) fn.prototype .d = function ( ) { console .log ('你好啊' ) console .log (this ) } console .log (fn.prototype )n1.d () n2.d () console .log (n1.d == n2.d )
13.constructor属性 构造函数prototype指向constructor, 然后constructor再指向回来 1 2 3 4 5 function fn ( ) {} console .log (fn.prototype )console .log (fn.prototype .constructor == fn)
constructor使用场景 1 2 3 4 5 6 7 8 9 10 11 12 13 14 fn.prototype = { constructor : fn, n1 : function ( ) { console .log ('你好' ) }, n2 : function ( ) { console .log ('你好1' ) } } console .log (fn.prototype )console .log (fn.prototype .constructor )
14.对象原型_proto__ 对象都有proto属性 指向原型对象的prototype 之所以对象可使用原型的方法 是因为有__proto__原型的存在 1 2 3 4 5 6 7 8 function fn ( ) {}let n1 = new fn ()console .log (n1)console .log (n1.__proto__ == fn.prototype )console .log (n1.__proto__ .constructor == fn)
15.原型继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 function fn1 ( ) {} let n1 = new fn1 ()console .log (n1)function fn2 ( ) {} let n2 = new fn2 ()console .log (n2)let fn3 = { a : 1 , b : 2 } n1.prototype = fn3 console .log (n1)console .log (n1.prototype )n1.prototype .constructor = fn1 console .log (n1.prototype )n1.prototype .say = function ( ) { console .log ('你好啊' ) } console .log (n1)n2.prototype = fn3 console .log (n2)function fns ( ) { this .a = 1 this .b = 2 } n1.prototype = new fns () console .log (n1)n2.prototype = new fns () console .log (n2)n1.prototype .constructor = fn1 n2.prototype .constructor = fn2
16.原型链与instanceof运算符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 function fn ( ) {} let n1 = new fn ()console .log (n1.__proto__ == fn.prototype )console .log (fn.prototype .__proto__ == Object .prototype )console .log (Object .prototype .__proto__ ) console .log (n1 instanceof fn) console .log (n1 instanceof Object ) console .log (n1 instanceof Array ) console .log ([1 ,2 ,3 ] instanceof Array ) console .log (Array instanceof Object )
17.浅拷贝 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 let n1 = { name : '小城' , age : 18 } let n2 = n1console .log (n2)n2.age = 20 console .log (n2)console .log (n1) let n3 = {...n1}console .log (n3) n3.age = 21 console .log (n3) console .log (n1) let n4 = {}Object .assign (n4, n1)n4.age = 100 console .log (n4)console .log (n1)let obj = { name : '小东' , age : 10 , sex : { num : 1 } } let n5 = {}Object .assign (n5, obj)n5.age = 100 n5.sex .num = 2 console .log (n5)console .log (obj)
18.深拷贝 1.浅拷贝和深拷贝只针对引用类型 深拷贝: 拷贝的是对象 不是地址, 通过递归实现深拷贝 2.深拷贝如何实现的? 深拷贝拷贝的新对象不会影响旧对象 实现深拷贝用递归函数 当普通对象拷贝没问题 但遇到有数组的再次调用递归函数就ok 当用的是对象形式 再次利用递归函数解决 if判断 先Array在Object 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 let obj = { a : 1 , b : 2 , c : ['苹果1' , '苹果2' ], d : { name : '小' } } let n1 = {}function f (x, y ) { for (let k in y) { if (y[k] instanceof Array ) { x[k] = [] f (x[k], y[k]) } else if (y[k] instanceof Object ) { x[k] = {} f (x[k], y[k]) } else { x[k] = y[k] } } } f (n1, obj) n1.a = 10 n1.c [0 ] = '香蕉' n1.d .name = '大' console .log ([1 ,2 ] instanceof Object )console .log (n1)console .log (obj)let n2 = _.cloneDeep (obj)n2.d .name = '大' console .log (n2)console .log (obj)console .log (JSON .stringify (obj))let n3 = JSON .parse (JSON .stringify (obj))n3.d .name = '大' console .log (n3)
19.this指向的三种方法 1.call和apply的区别 都能调用函数
都能改变this指向
参数不一样 apply传递必须数组
严格模式下指向undefined
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 let n1 = {name : '小城' }function fn (x, y ) { console .log (this ) console .log (x + y) } fn.call (n1, 1 , 2 ) fn.apply (n1, [10 , 20 ]) let arr = [1 ,2 ,3 ]let max = Math .max .apply (Math , arr)let min = Math .min .apply (Math , arr)console .log (max, min)console .log (this ) function fn1 ( ) { console .log (this ) } fn1 ()window .setTimeout (function ( ) { console .log (this ) }, 100 ) document .querySelector ('button' ).addEventListener ('click' , function ( ) { console .log (this ) }) let obj = { a : function ( ) { console .log (this ) } } obj.a () document .querySelector ('button' ).addEventListener ('click' , () => { console .log (this ) }) function fn2 ( ) {} fn2.prototype .say = () => { console .log (this ) } let n2 = new fn2 ()n2.say ()
2. this.bind方法 call、apply、bind的区别: 相同点: 都可改变this指向 call和apply会调用函数 可修改this bind不会调用函数 可修改this call和apply传递参数不一样 call是(1,2) apply是数组形式 3.应用场景: call 想要调用函数 还要传递参数 apply 想要调用函数 并且要改变this 还想传递数组 bind 不想调用函数 只想改变this 比如改变定时器的this 普通参数用call 数组用apply 改变this不想调用用bind 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let obj = {name : '小城' }function fn ( ) { console .log (this ) } fn.bind () let n1 = fn.bind (obj)console .log (n1) n1 () let btn = document .querySelector ('button' )btn.addEventListener ('click' , function ( ) { this .disabled = true setTimeout (function ( ) { this .disabled = false }.bind (this ), 300 ) })
20.防抖以及底层实现 在点击事件内的时间 频繁触发事件 只执行一次 比如王者的回城 被打断就会重来 使用场景: 搜索框输入联想词、手机号邮箱验证 可减少服务器请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 let box = document .querySelector ('.box' )let num = 1 function fn ( ) { box.innerHTML = num++ } function fn1 (fn, time ) { let t = 0 return function ( ) { if (t) clearTimeout (t) t = setTimeout (function ( ) { fn () }, time) } } box.addEventListener ('mousemove' , fn1 (fn, 500 ))
21.节流以及底层实现 在3秒内频繁触发事件只执行一次 比如王者的技能冷却期间是无法释放的 使用场景: 鼠标mousemove、页面尺寸缩放resize、滚动条scroll滚动… 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 let box = document .querySelector ('.box' )let num = 1 function fn ( ) { box.innerHTML = num++ } function fn1 (fn, time ) { let t = 0 return function ( ) { if (!t) { t = setTimeout (function ( ) { fn () t = null }, time) } } } box.addEventListener ('mousemove' , fn1 (fn, 200 )) let n1 = null n1 = setTimeout (function ( ) { clearTimeout (n1) console .log (n1) }, 200 )
22.Ajax接口请求过程 用户向服务器提交数据 通过Ajax载体发起get请求 服务器处理Ajax请求 然后响应get请求 在通过Ajax处理返回给用户 什么是Ajax? 网页中利用XMLHttpRequest对象和服务器的数据交互方式 通信协议 服务器名称 具体存放位置 http:// baidu.com /index.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 $(function ( ) { $('.btn2' ).click (function ( ) { $.post ('http://ajax-api.itheima.net/api/books' , {bookname : '水浒传' , author : '出版社' , publisher : '施耐庵' }, function (res ) { console .log (res) }) }) }) $(function ( ) { $('.btn4' ).click (function ( ) { $.ajax ({ type : 'POST' , url : 'http://ajax-api.itheima.net/api/books' , data : { bookname : '西游记' , author : '人民出版社' , publisher : '孙悟空' }, success : function (res ) { console .log (res) } }) }) })
23.HTTP状态码 200 成功 只要有2 就是成功 302 重定向 直接修改网址 把a网址改为b网址 400 参数错误 401 未登录/未认证 没登录但输入了后台页 403 无权限 跟视频会员一样 404 请求行错误 url错误/请求method错误 413 文件上传限制 文件过大 24.restful接口规范 这三个一定是post请求体传参: post 新增数据、put 全局更新数据、patch 局部更新数据 delete 删除数据 请求体get/post都可以传参 Get和Post请求区别: 1.传参方式不一样 get在请求行传参 post在请求体载荷传参 参数会进行切片处理 切多少取决于数据和带宽 204预检请求: 遇到请求体一次发不完 先通知服务器接收 在内存里准备空间 然后建立数据流传参 2.传参速度 get更快 直接url查找 3.传参数据大小不一样 get有大小限制 2~4kb post无上限 4.安全性 post更高 25.Ajax的XMLHttpRequest原理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 let xhr = new XMLHttpRequest ()xhr.open ('get' , 'http://hmajax.itheima.net/api/news' ) xhr.send () xhr.addEventListener ('loadend' , function ( ) { console .log (xhr.response ) }) let xhr1 = new XMLHttpRequest ()xhr1.open ('get' , 'http://hmajax.itheima.net/api/city?pname=湖北省' ) xhr1.send () xhr1.addEventListener ('loadend' , function ( ) { console .log (xhr1.response ) console .log (JSON .parse (xhr1.response )) }) let xhr2 = new XMLHttpRequest ()xhr2.open ('post' , 'http://hmajax.itheima.net/api/register' ) xhr2.setRequestHeader ('Content-Type' , 'application/json; charset=utf-8' ) xhr2.send ('{"username": "admin123", "password": "123456"}' ) xhr2.addEventListener ('loadend' , function ( ) { console .log (xhr2.response ) })
26.Promise作用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 let xhr1 = new XMLHttpRequest ()xhr1.open ('get' , 'http://123.57.109.30:3999/api/categoryfirst' ) xhr1.send () xhr1.onload = function ( ) { console .log ('一级' ) console .log (JSON .parse (xhr1.response )) let xhr2 = new XMLHttpRequest ()xhr2.open ('get' , 'http://123.57.109.30:3999/api/categorySecond?firstId=621' ) xhr2.send () xhr2.onload = function ( ) { console .log ('二级' ) console .log (JSON .parse (xhr2.response )) let xhr3 = new XMLHttpRequest ()xhr3.open ('get' , 'http://123.57.109.30:3999/api/categoryThird?secondId=622' ) xhr3.send () xhr3.onload = function ( ) { console .log ('三级' ) console .log (JSON .parse (xhr3.response )) } } } console .log (666 )let pro = new Promise ((resolve, reject ) => { setTimeout (function ( ) { reject (2 ) }, 500 ) }) pro.then (res => { console .log (res) }).catch (error => { console .log (error) })
27.Promise工作原理 Promise是什么? 是ES6新增的构造函数 Promise作用: 解决回调地狱 1.Promise应用场景/原理 Promise对象有三种状态: pending 进行中(默认状态) 所以一旦创建Promise 里面代码会立即执行 fuifilled 已完成 rejected 已失败 Promise相当于是一个容器 把异步代码放入容器中 2.Promise对象状态只有两种状态: 调用resolve()方法时: 从pending变为fuifilled 调用reject()方法时: 从pending变为rejected 状态只能改变一次 不管成功/失败 都会有一个数据结果 3.Promise状态发生改变后 在任何时候都可以获取结果 怎么拿Promise结果呢? Promise实例的then方法获取成功结果 Promise实例的catch方法获取失败结果 4.Promise在创建实例时 里面代码会立即执行 Promise自己是同步的 只有then方法才是异步的 28.Promise使用链式语法解决回调地狱 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 function fn (url ) { return new Promise ((resolve, reject ) => { let xhr = new XMLHttpRequest () xhr.open ('get' , url) xhr.send () xhr.onload = function ( ) { let res = JSON .parse (xhr.response ) resolve (res) } }) } let p1 = fn ('http://123.57.109.30:3999/api/categoryfirst' )let p2 = fn ('http://123.57.109.30:3999/api/categorySecond?firstId=622' )let p3 = fn ('http://123.57.109.30:3999/api/categoryThird?secondId=621' )p1.then (res => { console .log (res) return p2 }) .then (res => { console .log (res) return p3 }) .then (res => { console .log (res) })
29.JS异步微任务-宏任务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 console .log (1 )setTimeout (() => { console .log (2 ) new Promise ((resolve, reject ) => { console .log (3 ) resolve () console .log (4 ) }).then (res => { console .log (5 ) }) console .log (6 ) }, 0 ) console .log (7 )new Promise ((resolve, reject ) => { console .log (8 ) resolve () console .log (9 ) }).then (res => { console .log (10 ) })
30.跨域的概念 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
31. 32. 33. 34. 35.