当前位置: 首页 > news >正文

网站定制开发与模版婚介网站建站

网站定制开发与模版,婚介网站建站,怎么样做手机网站,在线设计响应式网站前端 进阶一、HTMLmetaviewport[题] meta标签#xff0c;实现页面自动刷新/跳转二、CSSCSS选择器CSS选择器匹配原理CSS优先级 / 权重可继承 / 不可继承属性盒模型offsetWidth、clientWidth、scrollWidth**box-sizing属性BFC块级格式化上下文position定位实现水平居中实现垂直居… 前端 进阶一、HTMLmetaviewport[题] meta标签实现页面自动刷新/跳转二、CSSCSS选择器CSS选择器匹配原理CSS优先级 / 权重可继承 / 不可继承属性盒模型offsetWidth、clientWidth、scrollWidth**box-sizing属性BFC块级格式化上下文position定位实现水平居中实现垂直居中垂直居中一个实现一个浮动元素垂直居中display属性CSS3新特性flex弹性盒布局CSS创建一个三角形实现一个广告跑马灯效果实现一个文字颜色跑马灯效果实现一个简单的幻灯片效果实现一个手机版淘宝商品并列显示设计一个满屏品字布局CSS常见的兼容性问题三、JavaScriptJavaScript 执行机制【题】异步加载JS的方式?事件循环 Event Loop【题】异步代码执行顺序解释一下什么是 Event Loop 【题】小试牛刀JS内置数据类型1、数据类型2、数据类型存储【题】小试牛刀【题】 null 和 undefined 的区别3、数据类型检测【题】实现一个数据类型判断方法4、数据类型转换【题】类型转换经典面试题JS各种运算符数组1、创建数组2、增删查改方法3、排序方法4、转换方法5、迭代方法6、数组最高/最低数值7、判断是否数组8、ES6新扩展【题】js实现随机选取10–100之间的10个数字存入一个数组并排序?【题】计算数组元素总和[题] 数组扁平化[题] 数组去重对象对象的创建字符串字符串的常用方法浅拷贝 / 深拷贝浅拷贝深拷贝区别[题] js实现一个clone函数 | 深拷贝Thisapply/call/bind变量提升执行上下文作用域闭包[题] 大厂面试循环输出问题new操作符原型 / 原型链继承面向对象事件机制捕获 / 冒泡事件对象阻止冒泡 / 捕获事件模型事件代理又叫事件委托节流与防抖模块化Promise[题] 手写实现promiseasync/await[题] 小试牛刀浏览器HTTP前端性能优化定位问题技术选型NetWork优化方案兼容、适配问题[题] 移动端 1px 问题TypeScript类型接口(interface)Vue2 Vue3Vue中data为什么要返回函数Vue3新特性 Vue2 Vue3 响应原理对比Vue3有了解过吗能说说跟Vue2的区别吗小试牛刀1. JS中 ?? 与 || 的区别一、HTML meta !DOCTYPE html !--H5标准声明使用 HTML5 doctype不区分大小写-- head lang”en” !--标准的 lang 属性写法-- meta charset’utf-8′ !--声明文档使用的字符编码-- meta http-equiv”X-UA-Compatible” content”IEedge,chrome1″/ !--优先使用 IE 最新版本和 Chrome-- meta name”description” content”不超过150个字符”/ !--页面描述-- meta name”keywords” content””/ !-- 页面关键词-- meta name”author” content”name, emailgmail.com”/ !--网页作者-- meta name”robots” content”index,follow”/ !--搜索引擎抓取-- meta name”viewport” content”initial-scale1, maximum-scale3, minimum-scale1, user-scalableno” !--为移动设备添加 viewport-- meta name”apple-mobile-web-app-title” content”标题” !--iOS 设备 begin-- meta name”apple-mobile-web-app-capable” content”yes”/ !--添加到主屏后的标题iOS 6 新增 是否启用 WebApp 全屏模式删除苹果默认的工具栏和菜单栏-- meta name”apple-itunes-app” content”app-idmyAppStoreID, affiliate-datamyAffiliateData, app-argumentmyURL” !--添加智能 App 广告条 Smart App BanneriOS 6 Safari-- meta name”apple-mobile-web-app-status-bar-style” content”black”/ meta name”format-detection” content”telphoneno, emailno”/ !--设置苹果工具栏颜色-- meta name”renderer” content”webkit” !-- 启用360浏览器的极速模式(webkit)-- meta http-equiv”X-UA-Compatible” content”IEedge” !--避免IE使用兼容模式-- meta http-equiv”Cache-Control” content”no-siteapp” / !--不让百度转码-- meta name”HandheldFriendly” content”true” !--针对手持设备优化主要是针对一些老的不识别viewport的浏览器比如黑莓-- meta name”MobileOptimized” content”320″ !--微软的老式浏览器-- meta name”screen-orientation” content”portrait” !--uc强制竖屏-- meta name”x5-orientation” content”portrait” !--QQ强制竖屏-- meta name”full-screen” content”yes” !--UC强制全屏-- meta name”x5-fullscreen” content”true” !--QQ强制全屏-- meta name”browsermode” content”application” !--UC应用模式-- meta name”x5-page-mode” content”app” !-- QQ应用模式-- meta name”msapplication-tap-highlight” content”no” !--windows phone 点击无高亮 设置页面不缓存-- meta http-equiv”pragma” content”no-cache” meta http-equiv”cache-control” content”no-cache” meta http-equiv”expires” content”0″viewport meta nameviewport contentwidthdevice-width,initial-scale1.0,minimum-scale1.0,maximum-scale1.0,user-scalableno /width 设置viewport宽度为一个正整数或字符串‘device-width’ device-width 设备宽度 height 设置viewport高度一般设置了宽度会自动解析出高度可以不用设置 initial-scale 默认缩放比例初始缩放比例为一个数字可以带小数 minimum-scale 允许用户最小缩放比例为一个数字可以带小数 maximum-scale 允许用户最大缩放比例为一个数字可以带小数 user-scalable 是否允许手动缩放 [题] meta标签实现页面自动刷新/跳转 实现一个类似 PPT 自动播放的效果 meta http-equivRefresh content5; URLpage2.html // 5s 之后自动跳转到同域下的 page2.html 页面另一种场景比如每隔一分钟就需要刷新页面的大屏幕监控 meta http-equivRefresh content60二、CSS CSS选择器 id选择器#box类选择器 (.myclassname)标签选择器 (div)属性选择器 a[rel“external”]后代选择器li a子选择器ul li相邻选择器(h1 p)层次选择器p~ul选择前面有p元素的每个ul元素伪类选择器a:hover, li:nth-child伪元素选择器群组选择器h1, p, span通配符选择器* 伪类选择器 :link 选择未被访问的链接 :visited选取已被访问的链接 :active选择活动链接 :hover 鼠标指针浮动在上面的元素 :focus 选择具有焦点的 :first-child父元素的首个子元素 es6: :first-of-type 父元素的首个元素 :last-of-type 父元素的最后一个元素 :only-of-type 父元素的特定类型的唯一子元素 :only-child 父元素中唯一子元素 :nth-child(n) 选择父元素中第N个子元素 :nth-last-of-type(n) 选择父元素中第N个子元素从后往前 :last-child 父元素的最后一个元素 :root 设置HTML文档 :empty 指定空的元素 :enabled 选择被禁用元素 :disabled 选择被禁用元素 :checked 选择选中的元素 :not(selector) 选择非 selector 元素的所有元素伪元素选择器 :first-letter 用于选取指定选择器的首字母 :first-line 选取指定选择器的首行 :before : 选择器在被选元素的内容前面插入内容 :after : 选择器在被选元素的内容后面插入内容属性选择器 [attribute] 选择带有attribute属性的元素 [attributevalue] 选择所有使用attributevalue的元素 [attribute~value] 选择attribute属性包含value的元素 [attribute|value]选择attribute属性以value开头的元素 es6: [attribute*value]选择attribute属性值包含value的所有元素 [attribute^value]选择attribute属性开头为value的所有元素 [attribute$value]选择attribute属性结尾为value的所有元素CSS选择器匹配原理 采取 从右向左 的顺序来匹配选择器。从最具体的选择器开始 CSS优先级 / 权重 三条规则 权重不同的样式规则作用于同一元素时权重高的规则生效权重相同的样式规则作用于同一元素时后声明的规则生效直接作用于元素的样式规则优先级高于从祖先元素继承的规则 CSS 权重等级优先级从上往下由高到低 !important 大于一切 10000内联样式 外联样式 1000ID选择器 100类选择器 伪类选择器 属性选择器 10标签选择器 伪元素选择器 1通配选择器 后代选择器 兄弟选择器 0 div{ /*1*/ } .class1{ /*10*/ } #id1{ /*100*/ } #id1 div{ /*100 1*/ }可继承 / 不可继承属性 在css中继承是指的是给父元素设置一些属性后代元素会自动拥有这些属性 可继承的样式 1、字体系列属性 font:组合字体 font-family:规定元素的字体系列 font-weight:设置字体的粗细 font-size:设置字体的尺寸 font-style:定义字体的风格 font-variant:偏大或偏小的字体2、文本系列属性 text-indent:文本缩进 text-align:文本水平 line-height:行高 word-spacing:增加或减少单词间的空白 letter-spacing:增加或减少字符间的空白 text-transform:控制文本大小写 direction:规定文本的书写方向 color:文本颜色3、元素可见性 visibility: hidden;4、表格布局属性 caption-side:定位表格标题位置 border-collapse:合并表格边框 border-spacing:设置相邻单元格的边框间的距离 empty-cells:单元格的边框的出现与消失 table-layout:表格的宽度由什么决定5、列表属性 list-style-type:文字前面的小点点样式 list-style-position:小点点位置 list-style:以上的属性可通过这属性集合6、引用 quotes:设置嵌套引用的引号类型7、光标属性 cursor: 箭头可以变成需要的形状特殊的几点 a 标签的字体颜色不能被继承 h1-h6标签字体的大下也是不能被继承的 不可继承的样式 display文本属性vertical-align、text-decoration盒子模型的属性宽度、高度、内外边距、边框等背景属性背景图片、颜色、位置等定位属性浮动、清除浮动、定位position等生成内容属性content、counter-reset、counter-increment轮廓样式属性outline-style、outline-width、outline-color、outline页面样式属性size、page-break-before、page-break-after 盒模型 标准(W3C)盒模型元素总宽度 width padding border margin 怪异(IE)盒模型元素总宽度 width margin .box{width: 200px;height: 200px;padding: 20px;border: 10px solid #000;margin: 15px;box-sizing: content-box; } // box宽度 200 20*2 10*2 260px;.box{width: 200px;;height: 200px;padding: 20px;border: 10px solid #000;margin: 15px;box-sizing: border-box; } // box宽度 200px;offsetWidth、clientWidth、scrollWidth** offsetWidth/offsetHeight content padding borderclientWidth/clientHeight content paddingscrollWidth/scrollHeight content padding border 滚动条 box-sizing属性 默认为content-box box-sizing: content-box; 默认的标准(W3C)盒模型元素效果box-sizing: border-box; 触发怪异(IE)盒模型元素的效果box-sizing: inherit; 继承父元素 box-sizing 属性的值 BFC块级格式化上下文 BFC(Block Formatting Context)块级格式化上下文是一个独立的渲染区域让处于 BFC 内部的元素与外部的元素相互隔离使内外元素的定位不会相互影响 触发条件 (以下任意一条) float的值不为noneoverflow的值不为visibledisplay的值为table-cell、tabble-caption和inline-block之一position的值不为static或则releative中的任何一个IE下, 可通过zoom:1 触发 BFC布局与普通文档流布局区别: 普通文档流布局: 浮动的元素是不会被父级计算高度非浮动元素会覆盖浮动元素的位置margin会传递给父级元素两个相邻元素上下的margin会重叠 BFC布局规则: 浮动的元素会被父级计算高度(父级元素触发了BFC)非浮动元素不会覆盖浮动元素的位置(非浮动元素触发了BFC)margin不会传递给父级(父级触发BFC)属于同一个BFC的两个相邻元素上下margin会重叠 应用场景 阻止margin重叠可以包含浮动元素 清除内部浮动自适应两栏布局可以阻止元素被浮动元素覆盖 position定位 static 默认没有定位按照正常文档流进行排列inherit 规定从父元素继承 position 属性的值relative相对定位不脱离文档流参考自身静态位置通过 top, bottom, left, right 定位absolute(绝对定位)参考距其最近一个不为static的父级元素通过top, bottom, left, right 定位fixed(固定定位)相对于可视窗口进行定位 实现水平居中 行内元素设置父元素text-align:center块级元素 1、宽度固定设置左右margin: auto;或者left:50%margin-left值为宽度一半的负值 2、宽度不固定设置position: absolute; left: 50%; transform:translateX(-50%); 或者 设置父元素 flex布局指定justify-content: centerdisplay: table-cell; 实现垂直居中 文本设置line-height为height值块级元素 1、固定高度时设置top:50%margin-top值为高度一半的负值 2、设置bottom:0,top:0,并设置margin:auto 3、父元素设置 flex布局设置为align-itemcenter将显示方式设置为表格display:table-cell,同时设置vertial-alignmiddle 垂直居中一个img .img-box{display:table-cell;text-align:center;vertical-align:middle; }实现一个浮动元素垂直居中 已知元素的高宽 #div1{width:200px;height:200px;background-color:#6699FF;position: absolute; //父元素需要相对定位top: 50%;left: 50%;margin-top: -100px ; //二分之一的heightwidthmargin-left: -100px; }#div1{width: 200px;height: 200px;background-color: #6699FF;position: absolute; //父元素需要相对定位left: 0;top: 0;right: 0;bottom: 0;margin:auto; }未知元素的高宽 Flex布局 .box {width: 100vw;height: 100vh;display: flex;align-items: center;justify-content: center; }.box-center{background-color: greenyellow; }绝对定位 .box-center{background-color: greenyellow;position: fixed;top: 50%;left: 50%;transfrom: translate(-50%, -50%); }display属性 block 块状元素inline 行内元素 默认inline-block 象行内元素一样显示但其内容象块类型元素一样显示。inline-flex 象行内元素一样显示但其内容象弹性元素一样显示list-item 象块类型元素一样显示并添加样式列表标记。table 块级表格元素inherit 从父元素继承 display 属性的值none 元素不可见。 CSS3新特性 新增选择器弹性盒模型 display: flex;多列布局 column-count: 5;媒体查询 media (max-width: 480px) {}个性化字体 font-face{font-family: BorderWeb; src:url(BORDERW0.eot);}颜色透明度rgba color: rgba(255, 0, 0, 0.75);圆角 border-radius: 5px;渐变 background:linear-gradient(red, green, blue);阴影 box-shadow:3px 3px 3px rgba(0, 64, 128, 0.3);倒影 box-reflect: below 2px;文字装饰 text-stroke-color: red;文字溢出 text-overflow:ellipsis;背景效果 background-size: 100px 100px;边框效果 border-image:url(bt_blue.png) 0 10;转换 旋转 transform: rotate(20deg); 倾斜 transform: skew(150deg, -10deg); 位移 transform: translate(20px, 20px); 缩放 transform: scale(.5);平滑过渡 transition: all .3s ease-in .1s;动画 keyframes anim-1 {50% {border-radius: 50%;}} animation: anim-1 1s; flex弹性盒布局 设置flex弹性盒布局 display: flex; display: inline-flex;flex-direction 决定主轴的方向 row默认值主轴为水平方向起点在左端。row-reverse主轴为水平方向起点在右端。column主轴为垂直方向起点在上沿。column-reverse主轴为垂直方向起点在下沿。 justify-content 设置主轴的对齐方式 flex-start默认值向主轴开始方向对齐。flex-end向主轴结束方向对齐。center 居中。space-between两端对齐项目之间的间隔都相等。space-around每个项目两侧的间隔相等。所以项目之间的间隔比项目与边框的间隔大一倍。 align-items 设置交叉轴的对齐方式flex-start交叉轴的起点对齐。flex-end交叉轴的终点对齐。center交叉轴的中点对齐。baseline: 项目的第一行文字的基线对齐。stretch默认值如果项目未设置高度或设为 auto将占满整个容器的高度。 CSS创建一个三角形 div {width: 0;height: 0;border: 40px solid;border-color: orange blue red green; }/* 把元素的宽度、高度设为0。 把上、左、右三条边框隐藏掉颜色设为 transparent */ #demo {width: 0;height: 0;border-width: 20px;border-style: solid;border-color: transparent transparent red transparent; }实现一个广告跑马灯效果 单行文字跑起来 div classboxp classtext跑马灯效果,跑起来~~/p /div.box{width: 400px;border: 1px solid #000;overflow: hidden; } .text{font-size: 24px;color: red;line-height: 60px;display: inline-block;width: 100%;animation: 10s loop linear infinite normal; } keyframes loop {0%{transform: translateX(0px);}100% {transform: translateX(100%);} }实现一个文字颜色跑马灯效果 div classboxp classtext文字颜色跑马灯效果,跑起来~~/p /div.box{width: 400px;border: 1px solid #000;overflow: hidden; } .text{font-size: 24px;line-height: 60px;background-image: linear-gradient(90deg,#ffffff,#ff0000 6.25%,#ff7d00 12.5%,#ffff00 18.75%, #00ff00 25%,#ffff00 50%,#ffff00 100%);-webkit-background-clip: text; color: transparent; background-size: 200% 100%;animation: 2s loop linear infinite; } keyframes loop {0%{background-position: 0 0;}100% {background-position: -100% 0;} }实现一个简单的幻灯片效果 幻灯片自动切换 .box{width: 200px;height: 200px;overflow: hidden;background-size: cover;background-position: center;animation: loops 20s infinite; }-webkit-keyframes loops {0% {background:url(./bg1.jpg) no-repeat; }25% {background:url(./bg2.jpg) no-repeat;}50% {background:url(./bg3.jpg) no-repeat;}75% {background:url(./bg4.jpg) no-repeat;}100% {background:url(./bg5.jpg) no-repeat;} }实现一个手机版淘宝商品并列显示 div classlistdiv classitemp classtext1跑马灯效果,跑起来~~跑马灯效果,跑起来~~/p/divdiv classitemp classtext2跑马灯效果,跑起来~~/p/divdiv classitemp classtext3跑马灯效果,跑起来~~跑马灯效果,跑起来~~/p/divdiv classitemp classtext4跑马灯效果,跑起来~~跑马灯效果,跑起来~~/p/divdiv classitemp classtext5跑马灯效果,跑起来~~/p/div /div.list{width: 400px; /* 设置宽度 */column-count: 2; /* 1~10 列数自定分配宽度 */column-gap: 1em; /* 设置列与列之前的间距 */margin: 0 auto; } .item{padding: 10px;margin-bottom: 1em;border: 1px solid #000;border-radius: 10px;break-inside: avoid; }设计一个满屏品字布局 1、 div设置成100%子元素分别宽50% 上面那块用float或者display: block;margin: 0 auto;居中 下面两块用float或者inline-block不换行 div classmaindiv classheader/divdiv classleft/divdiv classright/div /div .main {height: 100%;width: 100%;} .header {display: block;width: 50%;height: 50%;background-color: rgb(255,2545,167);margin: 0 auto; } .left {display: inline-block; /* float: left; */width: 50%;height: 100%;background-color: rgb(204,255,102); } .right {display: inline-block; /* float: left; */width: 50%;height: 100%;background-color: rgb(189,255,255); }2、 上面那块用margin: 0 auto;居中 下面两块用float或者inline-block不换行 div classheader/div div classmaindiv classleft/divdiv classright/div /div .header {width: 50%;height: 50%;background-color: rgb(255,2545,167);margin: 0 auto; } .main {height: 50%;width: 100%;} .left {display: inine-block; /* float: left; */width: 50%;height: 100%;background-color: rgb(204,255,102); } .right {display: inine-block; /* float: left; */width: 50%;height: 100%;background-color: rgb(189,255,255); }CSS常见的兼容性问题 三、JavaScript JavaScript 执行机制 最原始的 JavaScript 文件加载方式就是Script 标签。 同步/ 异步 最原始的 JavaScript 文件加载方式就是Script 标签。 默认情况下浏览器是同步加载 JavaScript 脚本即渲染引擎遇到script标签就会停下来等到执行完脚本再继续向下渲染。如果是外部脚本还必须加入脚本下载的时间。 如果脚本体积很大下载和执行的时间就会很长因此造成浏览器堵塞用户会感觉到浏览器“卡死”了没有任何响应。这显然是很不好的体验所以浏览器允许脚本异步加载。 script srcpath/to/myModule.js defer/script script srcpath/to/myModule.js async/scriptscript标签添加defer或async属性脚本就会异步加载。渲染引擎遇到这一行命令就会开始下载外部脚本但不会等它下载和执行而是直接执行后面的命令。 defer要等到整个页面在内存中正常渲染结束才会执行多个脚本时按顺序执行 async一旦下载完渲染引擎就会中断渲染执行这个脚本再继续渲染。多个脚本时不能保证按执行顺序谁先下载好先执行谁 总结一句话defer是“渲染完再执行”async是“下载完就执行”。 【题】异步加载JS的方式? defer设置script属性 deferdeferasync 设置script属性 asyncasync动态创建 script DOMdocument.createElement(script);XmlHttpRequest 脚本注入异步加载库 LABjs模块加载器 Sea.js 事件循环 Event Loop 1、默认代码从上到下执行执行环境通过script来执行宏任务 2、在代码执行过程中调用定时器 promise click事件…不会立即执行需要等待当前代码全部执行完毕 3、给异步方法划分队列分别存放到微任务立即存放和宏任务时间到了或事情发生了才存放到队列中 4、script执行完毕后会选择清空所有的微任务 5、微任务执行完毕后会渲染页面不是每次都调用 6、再去宏任务队列中看有没有到达时间的拿出来其中一个执行 7、执行完毕后按照上述步骤不停的循环 微任务 process.nextTick promise Object.observe MutationObserver 宏任务 script setTimeout setInterval setImmediate I/O 网络请求完成、文件读写完成事件Ajax UI rendering 用户交互事件比如鼠标点击、滚动页面、放大缩小等 【题】异步代码执行顺序解释一下什么是 Event Loop 简单说法 执行同步代码这属于宏任务 执行栈为空查询是否有微任务需要执行 执行所有微任务 必要的话渲染 UI 然后开始下一轮 Event loop执行宏任务中的异步代码 首先js 是单线程运行的在代码执行的时候通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行在执行同步代码的时候如果遇到了异步事件js 引擎并不会一直等待其返回结果而是会将这个事件挂起继续执行执行栈中的其他任务当同步事件执行完毕后再将异步事件对应的回调加入到与当前执行栈中不同的另一个任务队列中等待执行任务队列可以分为宏任务对列和微任务对列当当前执行栈中的事件执行完毕后js 引擎首先会判断微任务对列中是否有任务可以执行如果有就将微任务队首的事件压入栈中执行当微任务对列中的任务都执行完成后再去判断宏任务对列中的任务。 【题】小试牛刀 setTimeout(function() {console.log(1) }, 0); new Promise(function(resolve, reject) {console.log(2);resolve() }).then(function() {console.log(3) }); process.nextTick(function () {console.log(4) }) console.log(5) // 2, 5, 4, 3, 1console.log(1) async function asyncFunc(){console.log(2)// await xx promise.resolve((){console.log(3)}).then()// console.log(3) 放到promise.resolve或立即执行await console.log(3) // 相当于把console.log(4)放到了then promise.resolve((){console.log(3)}).then((){// console.log(4)// })// 微任务谁先注册谁先执行console.log(4) } setTimeout((){console.log(5)}) const promise new Promise((resolve,reject){console.log(6)resolve(7) }) promise.then(d{console.log(d)}) asyncFunc() console.log(8)// 输出 1 6 2 3 8 7 4 5JS内置数据类型 1、数据类型 7种基本类型undefined, null, String, Number, Boolean, Symbol, BigInt(es10新增) 引用类型 Object, Array, Function, Date, Math, RegExp 2、数据类型存储 栈内存、堆内存 基本数据类型存储在栈内存中占据空间小、大小固定属于被频繁使用数据被拷贝时会创建一个完全相等的变量 引用数据类型指针地址存储在栈内存指针指向堆内存中的数据占据空间大、大小不固定。多个引用指向同一个地址存在共享数据的可能。 【题】小试牛刀 let a {name: lee,age: 18 } let b a; // “共享” console.log(a.name); // lee b.name son; console.log(a.name); // son console.log(b.name); // sonlet a {name: Julia,age: 20 } function change(o) {o.age 24;o {name: Kath,age: 30}return o; } let b change(a); console.log(b.age); // 30 console.log(a.age); // 24 // 函数传参进来的 o传递的是对象在堆中的内存地址值【题】 null 和 undefined 的区别 1、Undefined 和 Null 都是基本数据类型。都只有一个值就是 undefined 和 null。 2、undefined 代表的含义是未定义 null 代表的含义是空对象 3、我们使用双等号对两种类型的值进行比较时会返回 true使用全等号时会返回 false。 3、数据类型检测 typeof instanceofconstructorObject.prototype.toString.call()、Array.isArray(arr) typeof 返回一个类型字符串。类型结果 undefined, number, string, boolean, function, object 缺陷不能准确判断引用类型 typeof str // string typeof 2 // number typeof NaN // number typeof true // boolean typeof undefined // undefined typeof null // object typeof function(){} // function typeof {} // object typeof [] // objectinstanceof 原理instanceof 内部机制是通过判断对象的原型链中是不是能找到类型的 prototype 返回判断结果 true/false 缺陷不能检测基本数据类型 console.log(2 instanceof Number); // false console.log(true instanceof Boolean); // false console.log(str instanceof String); // false console.log([] instanceof Array); // true console.log(function(){} instanceof Function); // true console.log({} instanceof Object); // true console.log(undefined instanceof Undefined); // 报错 console.log(null instanceof Null); // 报错手动实现一个instanceof function instanceof(left, right) {// 获得类型的原型let prototype right.prototype// 获得对象的原型left left.__proto__// 判断对象的类型是否等于类型的原型while (true) {if (left null)return falseif (prototype left)return trueleft left.__proto__} }constructor 返回当前实例所属类信息 缺陷如果一个对象更改它的原型constructor就会变得不可靠了 (str).constructor String // true (2).constructor Number // true (true).constructor Boolean// true (undefined).constructor Undefined // 报错 (null).constructor Null// 报错 (function(){}).constructor Function // true ...Object.prototype.toString.call() 调用Object 的原型方法toString()可以统一返回格式为 “[object Xxx]” 的字符串其中 Xxx 就是对象的类型。对于 Object 以外的其他对象则需要通过 call 来调用才能返回正确的类型信息。 返回 “[object Xxx]”的字符串 Object.prototype.toString.call(1) // [object String] Object.prototype.toString.call(1) // [object Number] Object.prototype.toString.call(true) // [object Boolean] Object.prototype.toString.call(null) //[object Null] Object.prototype.toString.call(undefined) //[object Undefined] Object.prototype.toString.call(function(){}) // [object Function] Object.prototype.toString({}) // [object Object] Object.prototype.toString.call({}) // 同上结果加上call也ok Object.prototype.toString.call([]) //[object Array] Object.prototype.toString.call(/123/g) //[object RegExp] Object.prototype.toString.call(new Date()) //[object Date] Object.prototype.toString.call(document) //[object HTMLDocument] Object.prototype.toString.call(window) //[object Window]【题】实现一个数据类型判断方法 function getType(obj){let type typeof obj;if (type object type ! null) {return Object.prototype.toString.call(obj).replace(/^\[object (\S)\]$/, $1);} else {return type;} }4、数据类型转换 JS 中类型转换只有三种情况分别是 转换为布尔值 转换为数字 转换为字符串 转Boolean 在条件判断时除了 undefinednull false NaN 0 -0转为 false 其他所有值都转为 true包括所有对象 强制转换显示转换 自动转换隐式转换 Object 的转换规则 对象转原始类型 会调用内置的 [[ToPrimitive]] 函数其规则逻辑如下 1、如果已经是原始类型了那就不需要转换了; 2、调用 x.valueOf()如果转换为基础类型则返回 3、调用 x.toString()如果转换为基础类型则返回 4、如果都没有返回基础类型会报错。 var obj {value: 1,valueOf() {return 2;},toString() {return 3},[Symbol.toPrimitive]() {return 4} } console.log(obj 1); // 输出5JS 中类型转换 隐式转换由四则运算、比较运算引起的强制转换Number()parseInt()parseFloat() toString()String()Boolean() 隐式转换 算术运算、-、*、/、% 比较运算、!、、、if、while需要布尔值地方 四则运算、-、*、/、%规则 1、 运算一旦一方是字符串就会进行字符串拼接操作 2、除了 运算 有可能把运算子转为字符串其他运算符都会把运算子自动转成数值运算优先转字符串其他运算只要其中一方是数字那么另一方就会被转为数字 3、null转数值时值为0 。undefined转数值时值为NaN 5 1 // 51 5 true // 5true 5 false // 5false 5 {} // 5[object Object] 5 [] // 5 5 function (){} // 5function (){} 5 undefined // 5undefined 5 null // 5null5 - 2 // 3 5 * 2 // 10 true - 1 // 0 false - 1 // -1 1 - 1 // 0 5 * [] // 0 false / 5 // 0 abc - 1 // NaN null 1 // 1 undefined 1 // NaNa b // - aNaN, b 等于 NaN’’ 的隐式类型转换规则 1、两边都是数字时进行的是加法运算 2、两边都是字符串则直接拼接 3、其中一个是字符串、一个是数字则按照字符串规则进行拼接 4、有一个是字符串另外一个是 undefined、null 或布尔型则调用 toString() 方法进行字符串拼接如果是纯对象、数组、正则等则默认调用对象的转换方法会存在优先级然后再进行拼接。 5、其中有一个是数字另外一个是 undefined、null、布尔型或数字则会将其转换成数字进行加法运算对象的情况还是参考上一条规则。 ’’ 的隐式类型转换规则 1、如果类型相同无须进行类型转换 2、只要都是null、undefined 无条件返回true否则都是false 3、只要一方是NaN没有隐式类型转换无条件返回false 4、都是stringbooleannumber。会将不是number类型的数据转成number 5、有一方是复杂数据类型 。 会先获取复杂数据类型的原始值之后再左比较 6、都是复杂数据类型。比较地址如果地址一致则返回true否则返回false 7、如果其中一个是 Symbol 类型那么返回 false console.log ( null undefined );//true console.log ( null null );//true console.log ( undefined undefined );//trueconsole.log ( NaN NaN );//falseconsole.log ( 1 true );//true (1) 1 Number(true) console.log ( 1 true );//false (1) 1 Number(true) console.log ( 1 ! true );//false (1) 1 !Boolean(true) (2) 1 !true (3) 1 false (4)1 Number(false) console.log ( 0 ! true );//true console.log(true true) // falseconsole.log ( [].toString () );//空字符串 console.log ( {}.toString () );//[object Object] console.log ( [ 1, 2, 3 ].valueOf().toString());//‘123’ console.log ( [ 1, 2, 3 ] 1,2,3 );//true (1)[1,2,3].toString() 1,2,3 (2)1,2,3 1,2,3 console.log({} [object Object]);//truevar arr1 [10,20,30]; var arr2 [10,20,30]; var arr3 arr1; console.log ( arr1 arr2 );// false 它们是两个不同的地址 console.log ( arr3 arr1 );//true 两者地址是一样 console.log ( [] [] );//false console.log ( {} {} );//false强制转换 Number() 原始值转换结果undefinedNaNnull0true /false1/0字符串’‘ 0 ‘1’ 1’a‘ NaN’1a‘ NaN’1.23‘ 1.23 ’Ox11‘ 17symbol报错object{} 0[] 0 [1] 1[1,2,3] NaN先调用toPrimitive再调用toNumber parseInt()parseFloat() 除了数值和字符串其他都为NaN 相比Number没那么严格函数逐个解析字符遇到不能转换的字符就停下来 toString()String() 可以将任意类型的值转化成字符串 原始值转换结果undefined’undefined ‘true /false‘true’ / ‘false’number对应的字符串类型symbol报错object{} 0[] 0 [1] 1[1,2,3] NaN先调用toPrimitive再调用toNumber 【题】类型转换经典面试题 注意:八种情况转boolean得到false: 0 -0 NaN undefined null false document.all()console.log([] 0); //true // 分析:(1) [].valueOf().toString() 0 (2) Number() 0 (3) false 0 (4) 0 0 console.log(![] 0); //true// 分析: 逻辑非优先级高于关系运算符 ![] false (空数组转布尔值得到true)console.log([] []); //false // [] 与右边逻辑非表达式结果比较 //(1) [] !Boolean([]) (2) [] !true (3)[] false (4) [].toString() false (5) false (6)Number(0) Number(false) console.log([] ![]); //trueonsole.log({} {}); //false // {} 与右边逻辑非表达式结果比较 //(1){} !{} (2){} !true (3){} false (4){}.toString() false (5)[object Object] false (6)Number([object Object]) false console.log({} !{}); //falseJS各种运算符 算数运算符 令 y 5 作为二元运算符规则 Infinity Infinity Infinity(-Infinity) (-Infinity) -InfinityInfinity (-Infinity) NaNInfinity null Infinity0 0 0-0 -0 -00 -0 0如果是两个字符串则将字符串进行拼接如果有一个是字符串一个不是字符串则将非字符串转换成字符串再拼接如果操作数都不是字符串类型则将操作数转换为数值类型再相加 作为一元运算符 作用于Number()相同将其他类型的对象转换为数字类型 -*/, %运算符相同规则 与NaN相关的运算结果都为NaN如果有一个操作数是对象则先对对象进行隐式转换再根据前面的规则进行运算如果果有一个操作数是非数字类型则先在后台调用 Number()函数将其转换为数值再根据前面的规则进行运算。 -运算符规则 Infinity - Infinity NaN(-Infinity) - (-Infinity) -NaNInfinity - (-Infinity) InfinityInfinity - null Infinity0 - 0 0-0 - -0 -00 - -0 0 fdf *运算符规则 Infinity * Infinity Infinity如果操作数的值超过数值的表示范围结果为Infinity 或 - Infinity如果操作数中有一个操作数为NaN、0、null、undefined 结果为NaN /运算符规则 Infinity / 0 InfinityInfinity / null InfinityInfinity / Infinity NaN如果操作数的值超过数值的表示范围结果为Infinity 或 - Infinity如果操作数中有一个操作数为NaN、undefined结果为NaN %运算符规则 操作数都是数值执行常规的除法计算返回除的余数任何数 % undefined NaN任何数 % NaN NaNInfinity % 任何数 NaN有限大的数 % 0 NaN有限大的数 % Infinity 有限大的数0 % 除null、undefined、NaN任何数 0如果有一个操作数是对象则先对对象进行隐式转换再根据前面的规则进行取余运算有一个操作数不是数值则调用Number()转换 一次弄懂JavaScript的各种运算符 数组 1、创建数组 const arr []; // 字面量 const arr [1, 2, 3, 4]; const arr new Array(); // 构造函数, 不推荐 const arr new Array(5);fill() 创建一个长度确定、同时每一个元素的值也都确定的数组 const arr (new Array(5)).fill(1); // [1,1,1,1,1]2、增删查改方法 数组基本操作可以归纳为 增、删、改、查需要留意的是哪些方法会对原数组产生影响哪些方法不会 【增】push()unshift()splice()concat()。 前三种方法会对原数组产生影响concat() 不会对原数组产生影响 push() 在数组末尾添加任意多个值返回数组的最新长度 const arr []; // 创建一个数组 const count arr.push(1, 2); // 推入两项 console.log(arr) // [1, 2] console.log(count) // 2unshift() 在数组开头添加任意多个值返回数组的最新长度 const arr []; // 创建一个数组 const count arr.unshift(3); // 推入两项 console.log(arr) // [3, 1, 2] console.log(count) // 3splice(开始位置, 0, 插入的元素) 返回空数组 const arr [1, 2, 3]; let removed arr.splice(1, 0, 4, 5) console.log(arr) // [1, 4, 5, 2, 3] console.log(removed) // []concat() 创建一个当前数组的副本然后再把它的参数添加到副本末尾返回新构建的数组不会影响原始数组 const arr [1, 2, 3]; const arr1 arr.concat(4, 5, 6); console.log(arr) // [1, 2, 3] console.log(arr1) // [1, 2, 3, 4, 5, 6]【注意】concat属于浅拷贝所以会有以下情况 const arr [1, 2, 3, [1, 2, 3]]; const arr1 arr.concat(4, 5, 6); arr1[3][1] 6 console.log(arr) // [1, 2, 3] console.log(arr1) // [1, 2, 3, 4, 5, 6]【删】pop()shift()splice()slice()。 前三种方法会对原数组产生影响slice() 不会对原数组产生影响 pop() 删除数组的最后一项同时减少数组的length 值返回被删除的项 shift() 删除数组的第一项同时减少数组的length 值返回被删除的项 splice(开始位置删除元素的数量) 返回被删除元素的数组 const arr [1, 2, 3, 4, 5 ,6]; const arr1 arr.splice(4, 100); console.log(arr) // [1, 2, 3, 4] console.log(arr1) // [5, 6]slice(开始位置结束位置) 创建一个包含原有数组中一个或多个元素的新数组不会影响原始数组 const arr [1, 2, 3, 4, 5 ,6]; const arr1 arr.slice(1); const arr2 arr.slice(1, 4); console.log(arr) // [1, 2, 3, 4, 5, 6] console.log(arr1); // [2, 3, 4, 5, 6] console.log(arr2); // [2, 3, 4]【改】splice() splice(开始位置结束位置要替换为新的一个或多个元素) 返回被修改的元素的数组对原数组产生影响 const arr [1, 2, 3, 4, 5 ,6]; const arr1 arr.splice(1, 1, two); const arr2 arr.splice(1, 1, two, three); console.log(arr); // [1, two, three, 3, 4, 5, 6] console.log(arr1); // [2] console.log(arr2); // [two]【查】indexOf() includes() find() indexOf() 返回元素在数组中的位置索引返回从头找到的第一个位置的索引如果没找到则返回-1 let numbers [1, 2, 3, 4, 5, 4, 3, 2, 1]; numbers.indexOf(4) // 3includes() 查找元素在数组中的位置如果找到返回true否则false let numbers [1, 2, 3, 4, 5, 4, 3, 2, 1]; numbers.includes(4) // truefind() 返回第一个匹配的元素 const people [{name: Matt,age: 27},{name: Nicholas,age: 29} ]; people.find((item, index, array) item.age 28) // {name: Matt, age: 27}3、排序方法 reverse() : 数组元素反向排列sort() sort() 默认正序 升序arr.sort(function(a, b){return a - b}); 降序arr.sort(function(a, b){return b - a}); 乱序arr.sort((function(a, b){return 0.5 – Math.random() }) 排序对象数组 arr.sort((function(a, b){var x a.type.toLowerCase();var y b.type.toLowerCase();if (x y) {return -1;}if (x y) {return 1;}return 0;})4、转换方法 join()split() join() 接收一个字符串分隔符返回包含所有项的字符串 let colors [red, green, blue]; alert(colors.join(,)); // red,green,blue alert(colors.join(||)); // red||green||bluesplit() 将字符串按某个字符切割组成数组 let str red|green|blue; let colors1 str.split(|); alert(str); // red|green|blue alert(colors1 ); // [red, green, blue]5、迭代方法 不改变原数组 forEach() 对数组每一项都运行传入的函数没有返回值map() 对数组每一项都运行传入的函数返回由每次函数调用的结果构成的数组some() 对数组每一项都运行传入的函数如果有一项函数返回 true 则这个方法返回 trueevery() 对数组每一项都运行传入的函数如果每一项函数都返回 true 则这个方法返回 truefilter() 对数组每一项都运行传入的函数过滤返回为 true 的项组成的数组 let numbers [1, 2, 3, 4, 5, 4, 3, 2, 1]; let result numbers.forEach((item, index, array) {// 执行某些操作 }); console.log(result) // undefinedlet numbers [1, 2, 3, 4, 5, 4, 3, 2, 1]; let mapResult numbers.map((item, index, array) item * 2); let mapResult1 numbers.map((item, index, array) {}); console.log(mapResult) // [2,4,6,8,10,8,6,4,2] console.log(mapResult1) // [undefined, undefined, undefined,...]let numbers [1, 2, 3, 4, 5, 4, 3, 2, 1]; let someResult numbers.some((item, index, array) item 2); console.log(someResult) // truelet numbers [1, 2, 3, 4, 5, 4, 3, 2, 1]; let everyResult numbers.every((item, index, array) item 2); console.log(everyResult) // falselet numbers [1, 2, 3, 4, 5, 4, 3, 2, 1]; let filterResult numbers.filter((item, index, array) item 2); console.log(filterResult); // 3,4,5,4,36、数组最高/最低数值 1先sort()排序取头尾对应最低最高 2Math.max()Math.min()。接受的是参数序列 Math.max(…arr)等于Math.max(1,2,3) Math.max([2,3,1]) // NaN Math.max(...[2,3,1]) // 3, 等于 Math.max(2,3,1)7、判断是否数组 arr instanceof Arrayarr.constructor ArrayObject.prototype.toString.call(arr) [object Array]es6 Array.isArray(arr)Array.prototype.isPrototypeOf(arr);Object.getPrototypeOf(arr) Array.prototype; 8、ES6新扩展 Array.of 用于将参数依次转化为数组中的一项然后返回这个新数组而不管这个参数是数字还是其他。 Array.of(8.0); // [8] Array.of(8.0, 5); // [8, 5] Array(8); // [8]Array.from 加工函数 Array.from(item, callback , obj) item 必选类似数组的对象 callback 加工函数新生成的数组会经过该函数的加工再返回 obj this 作用域表示加工函数执行时 this 的值。var obj {0: a, 1: b, 2:c, length: 3}; Array.from(obj, function(value, index){console.log(value, index, this, arguments.length);return value.repeat(3); //必须指定返回值否则返回 undefined }, obj); // return 的 value 重复了三遍最后返回的数组为 [aaa,bbb,ccc]Array.from(obj, (value) value.repeat(3)); // 控制台返回 (3) [aaa, bbb, ccc]// String Array.from(abc); // [a, b, c] // Set Array.from(new Set([abc, def])); // [abc, def] // Map Array.from(new Map([[1, ab], [2, de]])); // [[1, ab], [2, de]]【题】js实现随机选取10–100之间的10个数字存入一个数组并排序? const arr []; for (let i 0; i 10; i) {/*Math.abs 转为正数Math.floor 向下取整加1是为了能够取到100*/const item Math.abs(Math.floor(Math.random() * (10 - 100 1))) 10;arr.push(item) } arr.sort(); console.log(arr);【题】计算数组元素总和 reduce()方法接收两个参数 第一个参数接收一个函数作为累加器数组中的每个值(从左到右)开始缩减最终计算为一个值。 第二个参数非必须是计算的初始值。 如果数组为空则不会调用reduce中传入的函数。 const array [1, 2, 3, 4, 5]; const sum array.reduce((total, currItem) {// total计算后的和, currItem 当前项return total currItem; }, 0); console.log(sum) // 15let studentsArray [{name: 张飞,age: 20},{name: 赵云,age: 10} ]; let ageSum studentsArray.reduce((ageSum, currStudent) {return ageSum currentStudent.age; },0)[题] 数组扁平化 6 种方式 1、递归 循环递归的方式一项一项地去遍历如果每一项还是一个数组那么就继续往下遍历利用递归程序的方法来实现数组的每一项的连接。 function flatten(arr) {let result [];for(let i 0; i arr.length; i) {if(Array.isArray(arr[i])) {result result.concat(flatten(arr[i]));} else {result.push(arr[i]);}}return result; }2、利用 reduce 函数迭代 用 reduce 来实现数组的拼接从而简化第一种方法的代码 function flatten(arr) {return arr.reduce(function(prev, next){return prev.concat(Array.isArray(next) ? flatten(next) : next)}, []) }3、扩展运算符实现 先用数组的 some 方法把数组中仍然是组数的项过滤出来然后执行 concat 操作利用 ES6 的展开运算符将其拼接到原数组中最后返回原数组 function flatten(arr) {while (arr.some(item Array.isArray(item))) {arr [].concat(...arr);}return arr; }4、split 和 toString 共同处理 数组会默认带一个 toString 的方法所以可以把数组直接转换成逗号分隔的字符串然后再用 split 方法把字符串重新转换为数组 function flatten(arr) {return arr.toString().split(,); }5、调用 ES6 中的flat arr.flat([depth])。depth 是可以传递数组的展开深度默认不填、数值是 1即展开一层数组。参数也可以传进 Infinity代表不论多少层都要展开。 function flatten(arr) {return arr.flat(Infinity); }6、正则和 JSON 方法共同处理 用 JSON.stringify 的方法先转换为字符串然后通过正则表达式过滤掉字符串中的数组的方括号最后再利用 JSON.parse 把它转换成数组 function flatten(arr) {let str JSON.stringify(arr);str str.replace(/(\[|\])/g, );str [ str ];return JSON.parse(str); }[题] 数组去重 数组去重 1、ES6 function unique (arr) {return Array.from(new Set(arr)) }2、for嵌套for然后splice去重ES5中最常用 function unique(arr){ for(var i0; iarr.length; i){for(var j i1; jarr.length; j){if(arr[i] arr[j]){ //第一个等同于第二个splice方法删除第二个arr.splice(j,1);j--;}}}return arr; }3、indexOf去重 function unique(arr) {if (!Array.isArray(arr)) {console.log(type error!)return}var array [];for (var i 0; i arr.length; i) {if (array.indexOf(arr[i]) -1) {array.push(arr[i])}}return array; }对象 【对象】无序属性的集合其属性可以包含基本值对象或者函数 对象的创建 1、工厂模式 通过在函数内部创建一个对象为其添加属性和方法并将对象返回从而实现创建多个对象的目的 优点能够解决创建多个对象的问题兼容各个浏览器 缺点没有解决对象识别的问题不能知道一个对象的类型 function createPerson(name, age) {var o new Object();o.name name;o.age age;o.sayName function() {console.log(this.name);};return o; } var person1 createPerson(Nicholas, 29); var person2 createPerson(Greg, 27);2、构造函数模式 通过创建自定义的构造函数从而定义自定义对象类型的属性和方法 优点可以创建多个对象解决对象的识别问题 缺点每个实例都会创建不同的function实例而其实创建完成同样任务的function实例是很没有必要的 // 为了区别构造函数和其它函数,构造函数需要以一个大写字母开头 function Person(name, age) {this.name name;this.age age;this.sayName function() {console.log(this.name);} }var person1 new Person(Nicholas, 29); var person2 new Person(Greg, 27);添加链接描述 字符串 字符串的常用方法 字符串常用的操作方法归纳为增、删、改、查 增 concat() 将一个或多个字符串拼接成一个新字符串 删slice()substr()substring() 三个方法都返回调用它们的字符串的一个子字符串而且都接收一或两个参数。不改变原字符串 let stringValue hello world; console.log(stringValue.slice(3)); // lo world console.log(stringValue.substring(3)); // lo world console.log(stringValue.substr(3)); // lo world console.log(stringValue.slice(3, 7)); // lo w console.log(stringValue.substring(3,7)); // lo w console.log(stringValue.substr(3, 7)); // lo worl改 不是改变原字符串而是创建字符串的一个副本再进行操作 trim()、trimLeft()、trimRight() 删除前、后或前后所有空格符再返回新的字符串repeat()padStart()、padEnd()toLowerCase()、 toUpperCase() 浅拷贝 / 深拷贝 浅拷贝 如果属性是基本类型拷贝的就是基本类型的值。如果属性是引用类型拷贝的就是内存地址 浅拷贝的限制所在——它只能拷贝一层对象。如果存在对象的嵌套那么浅拷贝将无能为力。因此深拷贝就是为了解决这个问题而生的它能解决多层对象嵌套问题彻底实现拷贝 常见的浅拷贝方式有 Object.assignArray.prototype.slice(), Array.prototype.concat()使用拓展运算符(...)实现的复制 Object.assign object.assign是 ES6 中 object 的一个方法该方法可以用于 JS 对象的合并等多个用途其中一个用途就是可以进行浅拷贝。 它不会拷贝对象的继承属性它不会拷贝对象的不可枚举的属性可以拷贝 Symbol 类型的属性。 const obj1 {age: 18,name: {name1: huahua,name2: Lotus} } // const obj2 {}; // Object.assign(obj2, obj1 ); // 或者 var obj2 Object.assign({}, obj1 ); obj2.age 20 obj2.name.name1 lala console.log(obj1); console.log(obj2);slice() const arr [1,2,3]; const arr1 arr.slice(0); arr1[1] 0; console.log(arr); // [1, 2, 3] console.log(arr1); // [1, 0, 3]concat() const arr [1,2,3]; const arr1 arr.concat(); arr1[1] 0; console.log(arr); // [1, 2, 3] console.log(arr1); // [1, 0, 3]拓展运算符(…) const obj1 {age: 18,name: {name1: huahua,name2: Lotus} } const obj2 {...obj1}; obj2.age 20 obj2.name.name1 lala console.log(obj1); console.log(obj2);手工实现一个浅拷贝 对基础类型做一个最基本的一个拷贝对引用类型开辟一个新的存储并且拷贝一层对象属性。 const shallowClone (target) {if (typeof target object target ! null) {const cloneTarget Array.isArray(target) ? []: {};for (let prop in target) {if (target.hasOwnProperty(prop)) {cloneTarget[prop] target[prop];}}return cloneTarget;} else {return target;} }深拷贝 将一个对象从内存中完整地拷贝出来一份给目标对象并从堆内存中开辟一个全新的空间存放新对象且新对象的修改并不会改变原对象二者实现真正的分离。 常见的深拷贝方式有 乞丐版JSON.stringify手写循环递归_.cloneDeep()jQuery.extend() JSON.stringify() JSON.stringify() 是目前开发过程中最简单的深拷贝方法 其实就是把一个对象序列化成为 JSON 的字符串并将对象里面的内容转换成字符串最后再用 JSON.parse() 的方法将 JSON 字符串生成一个新的对象 但是这种方式存在弊端会忽略undefined、symbol和函数 let a {age: 1,jobs: {first: FE} } let b JSON.parse(JSON.stringify(a)) a.jobs.first native console.log(b.jobs.first) // FE局限性 会忽略 undefined会忽略 symbol不能序列化函数无法拷贝不可枚举的属性无法拷贝对象的原型链拷贝 RegExp 引用类型会变成空对象拷贝 Date 引用类型会变成字符串对象中含有 NaN、Infinity 以及 -InfinityJSON 序列化的结果会变成 null不能解决循环引用的对象即对象成环 (obj[key] obj)。 虽然到目前为止还有很多无法实现的功能但是这种方法足以满足日常的开发需求并且是最简单和快捷的。 基础版手写递归实现 function deepClone(obj) { let cloneObj {}for(let key in obj) { //遍历if(typeof obj[key] object) { cloneObj[key] deepClone(obj[key]) //是对象就再次调用该函数递归} else {cloneObj[key] obj[key] //基本类型的话直接复制值}}return cloneObj }let obj2 deepClone(obj1);区别 浅拷贝是拷贝一层属性为对象时浅拷贝是复制两个对象指向同一个地址深拷贝是递归拷贝深层次属性为对象时深拷贝是新开栈两个对象指向不同的地址 [题] js实现一个clone函数 | 深拷贝 const cloneFunc (target) {if (typeof target object target ! null) {const cloneTarget Array.isArray(target) ? []: {};for (let prop in target) {if (target.hasOwnProperty(prop)) {cloneTarget[prop] deepClone(target[prop]);}}return cloneTarget;} else {return target;} }This 函数的 this 关键字在 JavaScript 中的表现略有不同此外在严格模式和非严格模式之间也会有一些差别 不同情况的调用this指向分别如何。顺带可以提一下 es6 中箭头函数没有 this, arguments, super 等这些只依赖包含箭头函数最接近的函数 this绑定规则 1、默认绑定全局环境中定义的 函数内部使用this关键字 一定是 window 严格模式下this会绑定到undefined var a 1 function foo() {console.log(this.a) } foo() // 1 不管 foo 函数被放在了什么地方this 一定是 window2、隐式绑定函数作为某个对象的方法调用这时this就指这个上级对象 function foo() {console.log(this.a) } const obj {a: 2,foo: foo } obj.foo() // 2谁调用了函数谁就是 this所以在这个场景下 foo 函数中的 this 就是 obj 对象 特殊情况 这个函数中包含多个对象尽管这个函数是被最外层的对象所调用this指向的也只是它上一级的对象 var o {a:10,b:{fn:function(){console.log(this.a); //undefined}} } o.b.fn();this永远指向的是最后调用它的对象虽然fn是对象b的方法但是fn赋值给j时候并没有执行所以最终指向window var o {a:10,b:{a:12,fn:function(){console.log(this.a); //undefinedconsole.log(this); //window}} } var j o.b.fn; j();3、new绑定通过构建函数new关键字生成一个实例对象此时this指向这个实例对象 function foo() {this.a 1;console.log(this.a) } const c new foo() // 1new 的方式使 this 被永远绑定在了 c 上面不会被任何方式改变 this 特殊情况 new过程遇到return一个对象此时this指向为返回的对象null虽然也是对象但是此时new仍然指向实例对象 function fn() { this.user xxx; return {}; } var a new fn(); console.log(a.user); //undefined如果返回一个简单类型的时候则this指向实例对象 function fn() { this.user xxx; return 1; } var a new fn; console.log(a.user); //xxx4、显示修改apply()、call()、bind() bind 这些改变上下文的 API 了对于这些函数来说this 取决于第一个参数如果第一个参数为空那么就是 window。 let a {} let fn function () { console.log(this) } fn.bind().bind(a)() // window不管我们给函数 bind 几次fn 中的 this 永远由第一次 bind 决定所以上面的结果是 window 5、箭头函数 箭头函数是没有 this 的箭头函数中的 this 只取决包裹箭头函数的第一个普通函数的 this。 function a() {return () {return () {console.log(this)}} } console.log(a()()()) // window在这个例子中因为包裹箭头函数的第一个普通函数是 a所以此时的 this 是 window。 另外对箭头函数使用 bind这类函数是无效的。 在浏览器里在全局范围内this 指向window对象在函数中this永远指向最后调用他的那个对象构造函数中this指向new出来的那个新的对象call、apply、bind中的this被强绑定在指定的那个对象上箭头函数中this比较特殊,箭头函数this为父作用域的this不是调用时的this.要知道前四种方式,都是调用时确定,也就是动态的,而箭头函数的this指向是静态的,声明的时候就确定了下来优先级new绑定优先级 显示绑定优先级 隐式绑定优先级 默认绑定优先级 apply/call/bind call、apply、bind作用是改变函数执行时的上下文简而言之就是改变函数运行时的this指向 call、apply、 bind 是挂在 Function 对象上的三个方法调用这三个方法的必须是一个函数。 this 取决于第一个参数如果第一个参数为空那么 this 就是 window。 func.call(thisArg, param1, param2, ...) func.apply(thisArg, [param1,param2,...]) func.bind(thisArg, param1, param2, ...)call、apply、 bind 区别: 1、三者都可以改变函数的this对象指向 2、三者第一个参数都是this要指向的对象如果如果没有这个参数或参数为undefined或null则默认指向全局window 3、三者都可以传参但是apply是数组而call是参数列表且apply和call是一次性传入参数而bind可以分为多次传入 // 方式一只在bind中传递函数参数 fn.bind(obj,1,2)()// 方式二在bind中传递函数参数也在返回函数中传递参数 fn.bind(obj,1)(2)4、bind是返回绑定this之后的函数apply、call 则是立即执行 实现一个 call 函数 Function.prototype.myCall function (context window) {context.fn this // 1 给 context 添加一个属性var args [...arguments].slice(1); // 2将 context 后面的参数取出来var result context.fn(...args) // 3delete context.fn // 删除 fnreturn result } // 1 getValue.call(a, pp, 24) a.fn getValue // 3 getValue.call(a, pp, 24) a.fn(pp, 24)实现一个 apply 函数 Function.prototype.myApply function(context window, ...args) {// 在context上加一个唯一值不影响context上的属性let key Symbol(key)context[key] this; let result context[key](args); // 这里和call传参不一样delete context[key]; // 不删除会导致context属性越来越多return result; }实现一个 bind 函数 1、修改this指向 2、动态传递参数 3、兼容new关键字 Function.prototype.myBind function (context window) {// 判断调用对象是否为函数if (typeof this ! function) {throw new TypeError(Error);}// 获取参数const args [...arguments].slice(1),fn this;return function Fn() {// 根据调用方式传入不同绑定值return fn.apply(this instanceof Fn ? new fn(...arguments) : context, args.concat(...arguments)); } }变量提升 当执行 JS 代码时会生成执行环境只要代码不是写在函数中的就是在全局执行环境中函数中的代码会产生函数执行环境只此两种执行环境。 在生成执行环境时会有两个阶段。 第一个阶段【创建阶段】JS 解释器会找出需要提升的变量和函数并且给他们提前在内存中开辟好空间函数的话会将整个函数存入内存中变量只声明并且赋值为 undefined 第二个阶段【代码执行阶段】我们可以直接提前使用 在提升的过程中相同的函数会覆盖上一个函数并且函数优先于变量提升 b() // call b secondfunction b() {console.log(call b fist) } function b() {console.log(call b second) } var b Hello world执行上下文 当执行 JS 代码时会产生三种执行上下文 当执行 JS 代码时会产生三种执行上下文 【全局执行上下文】只有一个浏览器中的全局对象就是 window对象 【函数执行上下文】只有在函数执行的时候才会被创建每次调用函数都会创建一个新的执行上下文 【eval 执行上下文】运行在 eval 函数中的代码很少用而且不建议使用 【执行上下文】可以简单理解为一个对象每个执行上下文中都有三个重要的属性: 【变量对象(VO)】包含变量、函数声明和函数的形参该属性只能在全局上下文中访问 【作用域链(词法作用域)】 【 this指向】 分析一 var a 10 function foo(i) {var b 20 } foo()执行栈中有两个上下文全局上下文和函数 foo 上下文。 stack [globalContext,fooContext ]全局上下文VO大概是这样的 globalContext.VO globe globalContext.VO {a: undefined,foo: Function, }函数 fooVO 不能访问只能访问到活动对象AO fooContext.VO foo.AO fooContext.AO {i: undefined,b: undefined,arguments: } // arguments 是函数独有的对象(箭头函数没有) // 该对象是一个伪数组有 length 属性且可以通过下标访问元素 // 该对象中的 callee 属性代表函数本身 // caller 属性代表函数的调用者【作用域链】可以把它理解成包含自身变量对象和上级变量对象的列表通过 [[Scope]]属性查找上级变量 fooContext.[[Scope]] [globalContext.VO ] fooContext.Scope fooContext.[[Scope]] fooContext.VO fooContext.Scope [fooContext.VO,globalContext.VO ]eg: 生命周期创建阶段 → 执行阶段 → 回收阶段 代码执行过程: 创建 全局上下文 (global EC)全局执行上下文 (caller) 逐行 自上而下 执行。遇到函数时函数执行上下文 (callee) 被push到执行栈顶层函数执行上下文被激活成为 active EC, 开始执行函数中的代码caller 被挂起函数执行完后callee 被pop移除出执行栈控制权交还全局上下文 (caller)继续执行 作用域 【作用域】即变量变量作用域又称上下文和函数生效能被访问的区域或集合 【作用域链】 可以把它理解成包含自身变量对象和上级变量对象的列表通过 [[Scope]]属性查找上级变量 作用是保证对执行环境有权访问的所有变量和函数的有序访问通过作用域链我们可以访问到外层环境的变量和 函数。 当在Javascript中使用一个变量的时候首先Javascript引擎会尝试在当前作用域下去寻找该变量如果没找到再到它的上层作用域寻找以此类推直到找到该变量或是已经到了全局作用域 如果在全局作用域里仍然找不到该变量它就会在全局范围内隐式声明该变量(非严格模式下)或是直接报错 作用域分为三种 【全局】作用域 【函数】作用域也叫局部作用域 【块级】作用域 全局作用域 全局变量是挂载在 window 对象下的变量所以在网页中的任何位置你都可以使用并且访问到这个全局变量 // 全局变量 var greeting Hello World!; function greet() {console.log(greeting); } greet(); // Hello World!函数作用域 函数中定义的变量叫作函数变量这个时候只能在函数内部才能访问到它所以它的作用域也就是函数的内部称为函数作用域 function getName () {var name inner;console.log(name); } getName(); // inner console.log(name); // undefined当这个函数被执行完之后这个局部变量也相应会被销毁。所以你会看到在 getName 函数外面的 name 是访问不到的 块级作用域 ES6 中新增了块级作用域最直接的表现就是新增的 let ,const 关键词使用 let ,const 关键词定义的变量只能在块级作用域中被访问有“暂时性死区”的特点也就是说这个变量在定义之前是不能被使用的。 {...} 花大括号里面所包括的就是块级作用域 console.log(a) //a is not defined if(true){let a 123console.log(a) // 123 } console.log(a) //a is not defined闭包 【闭包】其实就是一个可以访问其他函数内部变量的函数。创建闭包的最常见的方式就是在一个函数内创建另一个函数创建的函数可以 访问到当前函数的局部变量。 因为通常情况下函数内部变量是无法在外部访问的使用闭包就具备实现了能在外部访问某个函数内部变量的功能让这些内部变量的值始终可以保存在内存中。 function fun1() {var a 1;return function(){console.log(a);}; }fun1();var result fun1(); result(); // 1闭包的用途 1、匿名自执行函数。 从var声明的全局变量或者有时候的函数只需要执行一次其内部变量无需维护的情况考虑。 使用闭包创建一个匿名的函数并立即执行它外部无法引用它内部的变量执行完后很快就会被释放这种机制不会污染全局对象。 var datamodel { table : [], tree : {} }; (function(dm){ for(var i 0; i dm.table.rows; i){ var row dm.table.rows[i]; for(var j 0; j row.cells; i){ drawCell(i, j); } } })(datamodel); 2、缓存 设想我们有一个处理过程很耗时的函数对象每次调用都会花费很长时间 那么我们就需要将计算出来的值存储起来当调用这个函数的时候首先在缓存中查找如果找不到则进行计算 然后更新缓存并返回值如果找到了直接返回查找到的值即可。 var CachedSearchBox (function(){ var cache {}, count []; return { attachSearchBox : function(dsid){ if(dsid in cache){//如果结果在缓存中 return cache[dsid];//直接返回缓存中的对象 } var fsb new uikit.webctrl.SearchBox(dsid);//新建 cache[dsid] fsb;//更新缓存 if(count.length 100){//保正缓存的大小100 delete cache[count.shift()]; } return fsb; }, clearSearchBox : function(dsid){ if(dsid in cache){ cache[dsid].clearSelection(); } } }; })(); CachedSearchBox.attachSearchBox(input1); 3、实现封装 4、现面向对象中的对象 function Person(){ var name default; return { getName : function(){ return name; }, setName : function(newName){ name newName; } } }; var john Person(); console.log(john.getName()); john.setName(john); console.log(john.getName()); 闭包的表现形式 返回一个函数在定时器、事件监听、Ajax 请求、Web Workers 或者任何异步中只要使用了回调函数实际上就是在使用闭包 // 定时器 setTimeout(function handler(){console.log(1); }1000); // 事件监听 $(#app).click(function(){console.log(Event Listener); });作为函数参数传递的形式IIFE立即执行函数创建了闭包保存了全局作用域window和当前函数的作用域因此可以输出全局的变量 var a 2; (function IIFE(){console.log(a); // 输出2 })();[题] 大厂面试循环输出问题 for(var i 0; i 5; i ){setTimeout(function() {console.log(i)}, 0) } // 结果 5 5 5 5 5让你实现输出 1、2、3、4、5 的话怎么办呢 1、使用 ES6 中的 let推荐 for(let i 0; i 5; i ){setTimeout(function() {console.log(i)}, 0) } // 结果 1 2 3 4 52、可以利用 IIFE立即执行函数当每次 for 循环时把此时的变量 i 传递到定时器中然后执行 for(var i 1;i 5;i){(function(j){setTimeout(function timer(){console.log(j)}, 0)})(i) } // 结果 1 2 3 4 53、定时器传入第三个参数 for(var i1;i5;i){setTimeout(function(j) {console.log(j)}, 0, i) }new操作符 【new 操作符】主要作用就是执行一个构造函数、返回一个实例对象 【new 操作符】内部执行步骤 1、创建一个新对象 2、将对象与构建函数通过原型链连接起来 3、将构建函数中的this绑定到新建的对象obj上 4、根据构建函数返回类型作判断如果是原始值则被忽略如果是返回对象需要正常处理返回新对象 function Person(name, age){this.name name;this.age age; } const person1 new Person(Tom, 20) console.log(person1) // Person {name: Tom, age: 20}去掉 new有什么不一样呢 function Person(name, age){this.name name;this.age age; } const person1 Person(Tom, 20) console.log(person1) // undefined 默认情况下 this 的指向是 window当构造函数中有 return 一个对象的操作结果又会是什么样子呢 function Person(name, age){this.name name;this.age age;return {age: 18} } const person1 new Person(Tom, 20) console.log(person1) // {age: 18} console.log(person1.name) // undefined console.log(person1.age) // 18结论new 关键词执行之后总是会返回一个对象要么是实例对象要么是 return 语句指定的对象 手工实现new的过程 function create(fn, ...args) {// 1.创建一个新对象const obj {}// 2.新对象原型指向构造函数原型对象obj.__proto__ fn.prototype// 3.将构建函数的this指向新对象let result fn.apply(obj, args)// 4.根据返回值判断return result instanceof Object ? result : obj }合并简写 function create(fn, ...args) {if(typeof fn ! function) {throw fn must be a function;}// 1、2步骤合并const obj Object.create(fn.prototype);// 3、执行fn并将obj作为内部this。var res fn.apply(obj, args);// 4、如果fn有返回值则将其作为new操作返回内容否则返回objreturn res instanceof Object ? res : obj; };测试使用 function Person(name, age) {this.name name;this.age age; } Person.prototype.say function () {console.log(this.name) }let p create(Person, huihui, 123) console.log(p) // Person {name: huihui, age: 123} p.say() // huihui原型 / 原型链 【原型】一个简单的对象用于实现对象的 属性继承。每个对象拥有一个原型对象。每个JavaScript对象中都包含一个__proto__(非标准)的属性指向该对象的原型可obj.__proto__(非标准)进行访问。ES5 中新增了一个 Object.getPrototypeOf() 方法我们可以通过这个方法来获取对象的原型。 【原型链】 原型链是由原型对象组成每个对象都有 proto 属性指向了创建该对象的构造函数的原型proto 将对象连接起来组成了原型链。是一个用来实现继承和共享属性的有限的对象链 当我们访问一个对象的属性时如果这个对象内部不存在这个属性那么它就会去它的原型对象里找这个属性这个原型对象又会有自己的原型于是就这样一直找下去也就是原型链的概念。 三者的关系: 实例.proto 原型原型.constructor 构造函数构造函数.prototype 原型 js 获取原型的方法 obj.__proto__obj.constructor.prototypeObject.getPrototypeOf(p) person.__proto__ Person.prototype // 对象的__proto__指向它的构造函数的原型对象prototype Person.__proto__ Function.prototype // 构造函数的__proto__指向Function构造器的原型对象prototype Person.prototype.__proto__ Object.prototype // 原型对象本身是一个普通对象而普通对象的构造函数都是Object Object.__proto__ Function.prototype // 构造器都是函数对象函数对象都是 Function构造产生的 Object.prototype.__proto__ null // Object的原型对象也有__proto__属性指向nullnull是原型链的顶端继承 继承inheritance是面向对象软件技术当中的一个概念 如果一个类别B“继承自”另一个类别A就把这个B称为“A的子类”而把A称为“B的父类别”也可以称“A是B的超类” 继承的优点 继承可以使得子类具有父类别的各种属性和方法而不需要再次编写相同的代码在子类别继承父类别的同时可以重新定义某些属性并重写某些方法即覆盖父类别的原有属性和方法使其获得与父类别不同的功能 写一个类实现继承 class Car{constructor(color,speed){this.color colorthis.speed speed// ...} }// 货车 继承 class Truck extends Car{constructor(color,speed){super(color,speed);this.color black //覆盖this.Container true // 货箱} }常见的继承方式 原型链继承构造函数继承借助 call组合继承原型式继承寄生式继承寄生组合式继承Class 继承 原型链继承涉及构造函数、原型和实例和三者之间存在的关系 function Parent() {this.name parent1;this.play [1, 2, 3]}function Child() {this.type child2;}Child.prototype new Parent();var s1 new Child();var s2 new Child();s1.play.push(4);console.log(s1.play, s2.play); // [1,2,3,4]问题通过子类生成的实例使用的是同一个原型对象内存空间是共享的一发而动全身 构造函数继承借助 call调用Parent函数 function Parent(){this.name parent1; }Parent.prototype.getName function () {return this.name; }function Child(){Parent1.call(this);this.type child }let child new Child(); console.log(child); // 没问题 console.log(child.getName()); // 会报错问题相比第一种原型链继承方式父类的引用属性不会被共享优化了第一种继承方式的弊端但是只能继承父类的实例属性和方法不能继承原型属性或者方法 组合继承组合继承则将前两种方式继承起来 function Parent3 () {this.name parent3;this.play [1, 2, 3]; }Parent3.prototype.getName function () {return this.name; } function Child3() {// 第二次调用 Parent3()Parent3.call(this);this.type child3; }// 第一次调用 Parent3() Child3.prototype new Parent3(); // 手动挂上构造器指向自己的构造函数 Child3.prototype.constructor Child3;问题Parent3 执行了两次造成了多构造一次的性能开销 原型式继承借助Object.create方法实现普通对象的继承 let parent4 {name: parent4,friends: [p1, p2, p3],getName: function() {return this.name;}};const person4 Object.create(parent4);const person5 Object.create(parent4);person5.friends.push(lucy);console.log(person4.friends); // [p1, p2, p3,lucy]console.log(person5.friends); // [p1, p2, p3,lucy]问题Object.create方法实现的是浅拷贝多个实例的引用类型属性指向相同的内存存在篡改的可能 寄生式继承在上面继承基础上进行优化利用这个浅拷贝的能力再进行增强添加一些方法 let parent5 {name: parent5,friends: [p1, p2, p3],getName: function() {return this.name;} };function clone(original) {let clone Object.create(original);clone.getFriends function() {return this.friends;};return clone; }let person5 clone(parent5); console.log(person5.getName()); // parent5 console.log(person5.getFriends()); // [p1, p2, p3]问题跟上面讲的原型式继承一样 寄生组合式继承是所有继承方式里面相对最优的继承方式 // 父类 function Parent6() {this.name parent6;this.play [1, 2, 3]; } Parent6.prototype.getName function () {return this.name; } // 子类 function Child6() {Parent6.call(this); // this.friends child5; }// 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程 child.prototype Object.create(parent.prototype); child.prototype.constructor child;Child6.prototype.getFriends function () {return this.friends; }let person6 new Child6(); console.log(person6); //{friends:child5,name:child5,play:[1,2,3],__proto__:Parent6} console.log(person6.getName()); // parent6 console.log(person6.getFriends()); // child5class 继承 class 实现继承的核心在于使用 extends 表明继承自哪个父类并且在子类构造函数中必须调用 super因为这段代码可以看成 Parent.call(this, value)。 class Parent {constructor(value) {this.val value}getValue() {console.log(this.val)} } class Child extends Parent {constructor(value) {super(value); // 等于 Parent.call(this, value)this.val value} } let child new Child(1) child.getValue() // 1 child instanceof Parent // true面向对象 事件机制 【事件】javascript中的事件可以理解就是在HTML文档或者浏览器中发生的一种交互操作使得网页具备互动性 常见的有加载事件、鼠标事件、自定义事件等 【事件流】是一个事件沿着特定数据结构传播的过程。 由于DOM是一个树结构如果在父子节点绑定事件时候当触发子节点的时候就存在一个顺序问题这就涉及到了事件流的概念 事件流都会经过三个阶段捕获阶段 - 目标阶段 - 冒泡阶段 捕获 / 冒泡 【事件冒泡】是一种从下往上的传播方式由最具体的元素触发节点然后逐渐向上传播到最不具体的那个节点也就是DOM中最高层的父节点 【事件捕获】最开始由不太具体的节点最早接收事件, 而最具体的节点触发节点最后接收事件 事件对象 标准 Event 属性方法 【type】返回当前 Event 对象表示的事件的名称 【target】返回触发此事件的元素目标节点 【currentTarget】返回其事件监听器触发该事件的元素 【bubbles】返回布尔值事件是否是起泡事件类型 【cancelable】返回布尔值事件是否可拥有取消的默认动作 【eventPhase】返回事件传播的当前阶段 【timeStamp】返回事件生成的日期和时间 【initEvent()】初始化新创建的Event对象的属性 【preventDefault()】告知浏览器不要执行与事件相关的默认动作阻止默认事件 【stopPropagation()】不再派发事件阻止冒泡 IE Event 属性方法 阻止冒泡 / 捕获 阻止事件默认动作event.preventDefault()/event.returnValue false。阻止事件冒泡 event.stopPropagation()/ event.cancelBubble true。希望事件只触发在目标上其实该函数也可以阻止捕获事件。 其实stopImmediatePropagation 同样也能实现阻止事件但是还能阻止该事件目标执行别的注册事件 node.addEventListener(click,(event) {event.stopImmediatePropagation()console.log(冒泡) },false); // 点击 node 只会执行上面的函数该函数不会执行 node.addEventListener(click,(event) {console.log(捕获 ) },true)事件模型 事件模型可以分为三种 原始事件模型DOM0级:onclick、… 标准事件模型DOM2级addEventListener IE事件模型基本不用 1、原始事件模型DOM0级 HTML代码直接绑定input typebutton onclickfun() JS代码绑定 var btn document.getElementById(.btn); btn.onclick fun;特性 绑定速度快只支持冒泡不支持捕获一个类型的事件只能绑定一次删除 DOM0 级事件 btn.onclick null; 2、标准事件模型DOM2级 事件共有三个阶段捕获阶段 - 目标阶段 - 冒泡阶段 通常我们使用 addEventListener 注册事件 node.addEventListener(eventType, handler, useCapture) eventType第一个参数指定事件类型(不要加on) handler第二个参数是事件处理函数 useCapture第三个参数可以是布尔值也可以是对象。布尔值决定注册的事件是捕获事件还是冒泡事件 false默认冒泡 / true 捕获 var div document.getElementById(div); var p document.getElementById(p);function onClickFn (event) {var tagName event.currentTarget.tagName;var phase event.eventPhase;console.log(tagName, phase); } div.addEventListener(click, onClickFn, false); p.addEventListener(click, onClickFn, false);特性 可以在一个DOM元素上绑定多个事件处理器各自并不会冲突执行时机。当第三个参数(useCapture)设置为true就在捕获过程中执行反之在冒泡过程中执行处理函数 3、IE事件模型 IE事件模型共有两个过程目标阶段 - 冒泡阶段 事件绑定、移除监听函数的方式如下: attachEvent(eventType, handler) detachEvent(eventType, handler)事件代理又叫事件委托 【事件代理】把一个元素响应事件click、keydown…的函数委托到另一个元素。而事件委托就是在冒泡阶段完成 【原理】是利用了事件冒泡原理实现的。当我们为最外层的节点添加点击事件那么里面的ul、li、a的点击事件都会冒泡到最外层节点上委托它代为执行事件 应用场景 1、列表事件绑定 ul idulli1/lili2/lili3/li /ulwindow.onload function(){var ulEle document.getElementById(ul);ul.onclick function(ev){//兼容IEev ev || window.event;var target ev.target || ev.srcElement;if(target.nodeName.toLowerCase() li){alert( target.innerHTML);}} }2、动态列表实现事件绑定 可以减少很多重复工作 input typebutton name idbtn value添加 / ul idul1liitem 1/liliitem 2/liliitem 3/liliitem 4/li /ulconst oBtn document.getElementById(btn); const oUl document.getElementById(ul1); const num 4;//事件委托添加的子元素也有事件 oUl.onclick function (ev) {ev ev || window.event;const target ev.target || ev.srcElement;if (target.nodeName.toLowerCase() li) {console.log(the content is: , target.innerHTML);} };//添加新节点 oBtn.onclick function () {num;const oLi document.createElement(li);oLi.innerHTML item ${num};oUl.appendChild(oLi); };适合事件委托的事件有clickmousedownmouseupkeydownkeyupkeypress 无法进行委托绑定事件的有focus、blur因为这些事件没有事件冒泡机制 mousemove、mouseout这样的事件也是不适合于事件委托的因为要不断通过位置去计算定位对性能消耗高 优点 减少整个页面所需的内存提升整体性能 动态绑定减少重复工作 节流与防抖 【防抖】是指在事件被触发 n 秒后再执行回调如果在这 n 秒内事件又被触发则重新计时。 可以使用在一些点击请求的事件上避免因为用户的多次点击向后端发送多次请求。 【节流】是指规定一个单位时间在这个单位时间内只能有一次触发事件的回调函数执行如果在同一个单位时间内某事件被触发多次只有一次能生效。 节流可以使用在 scroll 函数的滚动事件监听上通过事件节流来降低事件调用的频率。 防抖的实现 function debounce(fn, wait) {var timer null;return function() {var context this,args arguments;// 如果此时存在定时器的话则取消之前的定时器重新记时if (timer) {clearTimeout(timer);timer null;}// 设置定时器使事件间隔指定事件后执行timer setTimeout(() {fn.apply(context, args);}, wait);}; }节流的实现 function throttle(fn, delay) {var preTime Date.now();return function() {var context this,args arguments,nowTime Date.now();// 如果两次时间间隔超过了指定时间则执行函数。if (nowTime - preTime delay) {preTime Date.now();return fn.apply(context, args);}}; }模块化 【模块Module】 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起 块的内部数据与实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信 模块化的进化过程 1、全局function模式将不同的功能封装成不同的全局函数 作用将不同的功能封装成不同的全局函数 问题污染全局命名空间, 容易引起命名冲突或数据不安全而且模块成员之间看不出直接关系 function m1(){//... } function m2(){//... }2、namespace模式 : 简单对象封装 作用减少了全局变量解决命名冲突 问题数据不安全(外部可以直接修改模块内部的数据) let myModule {data: www.baidu.com,foo() {console.log(foo() ${this.data})},bar() {console.log(bar() ${this.data})} } myModule.data other data //能直接修改模块内部的数据 myModule.foo() // foo() other data3、IIFE模式立即执行函数(闭包) 问题: 如果当前这个模块依赖另一个模块怎么办? // module.js文件 // 引入jQuery依赖 (function(window, $) {let data www.baidu.com//操作数据的函数function foo() {//用于暴露有函数console.log(foo() ${data})$(body).css(background, red)}function bar() {//用于暴露有函数console.log(bar() ${data})otherFun() //内部调用}function otherFun() {//内部私有的函数console.log(otherFun())}//暴露行为window.myModule { foo, bar } //ES6写法 })(window, jQuery)// index.html文件 !-- 引入的js必须有一定顺序 -- script typetext/javascript srcjquery-1.10.1.js/script script typetext/javascript srcmodule.js/script script typetext/javascriptmyModule.foo()myModule.bar()console.log(myModule.data) //undefined 不能访问模块内部数据myModule.data xxxx //不是修改的模块内部的datamyModule.foo() //没有改变 /script模块化的好处 1、避免命名冲突(减少命名空间污染) 2、更好的分离, 按需加载 3、更高复用性 4、高可维护性 模块化引入的问题 1、请求过多我们要依赖多个模块那样就会发送多个请求导致请求过多 2、依赖模糊不知道他们的具体依赖关系是什么很容易因为不了解他们之间的依赖关系导致加载先后顺序出错。 3、难以维护很可能出现牵一发而动全身的情况导致项目出现严重的问题 模块化固然有多个好处然而一个页面需要引入多个js文件就会出现以上这些问题。而这些问题可以通过模块化规范来解决下面介绍开发中最流行的commonjs, AMD, ES6, CMD规范。 模块化规范 【模块化规范】即为 JavaScript 提供一种模块编写、模块依赖和模块运行的方案。降低代码复杂度提高解耦性 1、CommonJS 方案 2、AMD (典型代表require.js) 3、CMD (典型代表sea.js) 4、ES6 提出的方案使用 import 和 export 的形式来导入导出模块 模块化规范 1、CommonJs 方案 特点 1、同步加载方式适用于服务端因为模块都放在服务器端对于服务端来说模块加载较快不适合在浏览器环境中使用因为同步意味着阻塞加载。 2、所有代码都运行在模块作用域不会污染全局作用域。 3、模块可以多次加载但只会在第一次加载时运行一次然后运行结果就被缓存了以后再加载就直接读取缓存结果。如果想要再次执行可清除缓存 4、模块加载的顺序按照其在代码中出现的顺序。 服务器端实现Node.js 浏览器端实现BrowserifyCommonjs的浏览器的打包工具 通过 module.exports 、exports定义模块的输出接口。 通过 require 来引入模块。读入并执行一个JavaScript文件然后返回该模块的exports对象。如果没有发现指定模块会报错。 // a.js module.exports {a: 1 } // or exports.a 1// b.js var module require(./a.js) module.a // - log 1module.exports 和exports用法其实是相似的但是不能对 exports 直接赋值不会有任何效果。 为什么 exports 和 module.exports 用法相似的原因 var exports module.exports var load function (module) {// 导出的东西var a 1module.exports areturn module.exports };2、AMD (典型代表require.js) 异步模块定义采用异步方式加载模块。所有依赖模块的语句都定义在一个回调函数中等到模块加载完成之后这个回调函数才会运行 require.js 实现了 AMD 规范 /** main.js 入口文件/主模块 **/ // 首先用config()指定各模块路径和引用名 require.config({baseUrl: js/lib,paths: {jquery: jquery.min, //实际路径为js/lib/jquery.min.jsunderscore: underscore.min,} }); // 执行基本操作 require([jquery,underscore],function($,_){// some code here });3、CMD (典型代表sea.js) 都是为了解决异步模块加载的问题sea.js 实现了 CMD 规范。它和require.js的区别在于模块定义时对依赖的处理不同和对依赖模块的执行时机的处理不同。 4、 ES6 提出的方案使用 import 和 export 的形式来导入导出模块 ES6 在语言标准的层面上实现了Module即模块功能完全可以取代 CommonJS和 AMD规范成为浏览器和服务器通用的模块解决方案 // fs.js // ES6模块 var firstName Michael; var lastName Jackson; var year 1958; function v1() { ... } function v2() { ... }export { firstName, lastName, year, v1, v2 };// import后面我们常接着from关键字from指定模块文件的位置可以是相对路径也可以是绝对路径 import { stat, exists, readFile } from fs; // 通过as关键字起别名 import { v1 as streamV1,v2 as streamV2 } from fs; // 当加载整个模块的时候需要用到星号* import * as circle from ./circle;ES6 模块与 CommonJS 模块的差异 1、前者支持动态导入也就是 require(${path}/xx.js)后者目前不支持但是已有提案 2、前者是同步导入因为用于服务端文件都在本地同步导入即使卡住主线程影响也不大。而后者是异步导入因为用于浏览器需要下载文件如果也采用同步导入会对渲染有很大影响 3、前者在导出时都是值拷贝就算导出的值变了导入的值也不会改变所以如果想更新值必须重新导入一次。但是后者采用实时绑定的方式导入导出的值都指向同一个内存地址所以导入值会跟随导出值变化 4、后者会编译成 require/exports 来执行的 Promise 【promise】ES6 新增的语法是异步编程的一种方式比传统回调函数更合理更强大。 优点链式操作解决了地狱回调降低了编码难度代码可读性增强 Promise对象仅有三种状态pending进行中, fulfilled已成功, rejected已失败 可以把 Promise看成一个状态机。初始是 pending 状态可以通过函数 resolve 和 reject将状态转变为 resolved 或者 rejected 状态状态一旦改变就不能再次变化。 promise能链式书写的原因 因为最后 Promise.prototype.then 和 Promise.prototype.catch 方法返回的是一个 Promise实例 并且该返回值是一个新的实例而不是之前的实例。因为 Promise 规范规定除了 pending 状态其他状态是不可以改变的如果返回的是一个相同实例的话多个 then 调用就失去意义了。 基本语法 const promise new Promise(function(resolve, reject) {if (true) {resolve(success);} else {reject(error)} }) .then((resualt) {console.log(resualt)return Promise.resolve(resualt); }) .then((data) {// 以上 promise 成功状态的回调函数 console.log(thenTwo, data);return Promise.reject(); }) .then(data {// 以上 promise 成功状态的回调函数 console.log(thenthree, data); }) .catch(error {// 处理前面所有Promise产生的错误console.log(error) }) .finally(() {// finally 前面所有Promise执行结束console.log(finally); })【Promise 】构造函数 【promise 】 一个带有状态和返回结果的 Promise 新实例 【resolve】回调函数将Promise对象的状态从“未完成”变为“成功” 【reject】回调函数将Promise对象的状态从“未完成”变为“失败” 【then()】 实例状态发生改变时的回调函数第一个参数是resolved成功状态的回调函数第二个参数是rejected失败状态的回调函数。 【catch()】 第二个参数.then(null, rejection)或.then(undefined, rejection)的别名用于指定发生错误时的回调函数 【finally()】不管 Promise 对象最后状态如何都会执行的操作 【链式调用的终止/取消】方式 第一种方法throw new error() 抛出异常 第二种方法return Promise.reject() Promise实例 拥有以下方法 then() resolved成功状态的回调函数。返回值是一个新的实例 catch()rejected失败状态的回调函数。返回值是一个新的实例。是.then(null, rejection)或.then(undefined, rejection)的别名 finally()不管 Promise 对象最后状态如何都能执行的操作 Promise构造函数 的静态方法 all() 语法 Promise.alliterable。 参数 一个可迭代对象如 Array。 描述将多个 Promise实例包装成一个新的 Promise实例。在 ES6 中可以将多个 Promise.all 异步请求并行操作 返回结果一般有两种情况 1、当所有结果成功返回时按照请求顺序返回成功结果。 2、当其中有一个失败方法时则进入失败方法 业务场景页面加载多个请求合并 /* 在一个页面中需要加载获取轮播列表、获取店铺列表、获取分类列表这三个操作 页面需要同时发出请求进行页面渲染这样用 Promise.all 来实现看起来更清晰、一目了然。 *///1.获取轮播数据列表 function getBannerList(){return new Promise((resolve,reject){setTimeout(function(){resolve(轮播数据)},300) }) } //2.获取店铺列表 function getStoreList(){return new Promise((resolve,reject){setTimeout(function(){resolve(店铺数据)},500)}) } //3.获取分类列表 function getCategoryList(){return new Promise((resolve,reject){setTimeout(function(){resolve(分类数据)},700)}) } function initLoad(){ Promise.all([getBannerList(),getStoreList(),getCategoryList()]).then(res{console.log(res) }).catch(err{console.log(err)}) } initLoad()race() 语法及参数跟 Promise.all 类似。唯一的不同在于只要有一个实例率先改变状态返回状态就跟着改变。那个率先改变的 Promise 实例的返回值就传递给 race 方法的回调函数 业务场景图片的加载超时判断 //请求某个图片资源 function requestImg(){var p new Promise(function(resolve, reject){var img new Image();img.onload function(){ resolve(img); }img.src http://www.baidu.com/img/flexible/logo/pc/result.png;});return p; } //延时函数用于给请求计时 function timeout(){var p new Promise(function(resolve, reject){setTimeout(function(){ reject(图片请求超时); }, 5000);});return p; } Promise.race([requestImg(), timeout()]) .then(function(results){console.log(results); }) .catch(function(reason){console.log(reason); });allSettled() 语法及参数跟 Promise.all 类似。唯一的不同在于执行完之后不会失败。只有等到所有这些参数实例都返回结果不管是成功还是失败包装实例才会结束 const resolved Promise.resolve(2); const rejected Promise.reject(-1); const allSettledPromise Promise.allSettled([resolved, rejected]); allSettledPromise.then(function (results) {console.log(results); }); // 返回结果 // [ // { status: fulfilled, value: 2 }, // { status: rejected, reason: -1 } // ]any() 语法及参数跟 Promise.all 类似。唯一的不同在于只要参数 Promise 实例有一个变成 fulfilled状态最后 any返回的实例就会变成 fulfilled 状态如果所有参数 Promise 实例都变成 rejected 状态包装实例就会变成 rejected 状态。 const resolved Promise.resolve(2); const rejected Promise.reject(-1); const anyPromise Promise.any([resolved, rejected]); anyPromise.then(function (results) {console.log(results); }); // 返回结果 // 2[题] 手写实现promise function myPromise(fn){let that this;that.status pending; // 定义状态管理that.value undefined; // 定义成功状态的返回值that.reason undefined; // 定义失败状态的返回值that.fulfilledCallbacks [];that.rejectedCallbacks [];function resolve(value){if (that.status pending) { // 保证了状态的改变是不可逆的that.value value;that.status fulfilled;//执行回调方法that.fulfilledCallbacks.forEach(myFn myFn(that.value))}}function reject(reason){if(that.status pending){ // 保证了状态的改变是不可逆的that.reason reason;that.status rejected;}}//捕获构造异常try{fn(resolve, reject);} catch(e) {reject(e);} } // 定义链式调用的then方法 myPromise.prototype.then function(onFullfilled, onRejected){let that this;switch(that.status){case pending: that.fulfilledCallbacks.push((){onFulfilled(that.value);});that.rejectedCallbacks.push((){onRejected(that.reason);})case fulfilled:onFullfilled(that.value);break;case rejected:onRejected(that.reason);break;default: } }async/await Generator 函数的语法糖。有更好的语义、更好的适用性、返回值是 Promise await 相比直接使用 Promise 来说优势在于处理 then 的调用链能够更清晰准确的写出代码。缺点在于滥用 await 可能会导致性能问题因为 await 会阻塞代码也许之后的异步代码并不依赖于前者但仍然需要等待前者完成导致代码失去了并发性此时更应该使用 Promise.all。 一个函数如果加上 async 那么该函数就会返回一个 Promise 基本用法 async function timeout (ms) {await new Promise((resolve) {setTimeout(resolve, ms) }) } async function asyncConsole (value, ms) {await timeout(ms)console.log(value) } asyncConsole(hello async and await, 1000)优缺点 async/await的优势在于处理 then 的调用链能够更清晰准确的写出代码并且也能优雅地解决回调地狱问题。 当然也存在一些缺点因为 await 将异步代码改造成了同步代码如果多个异步代码没有依赖性却使用了 await 会导致性能上的降低。 async原理 使用Generator函数自动执行器来运作的 // 定义了一个promise用来模拟异步请求作用是传入参数 function getNum(num){return new Promise((resolve, reject) {setTimeout(() {resolve(num1)}, 1000)}) }//自动执行器如果一个Generator函数没有执行完则递归调用 function asyncFun(func){var gen func();function next(data){var result gen.next(data);if (result.done) return result.value;result.value.then(function(data){next(data);});}next(); }// 所需要执行的Generator函数内部的数据在执行完成一步的promise之后再调用下一步 var func function* (){var f1 yield getNum(1);var f2 yield getNum(f1);console.log(f2) ; }; asyncFun(func);在执行的过程中判断一个函数的promise是否完成如果已经完成将结果传入下一个函数继续重复此步骤 每一个 next() 方法返回值的 value 属性为一个 Promise 对象所以我们为其添加 then 方法 在 then 方法里面接着运行 next 方法挪移遍历器指针直到 Generator函数运行完成 [题] 小试牛刀 var a 0 var b async () {a a await 10console.log(2, a) // - 2 10a (await 10) aconsole.log(3, a) // - 3 20 } b() a console.log(1, a) // - 1 1首先函数b 先执行在执行到 await 10 之前变量 a 还是 0因为在 await 内部实现了 generators generators 会保留堆栈中东西所以这时候 a 0 被保存了下来 因为 await 是异步操作遇到await就会立即返回一个pending状态的Promise对象暂时返回执行代码的控制权使得函数外的代码得以继续执行所以会先执行 console.log(‘1’, a) 这时候同步代码执行完毕开始执行异步代码将保存下来的值拿出来使用这时候 a 10 然后后面就是常规执行代码了 浏览器 HTTP 前端性能优化 性能问题无外乎两方面原因渲染速度慢、请求时间长。 定位问题 技术选型 我们主要从PC端、公众号移动端H5、小程序三大平台进行前端的技术选型并来说说选其技术的几大优势。 一个好的技术框架选型应该参考优秀的BAT公司让我们来通过boss直聘看一下这些大公司都用了什么技术框架。 前端3大框架分别是Angular、React与Vue。我们来比较一下各框架的优劣势 Angular 学习成本很高 难调试笨重 React 对于跨平台应用程序开发React Native是一个理想的选择因为它提供了现代功能您可以轻松地找到资源 vue Vue.js是超轻量级但功能非常丰富的框架它们结合了AngularJS和React两者。 渐进式构建能力是 vue.js 最大的优势vue 有一个简洁而且合理的架构使得它易于理解和构建。vue 有一个强大的充满激情人群的社区这为 vue.js 增加了巨大的价值使得为一个空白项目创建一个综合的解决方案变得十分容易。学习成本比较亲民。 Vue有以下特点 1API设计上简单语法简单学习成本低 2构建方面不包含路由和ajax功能使用vuex, vue-router 3指令dom和组件视图数据逻辑处理清晰 4性能好容易优化 5基于依赖追踪的观察系统并且异步队列更新 6独立触发 7v-model 实时渲染 8适用于模板和渲染函数的弹性选择 9简单的语法及项目搭建 10更快的渲染速度和更小的体积。 三大框架适用范围 vue框架脱颖而出的原因 1小程序可以使用mpvue , 公众号H5可以使用vuxapp开发可以使用weexPC端可以用nuxt.jsUI框架可以使用elementUI以上框架都基于vue开发做到了跨平台有更好的代码复用性做到了开发习惯的统一 2API文档简单上手容易学习成本比较亲民华人开发更接地气 3BAT公司的招聘要求都有在使用该框架 vue在各个平台上的技术选型 小程序 wepy 一个类Vue开发风格的小程序框架 特性 类Vue开发风格 支持组件化开发 支持NPM 支持Promise, 主动选择是否开启 支持ES2015 编译器支持less/sass/TypeScript等开发 小程序性能优化 框架大小24.3k8.9k wepy-redux数据管理 构建与编译工具wepy-cli编译配置wepy.config.js mpvue 框架基于 Vue.js 核心mpvue 修改了 Vue.js 的 runtime 和 compiler 实现使其可以运行在小程序环境中从而为小程序开发引入了整套 Vue.js 开发体验。 特性 组件化开发 完整Vue.js开发体验全部.vue单文件组件 Vuex数据管理方案 webpack构建机制自定义构建策略、开发阶段hotReload 支持npm 使用 Vue.js 命令行工具 vue-cli 快速初始化项目 H5代码转换编译成小程序目标代码能力可使用html开发 构建与编译工具vue-cli编译配置build 配套设施 mpvue-loader mpvue-webpack-target postcss-mpvue-wxss px2rpx-loader 其他 选择mpvue的原因特性对比 公众号H5 Nuxt/Vue SSRNuxt.js框架解决服务器端渲染问题和首屏加载时长问题实现 Vue SSR。 App 首选WEEX其次是Flutter再者是React native。 PC端 Nuxt.js框架解决服务器端渲染问题和首屏加载时长问题实现 Vue SSR。 UI框架 PC端element-ui APP端view-ui、vux CSS处理器 Stylusstylus在追求代码的整洁性上取得了优异性的胜利。 添加链接描述 NetWork 我们先来看一下network面板 优化方案 前端优化手段有哪些 【静态资源合并压缩(js,css, images)】请求数量优化【Gzip压缩】带宽优化大小优化【CDN】就近节点减少DNS查找【按需加载】列表分页、【lazyload懒加载】 减少请求【骨架屏】优化白屏【web缓存】缓存ajax数据【减少重绘和重排】批量更新样式【页面结构】将样式表放在顶部将脚本放在底部尽早刷新文档的输出 HTML性能优化 文件压缩合理使用标签少嵌套结构分划js引入方页面底部或者使用异步加载async 、defer link 标签通过预处理提升渲染速度rel“dns-prefetch”搜索优化.meta 标签提取关键信息 CSS性能优化 在CSS文件请求、下载、解析完成之前CSS会阻塞渲染浏览器将不会渲染任何已处理的内容 【内联首屏关键CSS】通过内联css关键代码能够使浏览器在下载完html后就能立刻渲染。而外部引用css代码在解析html结构过程中遇到外部css文件才会开始下载css代码再渲染 【异步加载CSS】 【资源压缩】 【合理使用选择器】 可以遵循以下规则 不要嵌套使用过多复杂选择器最好不要三层以上 使用id选择器就没必要再进行嵌套 通配符和属性选择器效率最低避免使用 【减少使用昂贵的属性】 在页面发生重绘的时候昂贵属性如box-shadow/border-radius/filter/透明度/:nth-child等会降低浏览器的渲染性能 【不要使用import】 css样式文件有两种引入方式一种是link元素另一种是import import会影响浏览器的并行下载使得页面在加载时增加额外的延迟 多个import可能会导致下载顺序紊乱 减少重排操作以及减少不必要的重绘 了解哪些属性可以继承而来避免对这些属性重复编写 cssSprite合成所有icon图片用宽高加上backgroud-position的背景图方式显现出我们要的icon图减少了http请求 把小的icon图片转成base64编码 CSS3动画或者过渡尽量使用transform和opacity来实现动画不要使用left和top属性 图片压缩使用 JS性能优化 兼容、适配问题 [题] 移动端 1px 问题 1、局部处理 meta标签中的 viewport属性 initial-scale 设置为 1 rem按照设计稿标准走外加利用transfrome的scale(0.5) 缩小一倍即可 meta nameviewport contentinitial-scale1.0 /.border-top{position: relative; } .border-top:befor{content: ;width: 100%;height: 1px;background: #ddd;transform:scaleY(0.5);position:absolute;top: 0;left: 0; }2、全局处理 mate标签中的 viewport属性 initial-scale 设置为 0.5 rem 按照设计稿标准走即可 TypeScript 类型 对值所具有的结构进行类型检查 布尔值数字字符串联合类型(eg: string | number)数组元组Tuple一个已知元素数量和类型的数组各元素的类型不必相同枚举enum可以为一组数值赋予友好的名字可以手动的指定成员的数值任意值any我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查空值void与any类型相反它表示没有任何类型本身的类型用处不是很大null 和 undefined本身的类型用处不是很大never永不存在的值的类型类型断言 as let boll:boolean true; let num:number 1; let str:string hello;联合类型 let a:string | number 1;数组 let arr1:number[] [1,2,3]; let arr2:Arraynumber [1, 2,3]; let list: any[] [1, true, free];元组Tuple let tup:[string, number, boolean] [hello, 1, true]枚举enum enum Color {Red, Green, Blue} let c: Color Color.Green;enum Color {Red 1, Green 2, Blue 4} let c: string Color[2];any let notSure: any 4; notSure maybe a string instead; notSure false; // okay, definitely a booleanvoid 当一个函数没有返回值时你通常会见到其返回值类型是void function warnUser(): void {alert(This is my warning message); }声明一个void类型的变量没有什么大用因为你只能为它赋予undefined和null let unusable: void undefined;as let someValue: any this is a string; let strLength: number (stringsomeValue).length; // 或者 let strLength: number (someValue as string).length;接口(interface) function printLabel(labelledObj: { label: string }) {console.log(labelledObj.label); }let myObj { size: 10, label: Size 10 Object }; printLabel(myObj);类型检查。我们只会去关注值的外形类型检查器不会去检查属性的顺序只要相应的属性存在并且类型也是对的就可以。 interface LabelledValue {label: string; }function printLabel(labelledObj: LabelledValue) {console.log(labelledObj.label); }let myObj {size: 10, label: Size 10 Object}; printLabel(myObj);可选属性在可选属性名字定义的后面加一个?符号 好处之一是可以对可能存在的属性进行预定义好处之二是可以捕获引用了不存在的属性时的错误。 interface SquareConfig {color?: string;width?: number; }function createSquare(config: SquareConfig): { color: string; area: number } {let newSquare {color: white, area: 100};if (config.color) {// Error: Property clor does not exist on type SquareConfignewSquare.color config.clor;}if (config.width) {newSquare.area config.width * config.width;}return newSquare; }let mySquare createSquare({color: black});只读属性 readonly定义一些对象属性只能在对象刚刚创建的时候修改其值 readonly interface Point {readonly x: number;readonly y: number; } let p1: Point { x: 10, y: 20 }; p1.x 5; // error!readonlyArray它与Array相似只是把所有可变方法去掉了因此可以确保数组创建后再也不能被修改 let a: number[] [1, 2, 3, 4]; let ro: ReadonlyArraynumber a; ro[0] 12; // error! ro.push(5); // error!readonly vs const 判断该用readonly还是const的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用const若做为属性则使用readonly。 函数类型 为了使用接口表示函数类型我们需要给接口定义一个调用签名。 它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。 interface SearchFunc {(source: string, subString: string): boolean; } let mySearch: SearchFunc; mySearch function(source: string, subString: string) {let result source.search(subString);return result -1; }Vue2 Vue3 Vue中data为什么要返回函数 Vue3新特性 数据响应重新实现Es6的 proxy 代替Es5的 Object.defineProperty源码使用TS重写更好的代码推导虚拟DOM新算法更快更小提供了 composition api为了更好的逻辑复用和代码组织自定义渲染器app、小程序、游戏开发Frament模板可以有多个根元素 Vue2 Vue3 响应原理对比 vue2使用Object.defineProperty方法劫持数据发布-订阅模式实现响应式数据 缺点 无法检测到对象属性的动态添加和删除 无法检测到数组的下标和length的属性的变更 解决方案 利用Vue.$set动态添加对象属性 利用Vue.$delete动态删除对象属性 重写数组的方法检测数组变更vue3使用 proxy 实现响应式数据 优点 可以检测到代理对象属性的动态添加和删除 可以检测到数组的下标和length属性的变化 缺点 ES6的 proxy 不支持低版本你浏览器 IE11 Vue3有了解过吗能说说跟Vue2的区别吗 小试牛刀 1. JS中 ?? 与 || 的区别
http://www.zqtcl.cn/news/430469/

相关文章:

  • 百度提交网站收录入口郑州网站app开发
  • 自己的身份已经网站备案了品牌建设目标包括哪些方面
  • 中国免费网站服务器下载保定网站制作系统
  • 深圳app网站设计数据库网站建设公司
  • 手机网站程序下载做地方黄页网站
  • 网站开发时如何设计英文版本专业vi机构
  • 黄骅市人事考试网电商网站怎样优化
  • 可信网站认证必须做吧陕西做网站的
  • 网站怎么静态化wordpress视频安装教程
  • 合浦县建设局网站网站备案号如何查询
  • 网站跳转代码 html亚马逊使用wordpress做的
  • 做哪一类的网站可以短时间变现东莞大朗网站设计
  • 框架网站模板建设淘宝客网站.lc和ev
  • 驻马店做网站推广涞源县住房和城乡建设局网站
  • 国外seo大神如何做网站 seo
  • 网站建设外文版要求昆山网站建设怎么样
  • 合肥知名网站制作网站建设宣传的目的
  • 曲阜做网站哪家好asp.net网站打不开html页面
  • 品牌网站开发普通人做电商赚钱吗
  • 网站建设与维护理解视频当背景图片 网站开发
  • 站酷设计师网站wordpress 设置静态内容缓存时间
  • 网站推广做什么好看的电商网站模板下载
  • 如何打破违法网站wordpress 无法上传文件
  • 自己做网站的软件下载wordpress发布文章 更新失败
  • 电大企业网站建设论文范文搜狗推广登录入口
  • 建设银行u盾用网站打不开软件工程师证书有哪些
  • 网站建设域名的购买多少钱注册的公司才能参加投标
  • 做婚纱网站策划方案php网站超市源码
  • 济南网站搭建公司有哪些做企业网站的
  • 360做企业网站多少钱凡客诚品网