面试

522 阅读26分钟

html语义化标签都有哪些,都代表什么,都有什么意义

<title>:页面主体内容。
<hn>:h1~h6,分级标题,<h1> 与 <title> 协调有利于搜索引擎优化。
<ul>:无序列表。
<li>:有序列表。
<header>:页眉通常包括网站标志、主导航、全站链接以及搜索框。
<nav>:标记导航,仅对文档中重要的链接群使用。
<main>:页面主要内容,一个页面只能使用一次。如果是web应用,则包围其主要功能。
<article>:定义外部的内容,其中的内容独立于文档的其余部分。
<section>:定义文档中的节(section、区段)。比如章节、页眉、页脚或文档中的其他部分。
<aside>:定义其所处内容之外的内容。如侧栏、文章的一组链接、广告、友情链接、相关产品列表等。
<footer>:页脚,只有当父级是body时,才是整个页面的页脚。
<small>:呈现小号字体效果,指定细则,输入免责声明、注解、署名、版权。
<strong>:和 em 标签一样,用于强调文本,但它强调的程度更强一些。
<em>:将其中的文本表示为强调的内容,表现为斜体。
<mark>:使用黄色突出显示部分文本。
<figure>:规定独立的流内容(图像、图表、照片、代码等等)(默认有40px左右margin)。
<figcaption>:定义 figure 元素的标题,应该被置于 figure 元素的第一个或最后一个子元素的位置。
<cite>:表示所包含的文本对某个参考文献的引用,比如书籍或者杂志的标题。
<blockquoto>:定义块引用,块引用拥有它们自己的空间。
<q>:短的引述(跨浏览器问题,尽量避免使用)。
<time>:datetime属性遵循特定格式,如果忽略此属性,文本内容必须是合法的日期或者时间格式。
<abbr>:简称或缩写。
<dfn>:定义术语元素,与定义必须紧挨着,可以在描述列表dl元素中使用。
<address>:作者、相关人士或组织的联系信息(电子邮件地址、指向联系信息页的链接)。
<del>:移除的内容。
<ins>:添加的内容。
<code>:标记代码。
<meter>:定义已知范围或分数值内的标量测量。(Internet Explorer 不支持 meter 标签)
<progress>:定义运行中的进度(进程)。

块级元素和行内元素的区别,行内元素可以设置margin/padding吗

块级元素会独占一行,默认情况下,其宽度自动填满父元素宽度,
行内元素不会独占一行,相邻的行内元素会排列在同一行里,直到一行排不下才会换行,其宽度随元素内容变化而变化

块级元素可以设置margin和padding属性,
行内元素的argin和padding属性很奇怪,水平方向margin-left,margin-right, padding-left,
padding-right都产生边距效果,但是竖直方向的margin-top,margin-bottom,padding-top,
padding-bottom却不会产生边距效果.

rem跟em有什么区别

rem 相对根元素的font-size
em  相对于父级元素的font-size

浏览器常驻的线程:

1.js 引擎线程(解释和执行js 代码,  JS内核-V8引擎 ,js引擎用来解释执行js代码 )
2.GUI 线程(绘制用户界面, 与js 主线程是互斥的);
3.http 网络请求线程(处理用户的get, post 等请求等, 返回结果后讲回调函数推入到任务队列);
4.定时器触发线程(setTimeoutsetInterval 等待时间结束后把执行函数推入到任务队列中);
5.浏览器事件处理线程(将click, mouse 等交互事件发生后将这些事件放入到事件队列中)。

代码预编译

javaScript从编译到执行的过程,大致可分为四步:

  • 函数声明,整体提升
  • 变量声明,声明提升
1.  词法分析
2.  语法分析:检查代码是否存在错误,若有错误,引擎会抛出语法错误。同时会构建一颗抽象语法树(`AST`)。
3.  预编译
4.  解释执行

函数预编译四部曲

1. 预编译开始,会建立`AO(Activation Object)`对象
2. 找形参和变量声明,使其作为`AO`的属性名,值赋予`undefined`
3. 实参和形参相统一(将实参值赋值给形参)
4. 找函数声明,函数名作为`AO`属性名,值赋予函数体

全局的预编译执行三部曲

1. 创建Global Object(以下简写为GO对象);
2. 找形参和变量声明,将变量声明和形参作为GO的属性名,值为undefined3. 在全局里找函数声明,将函数名作为GO对象的属性名,值赋予函数体。
js执行时会生成一个临时的对象(AO)
1 AO(activeObject) 存储所有的方法和局部变量,全局变量不会存储在这里
2 寻找var变量声明
3 形参实参赋值
4 寻找函数声明
5 执行代码

解释一下什么是闭包,闭包有什么优缺点吗?

1. 函数的执行,导致函数被定义 
2. 闭包和函数的定义有关 
3. this指向和函数的执行方式有关

优点: 避免全局变量的污染,同时,局部变量没有被销毁,驻留在内存中,还可以被访问
缺点: 使用不当,会造成内存泄露
1、函数里面包含的子函数,子函数访问父函数的局部变量
2、通过return将子函数暴露在全局作用域,子函数就形成闭包
3、通过闭包,父函数的局部变量没有被销毁,可通过闭包去调用,但同时,这个局部变量也不会被全局变量
污染

闭包存在的意义

1.延长变量的生命周期
2.创建私有环境(变量)

闭包的优点和缺点

优点: 避免全局变量的污染,同时,局部变量没有被销毁,驻留在内存中,还可以被访问
缺点: 使用不当,会造成内存泄露

闭包使用场景

blog.csdn.net/LTT1995/art…

vue的导航守卫API

全局前置守卫

router.beforeEach((to, from, next) => { // ... })
to: Route: 即将要进入的目标
from: Route: 当前导航正要离开的路由
next():函数,决定是否展示你要看到的路由页面

全局后置守卫

router.afterEach((to, from) => {
  // ...
})

某个路由的钩子

const router = new VueRouter({
  routes: [
    {
      path: '/login',
      component: Login,
      //component: () => import('../views/login.vue'),
      beforeEnter: (to, from, next) => {
        // ...
      },
      beforeLeave: (to, from, next) => {
        // ...
      }
    }
  ]
})

路由组件的钩子

const Foo = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
     在渲染该组件的对应路由被 confirm 前调用
     不!能!获取组件实例 `this`
     因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate(to, from, next) {
    在当前路由改变,但是该组件被复用时调用
    举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from, next) {
    导航离开该组件的对应路由时调用
    可以访问组件实例 `this`
  }
}

vue双向绑定的理解

Vue数据双向绑定原理是通过数据劫持结合发布者-订阅者模式的方式来实现的,
首先是对数据进行监听,然后当监听的属性发生变化时则告诉订阅者是否要更新,
若更新就会执行对应的更新函数从而更新视图

vue传值方式有哪些

 通过 props 传递
 通过 $emit 触发自定义事件
 使用 ref
 EventBus
 ProvideInject
 vuex

跨域

协议,域名,端口

1、JSONP跨域

 jsonp的原理就是利用`<script>`标签没有跨域限制,
 通过`<script>`标签src属性,发送带有callback参数的GET请求,
 服务端将接口返回数据拼凑到callback函数中,
 返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据。

https://www.imooc.com/article/291…

事件的冒泡捕获,监听事件的参数几个

element.addEventListener(event, function, useCapture);
第一个参数是事件的类型 (如 "click""mousedown").

第二个参数是事件触发后调用的函数。

第三个参数是个布尔值用于描述事件是冒泡还是捕获。该参数是可选的。
e.preventDefault();阻止默认行为
e.stopPropagation() 阻止冒泡,不阻止默认行为(例如a超链接跳转)‘

获取dom的方式

1. getElementById
2. getElementsByTagName
3. getElementsByClassName
4. getElementsByName
5. querySelector
6. querySelectorAll

给DOM节点上添加事件的几种方法

1.直接在标签上添加
<button onclick="function(1)">按钮</button>

2.用方法动态添加
    let temp = document.getElementByClassName("my_button")[0];
    temp.onclick = function(){
    }
    
3.通过监听事件添加
addEventListener

需要把keys设置为全局唯一吗?

不需要,key是用来进行diff算法的时候进行同层比较,
准备的说key只需要在兄弟节点之间唯一,
一般情况key选取是后端定义的id.
万不得已的时候可以选择index(选择index是万不得已的选择,
因为选择了index后,一些操作会改变index的值,
违背了唯一不变,在进行diff算法的时候出现问题)

事件循环机制

Event Loop ( 事件循环 )
浏览器的事件循环分为同步任务和异步任务;所有同步任务都在主线程上执行,
形成一个函数调用栈(执行栈),
而异步则先放到任务队列(task queue)里,
任务队列又分为宏任务(macro-task)与微任务(micro-task)。下面的整个执行过程就是事件循环

宏任务大概包括:
    script(整块代码)
    setTimeout、setInterva
    I/O、UI交互事件
    setImmediate(node环境)

微任务大概包括:
    new promise().then(回调)
    MutationObserver(html5新特新
    Object.observe(已废弃)
    process.nextTick(node环境)
若同时存在promise和nextTick,则先执行nextTick

执行过程
先从script(整块代码)开始第一次循环执行,接着对同步任务进行执行,直到调用栈被清空,
然后去执行所有的微任务,当所有微任务执行完毕之后。
再次从宏任务开始循环执行,直到执行完毕,然后再执行所有的微任务,
就这样一直循环下去。如果在执行微队列任务的过程中,又产生了微任务,
那么会加入整个队列的队尾,也会在当前的周期中执行。

原文链接:https://blog.csdn.net/weixin_52092151/article/details/119788483

react和vue的区别

数据流不同
    vue是响应式的数据双向绑定系统,而react是单向数据流,没有双向绑定
模版渲染
    模板的语法不同,React 是通过JSX渲染模板,
    而Vue是通过一种拓展的HTML语法进行渲染,
    但其实这只是表面现象,毕竟React并不必须依赖JSX1React是一个适合数据经常变化以及构建大型项目的复杂组件;
2Vue是一个更小更灵活适合用来开发单页面程序的简单化组件;

useEffect和useLayoutEffect的区别

useEffect是异步执行的,而`useLayoutEffect`是同步执行的。
useEffect的执行时机是浏览器完成渲染之后,
而 `useLayoutEffect` 的执行时机是浏览器把内容真正渲染到界面之前,
和 `componentDidMount` 等价。

axios和fetch的区别

blog.csdn.net/weixin_4001…

react中hook使用的注意事项

只在最顶层使用 Hook
不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们

HTTP状态码

状态码100 客户端继续其请求。
状态码101 切换协议,服务器根据客户端的请求切换协议,只能切换到更高级的协议。
状态码200 请求成功,一般用于GETPOST请求方式。
状态码201 成功求情并创建了新的资源。
状态码202 已接受请求,但是未处理完成。
状态码203 非授权信息,请求成功,但是返回的meta信息不再原始的服务器,而是一个副本。
状态码204 无内容,服务器处理成功,但是未返回内容,再未更新新网页的情况下,
    可确保浏览器继续显示当前文档。
状态码205 重置内容,服务器成功处理,用户浏览器应重置文档视图,可通过此返回码清除浏览器的表单域。
状态码206 服务器成功处理了部分GET请求。
状态码300
状态码301 永久移动,请求的资源被永久移动到新的URI,返回的信息会包括新的URI,
    浏览器会自动定向到新的URI,今后任何新的请求都应用使用新的URI代替。
状态码302 临时移动,与301类似,但是资源是临时被移动,客户端应继续使用原有的URI。
状态码303 查看其他地址,与301类似,使用GETPOST请求查看。
状态码304 未修改,锁清秋的资源未修改,服务器返回此状态时,不会反悔任何资源。
    客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源。
状态码305 使用代理。所请求的资源必须通过代理访问。
状态码307 临时重定向,和302类似,使用GET的方式去重定向。
状态码400 客户端的求情语法错误,服务器无法理解。
状态码401 请求要求用户身份验证。
状态码402 保留状态码,未启用。
状态码403 服务器理解请求客户端的请求,但是拒绝执行此请求。
状态码404 服务器无法根据客户端的请求找到对应的资源。
状态码405 客户端请求的方法被禁止。
状态码406 服务器无法根据客户端请求的内容特性完成请求。
状态码407 请求要求代理的身份认证,与401类似,但是请求者应当使用代理进行授权。
状态码408 服务器等待 客户端发送的请求时间过长,请求超时。
状态码409 服务器完成客户端的PUT请求是肯恩返回此代码,服务器处理请求的时候发生了冲突。
状态码410 客户端请求的 资源已经不存在,410404不同,如果资源以前有,
    现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置。
状态码411 服务器无法处理客户端发sing的不带Content-Length的请求信息。
状态码412 客户端请求信息的先决条件错误。
状态码413 由于请求的尸体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,
    服务器可能会关闭连接。如果只有服务器暂时无法处理,则会包含一个Retry-After的相应信息。
状态码414 请求的URI过长,服务器无法处理。
状态码415 服务器无法处理请求附带的媒体格式。
状态码416 客户端的请求范围无效。
状态码417 服务器无法满足Expect的请求头信息。
状态码500 服务器内部错误无法请求。
状态码501 服务器不支持请求的功能无法完成请求。
状态码502 充当网关或者代理的服务器,从远端服务器接受到了一个无效的请求。
状态码503 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,
    并且将在一段时间以后恢复。如果能够预计延迟时间,那么响应中可以包含一个 Retry-After 头
    用以标明这个延迟时间。如果没有给出这个 Retry-After 信息那么客户端应当
    以处理500响应的方式处理它。
状态码504 充当网关或者代理的服务器,未及时从远端服务器获取请求。
状态码505 服务器不支持请求的HTTP协议的版本,无法完成处理。

html全局属性是什么

在html中,全局属性是指可以用来配置所有元素共有行为、可以用在任何一个元素身上的
属性。常用全局属性有:class、hidden、id、lang、style、title、dir、
contenteditable等等。
局部属性:有些元素能规定自己的属性,这种属性称为局部属性。 比如 link 元素,它具
有的局部属性有 href、 rel、 hreflang、 media、 type、 sizes 这六个。

==和===

==:运算符称作相等,用来检测两个操作数是否相等,
        这里的相等定义的非常宽松,可以允许进行类型转换
===:用来检测两个操作数是否严格相等

@import和link的区别

加载顺序
  link引入的css在加载页面时同时加载,而@import中的css要在页面加载完毕后加载
从属关系
    link是HTML提供的标签
    @import是css的语法规则,只能加载在style标签内和css文件中
兼容性
    @import是css2.1时提出的,只能用于IE5+,而link不受兼容影响
DOM可控性
    link支持js控制DOM改变样式,而@import不支持

react hook 详解及使用

hooks 是什么

hook是react16(16.8)版本新增的特性,他可以使你在使用函数组件的情况下使用一些类组件里面的方法和特性

useEffect

1.componentDidMount(),在hook中需要使用下面的这种方式去取代,在useEffect中传递第二个参数,
    该参数为一个空数组,只会去执行一次
useEffect(() => {
},[])
2.componentDidUpdate(),有两种方式去解决
    a. 在每次渲染的时候都去调用hooks,解决的方式如下面所示
    useEffect(() => {
    })
    b.  用一个特殊变量的去触发hook,如下所示,count指的就是这个特殊的变量,
        该hook触发,只会是count的值改变时
    useEffect(() => {
    },[count])
3.componentWillUnmount(),用hook来代替,需要去return一个callback(回调函数),如下面的形式所示
    useEffect(() => {
        return () => {
            //执行的为componentWillUnmount
        }
    },[])

useMemo

shouldComponentUpdate(),常使用React.memo来代替,在默认情况下,
它将对props对象中的复杂对象进行浅层比较,如果想要去控制比较,
可以去提供一个自定义的比较函数作为第二个参数。代替hook的方式如下所示

react合成事件

react合成事件指的是react用js模拟了一个Dom事件流,
原生事件就是js的原生事件,如通过document.addEventListener来设置的监听事件。

React有自己的一套事件机制,它重新封装了绝大部分的原生事件。

合成事件采用了事件池,这样做可以大大节省内存,而不会频繁的创建和销毁事件对象。
React并不是将click事件绑在该div的真实DOM上,而是在document处监听所有支持的事
件,当事件发生并冒泡至document处时,React将事件内容封装并交由真正的处理函数运
行。

合成事件的一些特点总结:
-   React 上注册的事件最终会绑定在document这个 DOM 上,而不是 React 组件对应
的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事
件)
-   React 通过队列的形式,从触发的组件向父组件回溯,然后调用他们 JSX 中定义的 
callback
-   React 通过对象池的形式管理合成事件对象的创建和销毁,减少了垃圾的生成和新对
象内存的分配,提高了性能

usememo和usecallback区别

`useMemo``useCallback`接收的参数都是一样(第一个参数是个函数,第二个参数是个数组。),
都是在其依赖项发生变化后才执行,
都是返回缓存的值,区别在于`useMemo`返回的是函数运行的结果,`useCallback`返回的是函数。

多页面的开发,单页面的开发

多页面的开发:
弊端:不能实现局部dom元素的更新,只能实现全部(dom对象)更新(切换页面)
好处:页面独立,dom少(dom渲染的时候速度和效率最高)
元素冗余

单页面的开发:
弊端:有dom操作,维护不易
好处:dom都在一个页面内,开发的时候容易(轻松),需要通过js的操作实现dom切换(行为逻辑)

watch---component

watch

watch:属性监听
1.方法名不能随便定义,需要和监听的属性值的名称保持一致
2.不需要手动调用
3.他不是函数,是一个属性值
4.返回值可有可无,不是强制性(return5.没有缓存
6.单属性监听,不能多属性监听

handler(newVal) {}
immediate: true, // 开启默认调用(第一次直接调用执行)
deep: true // 就是开启深度监听的配置项

component

computed:计算属性监听
为了监听data或者props的属性值,
写法类似于函数,但是他不是函数,是一个属性值,属性值名称可以自定义,必须有返回值,
他有自己的作用域(缓存),如果数据值不在变化或者与之前一样,
就不在执行执行体,而是从缓存中获取数据结果直接返回。

1.他不是函数,是一个属性值
2.必须有返回值(return3.他有自己的作用域(缓存)
4.而是从缓存中获取数据结果直接返回
5.多属性值的监听

区别

watch中的函数是不需要调用的
computed内部的函数调用的时候不需要加()(内部的不是函数,而是属性对象只有get方法时的简写,
其实它是属性对象)
 
watch  属性监听 监听属性的变化
computed:计算属性通过属性计算而得来的属性
 
watch需要在数据变化时执行异步或开销较大的操作时使用
对于任何复杂逻辑或一个数据属性在它所依赖的属性发生变化时,也要发生变化,
这种情况下,我们最好使用计算属性computed。 
 
computed 属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;

computed中的函数必须用return返回最终的结果

当computed中的函数所依赖的属性如果没有发生改变的时候,那么调用当前函数的时候结果会从缓存中读取

watch 一个对象,键是需要观察的表达式,值是对应回调函数。
主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;

get和post区别

1GET请求,请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数用&连接。
URL的编码格式采用的是ASCII编码,而不是uniclde,即是说所有的非ASCII字符都要编码
之后再传输

POST请求:POST请求会把请求的数据放置在HTTP请求包的包体中。
上面的item=bandsaw就是实际的传输数据。
因此,GET请求的数据会暴露在地址栏中,而POST请求则不会。

2、传输数据的大小
在HTTP规范中,没有对URL的长度和传输的数据大小进行限制。
但是在实际开发过程中,对于GET,特定的浏览器和服务器对URL的长度有限制。
因此,在使用GET请求时,传输数据会受到URL长度的限制。
对于POST,由于不是URL传值,理论上是不会受限制的,
但是实际上各个服务器会规定对POST提交数据大小进行限制,ApacheIIS都有各自的配置。

3、安全性
POST的安全性比GET的高。
这里的安全是指真正的安全,而不同于上面GET提到的安全方法中的安全,
上面提到的安全仅仅是不修改服务器的数据。比如,在进行登录操作,通过GET请求,
用户名和密码都会暴露再URL上,因为登录页面有可能被浏览器缓存以及其他人
查看浏览器的历史记录的原因,此时的用户名和密码就很容易被他人拿到了。
除此之外,GET请求提交的数据还可能会造成Cross-site request frogery攻击

get和psot使用场景

1、get方式的安全性较Post方式要差些,包含机密信息的话,建议用Post数据提交方式;
2、在做数据查询时,建议用Get方式;而在做数据添加、修改或删除时,建议用Post方式;区别表现如下:
    1. get是从服务器上获取数据,post是向服务器传送数据。

ajax

    get({url, query, callback}) {
        const xhr = new XMLHttpRequest()
        xhr.open('GET', `${url}?${this.parser(query)}`)
        xhr.send()
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                callback(xhr.responseText)
            }
        }

    },
    post({ url, body = {}, header = {}, callback }) {
        const xhr = new XMLHttpRequest()
        xhr.open('POST', url)
        Object.keys(header).forEach(item => {
            xhr.setRequestHeader(item, header[item])
        })
        xhr.send(JSON.stringify(body))
        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
                callback(xhr.responseText)
            }
        }
    }

www.itcast.cn/news/202106…

判断对象是否为空

`a.`将对象转换成字符串,再判断是否等于“{}”
`b.`Object.keys()方法,返回对象的属性名组成的一个数组,若长度为0,
    则为空对象(ES6的写法)
`c.`Object.getOwnPropertyNames方法获取对象的属性名,存到数组中,
    若长度为0,则为空对象
`d.`for in循环
let result=function(obj){
    for(let key in obj){
        return false;//若不为空,可遍历,返回false
    }
    return true;
}
console.log(result(obj));//返回true

blog.csdn.net/sinat_34626…

构造函数和普通函数的区别

调用方式的不同
1. 构造函数需要使用new运算符调用, 如果构造函数没有参数可以省略小括号, 
    比如new Object2. 普通函数的调用不需要new 运算符, 而且必须有小括号。比如: function(){};

this的指向问题
1.  构造函数的this会绑定到创建的对象实例上;
2. 普通函数的this则属于此函数的调用者;

命名方式
1.  构造函数名称通常首字母要大些;
1.  普通函数名称首字母要小写, 使用驼峰命名方式

js中的new关键字做了什么操作?

1.先创建一个空的对象{}
2.将{}.`__proto__`指向构造函数.`prototype`
3.将构造函数中的this指向这个创建的对象
4.如果构造函数没有返回值或返回值是基本数据类型,则将这个新创建的对象返回
5.如果构造函数的返回值是引用数据类型,则将这个对象返回。

原文链接

实现一个方法sameNumbers,找出同时存在于两个数组的所有数字

• 需要处理异常传参,情况不限于:
○ 未传入arr1或arr2
○ arr1或arr2不是数组
• 字符串格式的数字需要转为数字,如:'1'需先转化为1再进行查重比较
• 返回结果需要过滤所有非数字项
function sameNumbers(arr1, arr2) {
    if (Array.isArray(arr1) && Array.isArray(arr2)) {
      let newArr = [];
      arr1.forEach((item) => {
        arr2.forEach((itm) => {
          if (Number.parseInt(item) === Number.parseInt(itm)) {
            newArr.push(itm);
          }
        });
      });
      console.log(newArr);
      return newArr.filter((item) => typeof Number.parseInt(item) === "number");
    }
  }
  console.log(sameNumbers(["1", "2", "很嗨", "4"], ["很嗨", "4", 5]));

实现一个方法flatten,将对象扁平化


function flatten(obj) {
    let res = {}
    let fn = ((key, value) => {
        if (Object.prototype.toString.call(key) === '[object Object]') {
            for (let k in key) {
                fn(key[k], `${value}${value? '.' : ''}${k}`)
            }
        } else if (Object.prototype.toString.call(key) === '[object Array]') {
            key.forEach((item, index) => {
                fn(item, `${value}[${index}]`)
            })
        } else {
            res[value] = key
        }
    })
    fn(obj, '')
    return res

}
console.log(flatten({
    a: {
        b: 1,
        c: 2,
        d: { e: 5 }
    },
    b: [1, 3, { a: 2, b: 3 }],
    c: 3
}));

reactDom.createportal封装全局弹窗实现思路,与reactDom.render区别

reactDom.createportal封装全局弹窗实现思路

// 在 DOM 中有两个容器是兄弟级 (siblings)
const appRoot = document.getElementById('app-root');
const modalRoot = document.getElementById('modal-root');

class Modal extends React.Component {
  constructor(props) {
    super(props);
    this.el = document.createElement('div');
  }

  componentDidMount() {
    // 在 Modal 的所有子元素被挂载后,
    // 这个 portal 元素会被嵌入到 DOM 树中,
    // 这意味着子元素将被挂载到一个分离的 DOM 节点中。
    // 如果要求子组件在挂载时可以立刻接入 DOM 树,
    // 例如衡量一个 DOM 节点,
    // 或者在后代节点中使用 ‘autoFocus’,
    // 则需添加 state 到 Modal 中,
    // 仅当 Modal 被插入 DOM 树中才能渲染子元素。
    modalRoot.appendChild(this.el);
  }

  componentWillUnmount() {
    modalRoot.removeChild(this.el);
  }

  render() {
    return ReactDOM.createPortal(
      this.props.children,
      this.el
    );
  }
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {clicks: 0};
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    // 当子元素里的按钮被点击时,
    // 这个将会被触发更新父元素的 state,
    // 即使这个按钮在 DOM 中不是直接关联的后代
    this.setState(state => ({
      clicks: state.clicks + 1
    }));
  }
  render() {
    return (
      <div onClick={this.handleClick}>
        <Modal>
          <Child />
        </Modal>
      </div>
    );
  }
}

function Child() {
  // 这个按钮的点击事件会冒泡到父元素
  // 因为这里没有定义 'onClick' 属性
  return (
    <div className="modal">
      <button>Click</button>
    </div>
  );
}

ReactDOM.render(<Parent />, appRoot);

reactDom.render区别

都是在提供的 container 里渲染 React 元素,
第一个参数child或element是任何可渲染的React元素。
第二个参数container是一个 真实的DOM元素。
主要的区别在于:
(1ReactDOM.render在首次调用时,
    会将容器节点里的所有DOM元素都替换,
    ReactDOM.createPortal是向container下插入一个子节点;
(2ReactDOM.render会直接渲染成DOM元素,
    而ReactDOM.createPortal则是渲染出React元素,
    最终还是需要通过ReactDOM.render渲染成真实DOM

图片懒加载,预加载实现思路

预加载实现思路

预加载是预先加载好后面需要用到的资源, 后面使用的时候直接去缓存里取。
举个栗子, 比如一个网站的开场动画, 这些动画是由很多图片组成的, 
假如不预先加载好, 那就会造成动画不流畅产生闪动白屏。
图片是提高用户体验的一个很好方法。图片预先加载到浏览器中,
保证了图片快速、无缝地发布,使用户在浏览你网站内容时获得更好的用户体验。

实现预载的方法非常多,可以用CSS(background)、JS(Image)、HTML(<img />)都可以。
常用的是new Image();,设置其src来实现预载,再使用onload方法回调预载完成事件。
只要浏览器把图片下载到本地,同样的src就会使用缓存,这是最基本也是最实用的预载方
法。当Image下载完图片头后,会得到宽和高,因此可以在预载前得到图片的大小(我所知
的方法是用记时器轮循宽高变化)。一般实现预载的工具类,都实现一个Array来存需要预
载的URL,然后实现FinishErrorSizeChange等常用事件,可以由用户选择是顺序预载
或假并发预载。JqueryPreLoad可以用于预载。

原文链接:blog.csdn.net/yang114544/…

图片懒加载

延迟加载图片或符合某些条件时才加载某些图片。
这样做的好处是减少不必要的访问数据库或延迟访问数据库的次数,
因为每次访问数据库都是比较耗时的即只有真正使用该对象的数据时才会创建。
懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。

图片预加载与懒加载的区别:

第一种是纯粹的延迟加载,使用setTimeOut或setInterval进行加载延迟,
如果用户在加载前就离开了页面,那么就不会加载。 
第二种是条件加载,符合某些条件,或触发了某些事件才开始异步下载。
第三种是可视区加载,即仅加载用户可以看到的区域,这个主要由监控滚动条来实现,
一般会在距用户看到某图片前一定距离遍开始加载,这样能保证用户拉下时正好能看到图片。

两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。
懒加载对服务器前端有一定的缓解压力作用,预载则会增加服务器前端压力。

JS找到字符串中最长且连续不重复的值


    const string = 'widoiadiaoewole';
    // 字符串转数组
    const stArr = string.split('')
    let contArr = []
    
    stArr.forEach((item, index) => {
        console.log(`###===第 ${index +1} 轮循环开始===###`)
        // 进入下次循环时清空数组盒子从头开始
        let itemArr = []
        for (var i=index;i<stArr.length;i++) {
            // 如果没有重复的就放到内容数组
            console.log('includes:', itemArr, stArr[i], index, stArr.length)
            if(!itemArr.includes(stArr[i])) {
                itemArr.push(stArr[i])

            } else {
                // 查看在if中收集的最终内容数据
                console.log('====itemArr:',itemArr)
                // 如果有重复的了就先把之前的放到数组盒子,清空内容数组重新赋值
                contArr.push(itemArr)
                itemArr = []
                itemArr.push(stArr[i])
            }
        }
        
    })
    // 将最长的放到第一位
    let sArr = contArr.sort((a, b) => {
        return b.length - a.length
    })
    console.log('排序后结果集:', sArr)
    // 转回字符串
    const str = sArr[0].join('')
    
    console.log('最后结果 ===', str)
    

JS实现千位分隔符

function format (num) {
	var reg=/\d{1,3}(?=(\d{3})+$)/g; 
		return (num + '').replace(reg, '$&.');
	}
console.log(format(1234567890));
正则表达式 \d{1,3}(?=(\d{3})+$)  表示前面有1~3个数字,后面的至少由一组3个数字结尾。

?=表示正向引用,可以作为匹配的条件,但匹配到的内容不获取,并且作为下一次查询的开始。

$& 表示与正则表达式相匹配的内容,具体的使用可以查看字符串replace()方法的

版本

Semantic Versioning 是一个前端通用的版本定义规范。
格式为“{MAJOR}.{MINOR}.{PATCH}-{alpha|beta|rc}.{number}”,
要求实现 compare(a, b) 方法,比较 a, b 两个版本大小。

•当 a > b 是返回 1;

•当 a = b 是返回 0;

•当 a < b 是返回 -1;

•其中,rc > beta > alpha,major > minor > patch;

例子,1.2.3 < 1.2.4 < 1.3.0.alpha.1 < 1.3.0.alpha.2 < 1.3.0.beta.1
< 1.3.0.rc.1 < 1.3.0


function compare(a, b) {
    a = a.split('.')
    b = b.split('.')
    // 你的代码 
    let index = Math.min(a.length, b.length)
    for (let i = 0; i <= index; i++) {
        if (i != 3) {
            if (a[i] > b[i]) {
                return 1
            } else if (a[i] < b[i]) {
                return -1
            }
        } else {
            let va = a[i] === 'rc' ? 1 : a[i] === 'beta' ? 2 : 3
            let vb = b[i] === 'rc' ? 1 : a[i] === 'beta' ? 2 : 3
            if (va > vb) {
                return 1
            } else if (va < vb) {
                return -1
            }
        }
    }
    return 0
}
console.log(compare('1.2.4.bata', '1.2.5.rc'));

redux数据持久化

redux数据持久化

redux数据持久化2

react是什么

是一个用于构建用户界面的js库

redux是一个专门用于状态管理的js库

作用 集中式管理react应用中多个组件共享的状态

什么情况下用到redux

1.一些组件的状态,需要让其他组件随时可以拿到(共享)
2.一个组件需要改变其他组件的状态(通信)
3.能不用就不用,不用比较吃力才考虑用

优点缺点

优点

   edux轻量,生态丰富,可以结合流行的`redux-thunk``redux-saga`等进行使用
   Redux的写法比较固定,团队应用中风格比较稳定,提高协作中的可维护性
   因为Redux中的reducer更新时,每次return的都是不可变对象,所以时间旅行操作相对容易

缺点

  每一次的dispatch都会从根reducer到子reducer嵌套递归的执行,所以效率相对较低;
  Redux核心是不可变对象,在Reducer中的操作都要比较小心,注意不能修改到state的属性
  Redux中写法固定,模板代码较多

redux执行流程

1.store通过reducer创建初始状态
2.view通过store.getState()获取公共状态
3.view改变状态,需要调用action里面的方法,action里面被调用的方法会通过
store.dispatch()方法传递行为标识给reducer
4.reducer接收到action并根据传递过来来的行为标识来改变状态
5.store中改变的状态会被view重新获取

CSS加载会造成阻塞吗?(dom树生成,阻塞dom渲染,阻塞js的执行)不会,会,会

GUI=>渲染css
js引擎(v8)=>实现效果
css和js是互斥的

单页面应用与多页面应用区别

定义

SPA单页面应用(SinglePage Web Application),指只有一个主页面的应用
(一个html页面),一开始只需要加载一次js、css的相关资源。
所有内容都包含在主页面,对每一个功能模块组件化。
单页应用跳转,就是切换相关组件,仅仅刷新局部资源。

MPA多页面应用(MultiPage Application),指有多个独立页面的应用
(多个html页面),每个页面必须重复加载js、css等相关资源。
多页应用跳转,需要整页资源刷新。

区别

1.刷新方式
    SPA:相关组件切换,页面局部刷新或更改
    MPA:整页刷新
2.路由模式
    SPA:可以使用hash,也可以使用history
    MPA:普通链接跳转
3.用户体验
    SPA:页面片段间时间的切换快,用户体验良好,当初次加载文件过多时,需要做相关调优。
    MPA:页面切换加载缓慢,流畅度不够,用户体验比较差,尤其网速慢的时候
4.转场动画
    SPA:容易实现转场动画
    MPA:无法实现转场动画
5.数据传递
    SPA:容易实现数据传递,方法有很多(通过路由带参数传值,Vuex传值等等)
    MPA:依赖url传参,cookie,本地存储
6.搜索引擎优化(SEOSPA:需要单独方案,实现较为困难,不利于SEO检索,可利用服务器端渲染(SSR)优化
    MPA:实现方法容易
7.使用范围
    SPA:高要求的体验度,追求界面流畅的应用
    MPA:适用于追求高度支持搜索引擎的应用
8.开发成本
    SPA:较高,长需要借助专业的框架
    MPA:较低,但也页面代码重复的多
9.维护成本
    SPA:相对容易
    MPA:相对复杂
10.结构
    SPA:一个主页面+许多模块的组件
    MPA:许多完整的页面
11.资源文件
    SPA:组件公用的资源只需要加载一次
    MPA:每个页面都需要自己加载公用的资源

原文链接:blog.csdn.net/m0_45070460…

react-router 路由的实现原理

链接:www.jianshu.com/p/77827bfa6…

vue的虚拟dom和react虚拟dom

react中数据发生变化(调用setstate时),
render函数就会执行,重新生成一个新的虚拟dom,
这个虚拟dom和旧的虚拟dom做比较,得出差异然后渲染。

而vue组件中数据发生变化,
由于数据变化会触发setter,
由于vue组件中数据的getter的作用,收集了依赖,
setter触发会根据这些依赖,生成新的虚拟dom,
然后对比新旧虚拟dom进行渲染。

链接1

vue与react虚拟dom对比

数组的方法

数组的方法

useMemo和useCallback区别

useCallback 和 useMemo 在第一次渲染组件的时候,就会执行,拿到各自的缓存;

之后,当它们的依赖发生改变的时候,才会执行。

useCallback缓存的是`函数`

useMemo缓存的是`值`
两者之间的比较:

useMemo和useCallback都是reactHook提供的两个API,用于缓存数据,
优化性能;两者接收的参数都是一样的,
第一个参数表示一个回调函数,
第二个表示依赖的数据。

共同作用
在依赖数据发生变化的时候,
才会调用传进去的回调函数去重新计算结果,
起到一个缓存的作用

两者的区别
useMemo 缓存的结果是回调函数中return回来的值,
主要用于缓存计算结果的值,
应用场景如需要计算的状态

useCallback 缓存的结果是函数,
主要用于缓存函数,
应用场景如需要缓存的函数,
因为函数式组件每次任何一个state发生变化,
会触发整个组件更新,一些函数是没有必要更新的,
此时就应该缓存起来,提高性能,减少对资源的浪费;
另外还需要注意的是,useCallback应该和React.memo配套使用,
缺了一个都可能导致性能不升反而下降。

防抖节流

防抖

防抖(将多此变为一次)在固定的时间内事件只允许发生一次

<input type="text" placeholder="电话" class="inp">
   function get(name) {
        return document.querySelector(name)
    }
    // 防抖
    get('.inp').addEventListener('input', antiShake(demo,2000))
    function antiShake(fn, wait) {
        let timeout = null
        return args => {
            if (timeout) {
                clearTimeout(timeout)
            }
          timeout=setTimeout(fn,wait)
        }
    }
    function demo(){
        console.log('发起请求');
    }

节流

一定时间只调用一次,应用场景 提交表单 高频的监听事件

function  get(){
let time=new Date().getTime()
let t=null
return function(){
let cur=new Date().getTime();
   clearTimeout(t);
   if (cur - begin >= delay) {
      fn.apply(_self, args);
      begin = cur;
    } else {
      t = setTimeout(function () {
        fn.apply(_self, args);
      }, delay);
    }
  }
}

原型原型链

原型  prototype  =>函数特有的
原型链 __ptoto__ => [[prototype]]
常规的对象和数组都是没有原型的
从当前实例属性查找,找到了返回,否则顺着原型链一层一层向上找
知道null为止,如果null也没找到报错


当你试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,
那么它会去它的隐式原型 __proto__
(也就是它的构造函数的显式原型 prototype)中寻找。

在这里插入图片描述

为什么选着react

React函数式编程理念使代码更优雅和合理
严谨的单向数据流设计,方便构建大型复杂稳定的单页面应用
丰富的技术生态圈,拥有世界范围内各大技术社区支持
容易维护
更容易实现前端自动化测试

为什么选着vue

视图层(html/css)和逻辑层(javascript)分开
vue两大特点:响应式编程、组件化 
vue的优势:轻量级框架、简单易学、双向数据绑定、组件化、视图、数据和结构的分离、
虚拟DOM、运行速度快

mvc和mvvm区别是什么

mvc和mvvm区别是:
1、处理业务的模式不同,MVC里,View是可以直接访问Model,而MVVM是将页面与数据逻
辑分离的模式,它把数据绑定工作放到一个JS里去实现;
2、处理数据操作不同,MVVM通过数据来显示视图层而不是节点操作

this的理解

1thisJavascript语言的一个关键字,它代表函数运行时自动生成的一个内部对象,
只能在函数内部使用。this指的是调用函数的那个对象。his的指向在函数定义的时候是确
定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那
个调用它的对象

call

第一各参数是this指向,后面可以添加多个参数,函数立即执行

apply

第一各参数是this指向,第二个是数组,数组内部是参数,函数立即执行

bind

第一各参数是this指向,后面可以添加多个参数,返回一个新的函数还需要进行调用

call和apply都是改变上下文中的this并立即执行这个函数
bind方法可以让对应的函数想什么时候调就什么时候调用,并且可以将参数在执行的时候添加,这是它们的区别
​
call()、bind()、apply()的用法,改变this的指向,区别在于
f.call(obj, arg1, arg2…),
f.bind(obj, arg1, arg2,…)(),
f.apply(obj, [arg1, arg2, .])

juejin.cn/post/706994…

自定义bind

const a = {
        name: 'test'
    }
    function say() {
        console.log(this.name);
    }
    Function.prototype.myBind = function (oThis) {
        if (typeof this !== 'function') {
            return;
        }
        var self = this
        var args=[...arguments]
        return function () {
        console.log(args);
​
            return self.apply(oThis,[...args]); 
        }
    }
    say.myBind(a,2)()

自定义apply

    var foo = {
      count: 1
    };
    function bar() {
      console.log(this.count);
    }
    bar.myApply(foo); // 1
--------------------------------------------------------------------
Function.prototype.myApply = function(context) {
  var context = context || window;
  context.fn = this;
  var result;
  // 判断第二个参数是否存在,也就是context后面有没有一个数组
  // 如果存在,则需要展开第二个参数
  if (arguments[1]) {
    result = context.fn(...arguments[1]);
  } else {
    result = context.fn();
  }
  delete context.fn;
  return result;
}

自定义call

    var foo = {
      count: 1
    };
    function bar() {
      console.log(this.count);
    }
    bar.myCall(foo); // 1Function.prototype.myCall = function(context) {
      // 取得传入的对象(执行上下文),比如上文的foo对象,这里的context就相当于上文的foo
      // 不传第一个参数,默认是window,
      var context = context || window;
      // 给context添加一个属性,这时的this指向调用myCall的函数,比如上文的bar函数
      context.fn = this;//这里的context.fn就相当于上文的bar函数
      // 通过展开运算符和解构赋值取出context后面的参数,上文的例子没有传入参数列表
      var args = [...arguments].slice(1);
      // 执行函数(相当于上文的bar(...args))
      var result = context.fn(...args);
      // 删除函数
      delete context.fn;
      return result;
    };

清除浮动

方法一:额外标签法
  给谁清除浮动,就在其后额外添加一个空白标签 ,给其设置clear:both。
  优点:通俗易懂,书写方便。
  缺点:添加许多无意义的标签,结构化比较差。

方法二:父元素添加overflow:hidden
  通过触发BFC方式,实现清除浮动
  优点:代码简洁
  缺点:内容增多的时候容易造成不会自动换行导致内容被隐藏掉,无法显示要溢出的元素。

方法三:使用after伪元素清除浮动
  优点:符合闭合浮动思想,结构语义化正确。
  缺点:ie6-7不支持伪元素:after,使用zoom:1触发hasLayout。

方法五:为父元素设置高度
  缺点: 需要手动添加高度,如何后面的高度发生变化,还要再次修改高度,给后期的维
  护带来麻烦。
  优点: 简单粗暴直接有效,清除了浮动带来的影响。

重绘和回流的区别

1、 重绘:元素样式的改变(但宽高、大小、位置等不变)
2、 回流:元素的大小或者位置发生改变(当页面布局和几何信息发生改变的时候),触
发了重新布局导致渲染树重新计算布局和渲染
        如添加或删除可见的DOM元素;
        元素的位置发生变化;
        元素的尺寸发生变化、
        内容发生变化(如文本变化或图片被另一个不同尺寸的图片所代替);
        页面一开始渲染的时候(无法避免)
注意:回流一定会触发重绘,而重绘不一定会回流

响应式布局有几种办法

一:媒体查询
使用@media媒体查询可以针对不同的媒体类型定义不同的样式,特别是响应式页面,可以
针对不同屏幕的大小,编写多套样式,从而达到自适应的效果。
二:百分比%
比如当浏览器的宽度或者高度发生变化时,通过百分比单位,通过百分比单位可以使得浏
览器中的组件的宽和高随着浏览器的变化而变化,从而实现响应式的效果。
三:vw/vh
css3中引入了一个新的单位vw/vh,与视图窗口有关,vw表示相对于视图窗口的宽度,vh
表示相对于视图窗口高度。 任意层级元素,在使用vw单位的情况下,1vw都等于视图宽度
的百分之一。
四:rem
rem单位是相对于字体大小的html元素,也称为根元素。 默认情况下,html元素的font-
size为16px。所以此时1rem = 16px。
五:flex弹性布局
弹性布局只需要依赖于CSS样式的实现响应式布局的方式,也是最多用到的一种实现响应式
的方法。

package.json文件

package.json是一个项目描述文件, 里面记录了当前项目的信息。
eg: 项目名称、
gitHub地址
、当前项目依赖哪些第三方模块等。 
使用npm安装第三方模块,
是模块的相关信息会自动添加到package.json文件中

web前端优化策略

(1) 减少HTTP请求数
(2) 从设计实现层面简化页面
(3) 合理设置HTTP缓存
(4) 资源合并与压缩
(5) css sprite
(6) 网页内联图片
(7) 图片懒加载

从url到页面显示发生了什么

1. 构建请求
2. 网络传输
3. 构建响应
4. 网络传输
5. 浏览器渲染页面
1.DNS 解析域名 (找到要去哪里)
2. TCP 三次握手 (找到服务器之后要交流下,征求下服务器意见)
3. 浏览器发出请求 (服务器同意之后,发送请求)
4. 服务器处理请求并返回页面信息 (服务器收到请求之后,把请求内容返回)
5. 浏览器解析渲染 (拿到返回内容,开始渲染页面)
原文链接:https://blog.csdn.net/Hey_guy_struggle/article/details/119953269
1.1 dns(域名解析)解析转换成IP  先去浏览器缓存查找->再去找系统缓存->再去找路由器缓存->运营商缓存->根域->顶级域名服务器->祖域服务器
1.2在应用层封装http 请求报文;请求行,头,体,响应行头,体
1.3在传输层建立tcp链接(添加tcp报头  然后添加ip报头)
1.4 数据链路层,物理层

5.1 解析html
    解码:unicode码
    分词:把字节流(unicode码)分成短语  Token
    解析:构建节点
    建树:构建dom树
    
    解析css
    ....
    解析css会构建  render 树和renderLaryTree
    
    解析js
    
    回流和重绘

DNS

dns.png

URL

url.png

url(1).png

udp---tcp--http

tcp和udp的区别有:1、udp是无连接的,tcp是面向连接的;2、udp是不可靠传输,tcp是
可靠传输;3、udp是面向报文传输,tcp是面向字节流传输。
udp是无连接协议
tcp建立可靠连接的协议
HTTPS:超文本传输协议(Hypertext Transfer ProtocolHTTP)
    是一个简单的请求-响应协议,它通常运行在TCP之上。
    它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。
    请求和响应消息的头以ASCII形式给出;而消息内容则具有一个类似MIME的格式。
    这个简单模型是早期Web成功的有功之臣,因为它使开发和部署非常地直截了当。
TCPUDP区别总结:
1TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要
建立连接
2TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,
且按序到达;UDP尽最大努力交付,即不保   证可靠交付
3TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
  UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有
  用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5TCP首部开销20字节;UDP的首部开销小,只有8个字节
6TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
————————————————
版权声明:本文为CSDN博主「码城」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附
上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Li_Ning_/article/details/52117463

udp---tcp.png

blob对象

二进制大对象(binary large object)存放二进制的容器

文件的下载
<a href="" download=""></a> 跳转的地址同源会进行下载
URl.createObjectURL将bolb对象变为url地址h5新增
<a id="btn">下载</a>
<input type="file" name="" id="inp">
function get(name) {
        return document.querySelector(name)
    }
    var str='123'
    var blob=new Blob([str],{
        type:'text/plain'
    })
    blob.text().then((res)=>{
        console.log(res);123
    })
 
    var str=`<div>
        <p>123</p>
    </div>`
    var blob=new Blob([str],{
        type:'text/html'
    })
    get('#btn').onclick=function(e){
        this.setAttribute('download','123.html')
        this.href=window.URL.createObjectURL(blob)
    }



    get('#inp').onchange = function (e) {
        let file = e.target.files[0]
        var a=document.createElement('a')
        a.setAttribute('download','mybaidu.html')
        a.href=window.URL.createObjectURL(file)
        a.click()
    }


    get('#inp').onchange = function (e) {
        let file = e.target.files[0]
        console.log(file);
        var img=new Image()
        var filder=new FileReader()
        document.body.appendChild(img)
        filder.onload=function(){
            img.src=filder.result
        }
        filder.readAsDataURL(file)
    }

定义一个变量发生了什么

1.JS引擎解析和渲染js代码的时候,提供一个运行环境,让JS代码执行全局作用域(scope)
2.在栈空间开辟一块内存空间,把10存储进去
3.在当前的作用域中,声明一个变量a (var,let ,const都是声明变量)
4.让声明的变量与存储的10进行关联(把存储的10赋值给了变量a ,赋值操作才叫定义变量)

将a的值进行了复制
将a的值在内存中开辟一块新空间存放a的值,这个a和b是没有关系的
let a=10
let b=a

继承

原型链继承

关键代码 Child.prototype = new Person();
缺点不能传递参数,
2,如果父类的属性是引用类型,子类实列修改了该属性,其他的子类实列会会共享该属性

构造函数继承

function Person(name){
	this.name = name;
	this.hobby  = ['唱歌','踢球','跑步']
}
Person.prototype.eat = function(){
	console.log("好吃");
}
//子类
function Child(age,name){
	this.age = age;
	Person.call(this,name)
}
 缺点:
 1,子类无法继承父类在原型链上的属性和方法。
 2,每个实例都拷贝一份,占用内存大,尤其是方法过多的时候。
 (函数复用又无从谈起了,本来我们用 prototype 就是解决复用问题的)

优点:
解决了通过原型链继承子类对于父类引用类型属性的修改,
导致其他子类实列共享了修改的问题

组合继承

function Person(name){
	this.name = name;
	this.hobby  = ['唱歌','踢球','跑步']
}
Person.prototype.eat = function(){
	console.log("好吃");
}
//子类
function Child(age,name){
	this.age = age;
	Person.call(this,name) //借用构造函数
}
Child.prototype = new Person(); // 原型链
Child.prototype.constructor = Child;
缺点:
组合继承是js最常用的继承模式,
组合继承最大的问题就是无论在什么情况下,
都会调用两次构造函数:
一次是在创建子类型原型时,
另一次是在子类构造函数内部。

判断类型

typeof

typeof可以测试出number、string、boolean、undefinedfunction,
对于null及数组、对象,typeof均检测出为object,不能进一步判断它们的类型。

instanceof

obj instanceof Object ,
可以左边放你要判断的内容,
右边放类型来进行JS类型判断,
只能用来判断复杂数据类型,
因为instanceof 是用于检测构造函数(右边)的 
prototype 属性是否出现在某个实例对象(左边)的原型链上。
console.log(arr instanceof Array);  //true

使用Object.prototype.toString.call

console.log(Object.prototype.toString.call(1));        // [object Number]
console.log(Object.prototype.toString.call('Hello tomorrow')); // [object String ]
console.log(Object.prototype.toString.call(true));     // [object Boolean]
console.log(Object.prototype.toString.call(undefined));  // [object Undefined]
console.log(Object.prototype.toString.call(fn));   // [object Function]
console.log(Object.prototype.toString.call(new Date));  // [object Date]
console.log(Object.prototype.toString.call(null));   // [object Null]
console.log(Object.prototype.toString.call([1, 2, 3]));  // [object Array]
console.log(Object.prototype.toString.call(obj));       // [object Object]