前端面试题

面试题

1.闭包篇

1.1作用域

作用域就是一套规则,用于确定在何处以及如何找变量标识符的规则。(就是查找变量的地方)

1.2 作用域链

函数之间的嵌套形成了作用域链。

1.3 词法作用域

词法作用域 是 作用域的一种工作模式。

在你写代码时将变量和块作用域写在哪里来决定,也就是词法作用域是静态的作用域,在你写代码时就确定了。

1.4 闭包的概念

当函数可以记住并访问它的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

1.5 闭包可能造成的问题:

(1)内存泄漏 解决:使用delete删除

1.6 闭包的作用

(1) 定义模块 (2)立即执行函数 (3)存储数据

2. 防抖与节流

2.1 函数防抖

定义: 在事件被触发N秒后再执行回调,如果在这 n 秒内又被触发,则重新计时

理解:像是法术发技能时 要读条,技能读条还没完成再按技能就会重新读条。

应用于:搜索框

2.2 函数节流

定义: 规定一个单位时间内,只能触发一次函数。如果这个单位时间内多次触发函数,只有一次生效

理解:

3.跨域(非同源请求策略)

方式1:jsonp

原理:利用script不受同源策略的影响

像script、img、link、iframe 等 是不受同源策略的影响。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sa0pO1Hf-1639491671619)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210614230945302.png)]

问题:jsonp只能处理get请求

代码:

客户端
   function jsonp({ url, params, callback }) {

      return new Promise((resolve, reject) => {
        let script = document.createElement('script')

        window[callback] = function (data) {
          resolve(data)
          document.body.removeChild(script)
          //为了在执行完这个函数之后,就将script这个元素给删了
        }

        let arr = []
        // 对象的克隆
        params = { ...params, callback }

        for (let key in params) {
          arr.push(`${key}=${params[key]}`)
        }

        script.src = `${url}?${arr.join('&')}`
        document.body.appendChild(script)
      })


    }
    jsonp({
      url: 'http://localhost:3000/say',
      params: { wd: 'Ilovety' },
      callback: 'show'
    }).then(data => {
      console.log(data)
    })
    
    
    服务端:
    
    const express = require('express')

let app = express()

app.get('/say',function(req,res){
  let {wd,callback} = req.query

  console.log(wd)

  console.log(callback)
  res.send(`${callback}('我也爱你')`)

})

app.listen(3000,function(){
  console.log('3000 is running !')
})

方式2:CORS跨域资源共享

1.客户端(发送ajax/fetch 请求)

2.服务端设置中间键

弊端:如果设置为所有源,这个res.header(“Access-Control-Allow-Origin”, “*”);就不可以携带cookie,如果设置可以携带cookie就只能允许一个访问源。

方式3:http proxy

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8GENACrw-1639491671621)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210614234326691.png)]

port:端口号

progress:显示加载进度

contentBase:目录

方式4:ngnix反向代理

方式5:postMessage

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RA8I6bVv-1639491671621)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210616110534520.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iV2XSKuS-1639491671622)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210616110509753.png)]

postMessage是为数不多可以跨域操作window属性之一,他可以用于解决:

  1. 页面和打开新窗口的数据传递

  2. 多窗口之间消息传递

  3. 页面与嵌套的iframe消息传递。

    postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递

otherWindow.postMessage(message, targetOrigin, [transfer]);

message:将要发送到其他 window 的数据

targetOrigin:允许那些窗口去来访问。其值可以是字符串“*”(表示无限制)或者URL。在不匹配是不会被接受到的。

transfer(可选):是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

// a.html
  <iframe src="http://localhost:4000/b.html" frameborder="0" id="frame" onload="load()"></iframe> //等它加载完触发一个事件
  //内嵌在http://localhost:3000/a.html
    <script>
      function load() {
        let frame = document.getElementById('frame')
        frame.contentWindow.postMessage('我爱你', 'http://localhost:4000') //发送数据
        window.onmessage = function(e) { //接受返回数据
          console.log(e.data) //我不爱你
        }
      }
    </script>




// b.html
  window.onmessage = function(e) {
    console.log(e.data) //我爱你
    e.source.postMessage('我不爱你', e.origin)
 }

方式6 : document.domain + iframe

适用范围:适用于主域相同,子域不同的情况下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sL6e16m1-1639491671623)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210616124920462.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tiRN9d8j-1639491671625)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210616124934861.png)]

7.node中间件代理

实现原理:同源策略是浏览器要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。

需要做到以下几个步骤:

  1. 接受客户端请求
  2. 将请求 转发给服务器
  3. 拿到服务器 响应数据
  4. 将响应转发给客户端

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XX53odYR-1639491671626)(https://user-gold-cdn.xitu.io/2019/1/17/1685c5bed77e7788?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)]

我们先来看个例子:本地文件index.html文件,通过代理服务器http://localhost:3000向目标服务器http://localhost:4000请求数据。

// index.html(http://127.0.0.1:5500)
 <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script>
      $.ajax({
        url: 'http://localhost:3000',
        type: 'post',
        data: { name: 'xiamen', password: '123456' },
        contentType: 'application/json;charset=utf-8',
        success: function(result) {
          console.log(result) // {"title":"fontend","password":"123456"}
        },
        error: function(msg) {
          console.log(msg)
        }
      })
     </script>

// server1.js 代理服务器(http://localhost:3000)
const http = require('http')
// 第一步:接受客户端请求
const server = http.createServer((request, response) => {
  // 代理服务器,直接和浏览器直接交互,需要设置CORS 的首部字段
  response.writeHead(200, {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': '*',
    'Access-Control-Allow-Headers': 'Content-Type'
  })
  // 第二步:将请求转发给服务器
  const proxyRequest = http
    .request(
      {
        host: '127.0.0.1',
        port: 4000,
        url: '/',
        method: request.method,
        headers: request.headers
      },
      serverResponse => {
        // 第三步:收到服务器的响应
        var body = ''
        serverResponse.on('data', chunk => {
          body += chunk
        })
        serverResponse.on('end', () => {
          console.log('The data is ' + body)
          // 第四步:将响应结果转发给浏览器
          response.end(body)
        })
      }
    )
    .end()
})
server.listen(3000, () => {
  console.log('The proxyServer is running at http://localhost:3000')
})

// server2.js(http://localhost:4000)
const http = require('http')
const data = { title: 'fontend', password: '123456' }
const server = http.createServer((request, response) => {
  if (request.url === '/') {
    response.end(JSON.stringify(data))
  }
})
server.listen(4000, () => {
  console.log('The server is running at http://localhost:4000')
})

结果:

上述代码经过两次跨域,值得注意的是浏览器向代理服务器发送请求,也遵循同源策略,最后在index.html文件打印出{"title":"fontend","password":"123456"}

4. Ajax

核心对象,ajax的XMLHttpRequest

1. var xhr=null;  
2. if (window.XMLHttpRequest)  
3.   {// 兼容 IE7+, Firefox, Chrome, Opera, Safari  
4.   xhr=new XMLHttpRequest();  
5.   } else{// 兼容 IE6, IE5 
6.     xhr=new ActiveXObject("Microsoft.XMLHTTP");  
7.   } `
xhr.onreadystatechange=function(){
if(xhr.readyState==4&&xhr.status==200){
 do something
}
}
xhr.open(method,url,是否为异步)
如果为post方法,则需要使用setHeader去设置content-type即发送数据的格式
xhr.send

get 与 post 请求的区别:

  1. get 请求,请求的数据会附加在URL之后,一?分割URL 和传输数据 ,多个参数用&连接。URL的编码格式采用的是ASCll编码,所以所有的非ASCll需要编码后才能传输过去。

  2. 输出数据的大小:
    http规范中没有对url的长度进行限制,但是实际开发中,对于get,特定的浏览器和服务器对URL的长度有限制。因此,GET请求时,传输数据会受到长度的限制。

    对于post,不是url传值,长度是不受限制的。

  3. 安全性

    post的安全性比GET的高。

  4. 幂等性

    get是幂等的,而post不一定是幂等的

5.CSS选择器

5.1 通配符选择器 *

5.2 Id选择器

5.3 类选择器

5.4 元素选择器

选择器的优先级

!important > 行内样式 > ID选择器 >类、伪类、属性>元素、伪元素>继承>通配符

5.5 nth-child 与 nth-of-type

nth-child(n) 选择父元素下某种类型的第n个子元素,如果子元素匹配样式才会显示(其他元素算在内,回收其他元素的影响)

nth-type-of(n) 选择父元素下某种类型的第n个子元素

6. 从输入url到页面显示

6.1 浏览器的主要进程

浏览器是多进程的,浏览器的主要进程有:

(1)Broswer进程:相当于一个大管家。只有一个。负责页面的创建销毁,网络资源的管理、下载等,

(2)第三方插件进程:每启动一个插件就创建一个进程。

(3)GPU进程:用于3D绘制

(4)浏览器渲染进程(浏览器内核)(render进程):每个网页对应一个进程

img

6.2 第一部分 输入网址并解析

6.2.1 URL的组成

URL主要有 协议、主机、端口、路径、参训参数、锚点

img

6.2.2 解析URL

输入URL后,浏览器会解析出协议、主机、端口、路径等信息,并构造一个HTTP请求

1.浏览器发送请求前,根据请求头的 expires 和cache-control (cache-control的优先级高于expries) 判断缓存是否过期 、是否命中了强缓存策略。若命中强缓存策略,就不需要发送http请求,直接在缓存中获取资源。如果未命中则进入下一步。

2.若没有命中强缓存,浏览器就发送HTTP请求,根据请求头last-modified和etag,判断是否命中协商缓存,如果命中,直接从缓存中获取资源。如果没有命中,就进入下一步。

3.如果前两步都没有命中,就直接从服务端获取资源。

6.2.2.1 强制缓存

对于强制缓存,服务器的相应的header 中会用两个字段来表明------Expires 和 Cache-Control

Expries的值为服务器返回数据的到期时间。的那个再次请求的时间小于返回的此时间,就直接使用缓存数据。 由于 服务端的时间 与客户端之间的时间可能有误差,就有了cache-control。cache-control的优先级高于expires

6.2.2.2 协商缓存

last-modify:上一次请求资源时,获得的该资源的最后修改时间

if-Modified-Since:浏览器再次请求服务器的时候,请求头会包含此字段,后面跟着在缓存中获得的最后修改时间。服务端收到此请求头发现有if-Modified-Since,则与被请求资源的最后修改时间进行对比,如果一致则返回304和响应报文头,浏览器只需要从缓存中获取信息即可。 从字面上看,就是说:从某个时间节点算起,是否文件被修改了

  • 如果真的被修改:那么开始传输响应一个整体,服务器返回:200 OK
  • 如果没有被修改:那么只需传输响应header,服务器返回:304 Not Modified

Etag:服务器响应请求时,通过该字段告诉浏览器当前资源在服务端生成的唯一标识符。(生成规则由服务器决定)。

If-None-Match:当服务器再次请求该数据中,浏览器的请求报文会包含此字段,后面的值在缓存中获取的标识。服务器收到此次报文发现If-None-Match就会与被请求资源的唯一标识符进行对比。

不同,说明被修改过,就响应整个资源内容,返回状态码200。

相同,说明没有被修改过,则响应header,浏览器直接从缓存中获取资源,返回状态码304

Etag的弊端:但是实际应用中由于Etag的计算是使用算法来得出的,而算法会占用服务端计算的资源,所有服务端的资源都是宝贵的,所以就很少使用Etag了。

缓存的优点:

减轻服务器的压力, (减压)

减少冗余的数据传递,节省带宽流量(节省流量哦)

加快了网页的加载速度(主要原因 用户体验好)

不同刷新的请求执行过程
浏览器地址栏中写入URL,回车
  • 浏览器发现缓存中有这个文件了,不用继续请求了,直接去缓存拿。(最快)
F5
  • F5就是告诉浏览器,别偷懒,好歹去服务器看看这个文件是否有过期了。于是浏览器就战战兢兢的发送一个请求带上If-Modify-since。
Ctrl+F5
  • 告诉浏览器,你先把你缓存中的这个文件给我删了,然后再去服务器请求个完整的资源文件下来。于是客户端就完成了强行更新的操作.

7. 垃圾回收机制

内存泄漏:用来为引用类型的堆内存,由于某种原因没有释放或者无法释放,导致程序运行速度减慢甚至导致程序崩溃。

7.1 引用计数法

记录每个对象被引用的次数,当引用次数为0的时候,立即进行回收。

优点:

(1)可以立即回收垃圾

(2)因为是即时回收,那么程序不会暂停去单独使用很长时间的GC,那么最大暂停时间也很短。

(3)不用去遍历堆里面所有的活动对象和非活动对象

缺点:

(1)不能解决循环引用的问题

(2)计数器需要占用很大的空间

7.2 标记清除法

分为两个阶段:

标记阶段:对每个活动对象进行标记

清除阶段:将为被标记的对象(即非活动对象)进行清除

标记阶段(如何标记):

GC从全局出发,沿着作用域逐层向里边(深度遍历),当遍历到堆中的对象的时候就将它打上标记,直到遍历到最后一个。

清除阶段:

遍历整个堆内存,将为未被标记的对象进行清除。

优点:

  1. 解决了循环引用的问题
  2. 实现比较简单,一个对象只有标记和未被标记两种状态。

缺点:

1.会造成碎片化

2.再分配时便利次数多

其他回收算法:复制算法

8. webpack

8.1 webpack的作用:

(1)模块打包: 可以将不同模块的文件进行打包整合到一起

(2)编译兼容

(3)能力扩展:通过webpack的Plugins机制,我们在实现模块打包和编译兼容的基础上,可以进一步实现诸如按需加载,代码压缩等一系列功能。对其他资源进行编译的预处理工作。

8.2 loader 与 Plugin 的区别

loader:loader的本质是一个函数,对接收到的内容进行转换进行转换。因为webpack只认识Javascript,loader就像是一个翻译官。

plugin:就是插件,基于事件流框架Tapable,对webpack功能的扩展,在webpack运行的生命周期中,会广播出许多事件,Plugin可以监听这些事件,在合适的机会通过webpack提供的Api改变输出结构。

8.3 使用过那些可以提高开发效率的插件?

1.webpack-merge 提取公共资源,减少代码的重复使用。

2.size-plugin 监控资源体积的变化。

8.4 一些loader 与plugin

file-loader 一般与file-loader搭配使用,功能与file类似,如果文件小于限制的大小,则会返回base64编码,否则就使用file-loader将文件移动到输出的项目中。

使用

const {CleanWebpackPlugin} = require(‘clean-webpack-plugin’)

在使用这个plugin时,要使用对象结构的方式去接受

9. 事件委托

9.1 事件委托是什么?

事件委托: 将原本需要绑定子元素上的响应事件,绑定到父元素或者更外层的元素。

9.2 事件委托的过程

(1)事件捕获阶段:从window对象传到目标节点上(上层传到底层)称为“捕获阶段”,捕获阶段不会响应任何事件

(2)处于目标阶段:在目标节点上触发,称为“目标阶段”

(3)事件冒泡阶段:从目标节点传回window对象(底层传回上层),称为 冒泡阶段,事件委托的原理

9.3 事件委托的优点

(1)可以大量节省内存占用,减少事件注册,

(2)对于新增的子对象无需再次对其绑定。

9.4 阻止默认 和 取消冒泡

(1)event.preventDefault()

(2)event.stopParpogation()

10. diff算法

10.1 当数据发生改变的时候,vue是怎么更新节点的?

首先,根据真实的Dom生成一个 Virtual DOM,当 virtual DOM 某个节点发生改变的时候,会生成一个Vnode。 然后将Vnode 和 OldVnode的元素进行对比, 发现有不一样的地方就直接在真实的DOM上修改,然后将oldVnode的值 为Vnode。

diff的过程:失调用名为patch 的函数,比较新旧节点,边比较边给真实的DOM打补丁。

10.2 virtual DOM 与真实 DOM之间的区别?

虚拟DOM 是将真实Dom的数据抽出来 而生成的一个对象。

10.3 diff的比较方法?

在采取diff算法比较新旧节点的时候,比较只会在同层之间进行,不会进行过跨级比较。

10.4 diff流程图

当数据发生改变的时候,通过发布者通知,然后调用patch函数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3VYIKuWG-1639491671630)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210619152140957.png)]

11. 深浅拷贝(都是相对于对象来说)

浅拷贝:

创建一个新的对象,这个对象有着原始对象属性值的一份精确拷贝。 如果属性是基本类型,拷贝的就是 基本类型的值。 如果是引用类型,拷贝的就是内存地址,所以如果其中的一个类型改变就会影响另一对象。

深拷贝:

将一个对象从内存中,完整的拷贝一份出来,在读内存中重新开辟一个新的区域存放新对象,且修改新对象不会影响原对象

1.1.2 深拷贝的实现

方法一:

Json.parse(JSON.stringify())

方法二:

function clone1(target){

 if(typeof target ==='object'){

  let targetClone ={}

  for(var key in target){

   // 如果为引用类型再次调用该函数

   targetClone[key] = clone1(target[key])

  }

  return targetClone

 }else{

  return target //如果为基本类型 直接返回

 }

}

js

12. CSS中的单位

px: 像素,相对长度单位,像素px 是相对于屏幕显示器的分辨率而言。

em: 相对与当前对象文本的字体大小,如果未设置,则相对于浏览器的默认字体尺寸。

特点: 1.em不是固定的 2.em会继承父级元素的字体大小。

**rem:**相对于根元素的大小,

**%:**一般是相对于父元素来说的

**vw:**vw 视窗宽度 1vw 等于视窗的1%

**vh:**视窗高度

**vm:**css3新单位,相对于视口的宽度或高度中较小的那个。其中最小的那个被均分为100单位的vm

举个例子:浏览器高度900px,宽度1200px,取最小的浏览器高度,1 vm = 900px/100 = 9 px。

13. cookie ,sessionStorage、lacalStorage

三者的异同:

13.1 生命周期:

cookie:可设置失效时间,没有设置的话,默认关闭浏览器后失效

localStorage:除非被手动清除,否则将会永久保存。

sessionStorage: 仅在当前网页会话下有效,关闭页面或浏览器后就会被清除。

13.2 存放数据的大小:

cookie:4KB左右

localStorage和sessionStorage:可以保存5MB的信息。

13.3 http请求:

cookie:每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题

localStorage和sessionStorage:仅在客户端(即浏览器)中保存,不参与和服务器的通信

13.4 应用场景:

localStorage和sessionStorage唯一的差别一个是永久保存在浏览器里面,一个是关闭网页就清除了信息。localStorage可以用来跨页面传递参数,sessionStorage用来保存一些临时的数据,防止用户刷新页面之后丢失了一些参数。

14. js的类型判断

方法1: typeof

一般只用来判断基本的数据类型,但是NUll除外,typeof Null 的类型为object

typeof(undefined); // undefined typeof(null); // object typeof(true); // boolean typeof(1); // number typeof(''); // string typeof(Symbol(1)); // symbol typeof(function () {}); // function typeof([]); // object typeof({}); // object typeof(new Date()); // object typeof(/abc/ig); // object typeof(Math); // object typeof(new Error('error')); // object

方法2:intenceof

instenceof 用来判断对象类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。但是,并不适用于一些基本数据类型。

1 instanceof Number; // false var num = new Number(1); num instanceof Number; // true

方法3:Object.prototype.toString.call(xxx)

15. 伪数组转化为数组

方式1:[].slice.call(arr)
方法2: Array.from() es6新增

16. position属性

16.1 static

static是position的默认属性,元素会按正常的文档流进行排序,不会受到top,bottom,left,right的影响

16.2 relative

relative相对定位的元素会较正常文档流中的位置进行偏移,受top,bottom,left,right的影响。就是元素还在文档流中像static一样占着位置,但视觉上会有偏移,多用于absolute绝对定位的父元素。

16.3 absolute

absolute绝对定位会脱离正常的文档流,相对于最近的进行过定位的**(非static)父级元素定位,若没有父级元素进行过定位,则相对于即浏览器窗口**定位

16.4 fixed

fixed固定定位同样是脱离正常的文档流,一直相对于浏览器窗口定位,无论页面如何滚动,此元素总在屏幕的同一个位置。

16.5 sticky

sticky粘性定位需要指定 top,right,bottom,left 四个阈值其中之一才会生效。

阈值为此元素在可视区域中与边界的距离,跨越特定阈值前为相对定位(relative),当页面滚动导致此元素跨越阈值则变为固定定位(absolute)。

17. Promise的状态

promise可能有三种状态:等待(pending)、已完成(fulfilled)、已拒绝(rejected)

特点:

不能逆向转换

then接受两个参数,一个是成功时的回调,一个是失败时的回调。

18. 伪元素与伪类的区别总结

根本区别在于:它们是否创造了新的元素。

伪类:用于向某些选择器添加特殊的效果

伪元素:用于将特殊的效果添加到某些选择器

伪类的效果可以通过添加实际的类来实现
伪元素的效果可以通过添加实际的元素来实现

19. 函数柯里化

就是讲一个可以接受多个参数的函数转化为可以接受单个参数的函数,并且返回接收余下参数且返回结果的新技术。


function curry2(fn, args) {
  var len = fn.length

  var args = args || []
  // var args = Array.from(arguments).shift()

  return function () {
    let newArgs = args.concat(Array.from(arguments))
// 如果传入参数长度小于len,就在此执行该函数
if (newArgs.length < len) {
     return curry2.call(this, fn, newArgs)
     }
else {
     // 如果长度不小于就执行
     return fn.apply(this, newArgs)
     }
  }
}

20. 不改变原数组的方法

filter()concat()slice()

21. JS中的继承

21.1 原型链继承

原理:将子类函数的原型指向父类原型的实例

缺点:

1.子类不能给父类传参

2.子类实例共用父类中引用的属性

代码:

function Parent() {
    this.age = 19 this.colors = ['red', 'blue', 'black']
}
Parent.prototype.sayName = function () {
    console.log('我是' + this.age)
}

function Child(friend) {
    this.friend = friend
}
Child.prototype = new Parent() 
let c1 = new Child('xss') 
c1.age = 20 
c1.colors.push('pink') 
let c2 = new Child('xcrf') 
console.log(c2.colors) //[ 'red', 'blue', 'black', 'pink' ]
console.log(c1.age,c2.age)  //20 19c1.sayName()  //我是20c2.sayName()  //我是19

21.2 盗用构造函数继承

原理:利用call或者apply 可以改变父函数this的指向

缺点:只能在构造函数中定义函数,不能使用 父函数原型中的属性

优点:可以传参

代码:

function Parent(like) {
    this.like = like 
    this.age = 19 
    this.colors = ['red', 'blue', 'black']
}
Parent.prototype.sayName = function () {
    console.log('我是' + this.age)
}

function Child(friend, like) {
    Parent.call(this, like) 
    this.friend = friend
}
let c1 = new Child('箭头函数', 'study') 
c1.colors.push('浏览器进程') 
// c1.sayName()  //c1.sayName is not a function 不能访问
let c2 = new Child('GPU进程','play') 
console.log(c1.like,c2.like)//study play  表示传参成功console.log(c1.colors)  
//[ 'red', 'blue', 'black', '浏览器进程' ]
console.log(c2.colors)  //[ 'red', 'blue', 'black' ]

21.3 组合式继承

原理:结合了原型链继承 和 构造函数继承

优点:两者的优点

缺点:

代码:

function Parent(like) {
    this.like = like 
    this.age = 19 
    this.colors = ['red', 'blue', 'black']
}
Parent.prototype.sayName = function () {
    console.log('我是' + this.age)
}

function Child(friend, like) {
    Parent.call(this, like) 
    this.friend = friend
}
Child.prototype = new Parent()

21.4 原型式继承

原理:在已有一个实例对象的前提下,在函数中声明一个函数,使函数的原型指向被传入的参数。返回函数的的实例

缺点:子类会共用被传入对象中的引用类型。

代码:

function inhert(o) {
    function fun() {}
    fun.prototype = o
    return new fun()
}
let parent = {
    name: 'ty',
    age: 18,
    colors: ['red', 'black']
}
let c1 = inhert(parent) 
c1.age = 10 
c1.colors.pop() 
let c2 = inhert(parent) 
console.log(c1.age, c2.age) 
//10 18
console.log(c2.colors,c2.colors)
 //[ 'red' ] [ 'red' ] 共享一个引用类型

或者直接使用 Object.create() 来设置原型,可以看出跟上边的结果是一样的

let parent = {
    name: 'ty',
    age: 18,
    colors: ['red', 'black']
}
let c1 = Object.create(parent) 
c1.age = 10 
c1.colors.pop() 

let c2 = Object.create(parent) 
console.log(c1.age, c2.age) //10 18
console.log(c2.colors,c2.colors) //[ 'red' ] [ 'red' ] 共享一个引用类型

21.5 寄生式继承

原理:根据已有的对象实例,深拷贝一个新的对象,并且可以在这个对象上添加方法和属性

缺点:每次调用继承的方法都会在函数中重新添加属性

优点:不需要共用引用的属性

function createAnthor(obj) {
    let clone = JSON.parse(JSON.stringify(obj))
    clone.sayName = function () {
        console.log('我是' + this.name)
    }
    return clone
}
let parent = {
    name: 'ty',
    age: 18,
    colors: ['red', 'black']
}
let c1 = createAnthor(parent)
c1.colors.pop()
c1.age = 10
c1.name = 'k'
let c2 = createAnthor(parent)
c1.sayName() //我是kc2.sayName()  //我是ty
console.log(c1.colors, c2.colors) //[ 'red' ] [ 'red', 'black' ]
console.log(c1.age,c2.age) //[ 'red' ] [ 'red', 'black' ]

21.6 寄生组合式继承

原因:使用组合式继承的时候,父类函数会被调用两次,在Child.prototype中会有父类函数的属性,在子类实例中中也会有父类的属性。总共有两次,

原理:克隆父类构造函数的prototype,利用寄生的方式

function Parent(like) {
    this.like = like
    this.age = 19
    this.colors = ['red', 'blue', 'black']
}
Parent.prototype.sayName = function () {
    console.log('我是' + this.age)
}

function Child(friend, like) {
    Parent.call(this, like)
    this.friend = friend
}

function isHarertPrototype(child, parent) {
    let prototype = JSON.parse(JSON.stringify(parent.prototype)) //深拷贝一个付构造函数的原型  // 
    console.log(prototype.sayName)
    prototype.constructor = child
    child.prototype = prototype
}
isHarertPrototype(Child, Parent)
Child.prototype.sayName = function () {
    console.log('我是' + this.age)
}
let c1 = new Child('浏览器渲染进程', '学习')
c1.age = 10
c1.colors = ['aaa']
let c2 = new Child('js引擎线程 Gui线程 事件监听线程', 'sty')
c2.colors.push('blue')
c1.sayName() //我是10c2.sayName()  //我是19 
console.log(c1.like, c2.like) //学习 sty
console.log(c1.age, c2.age) //10 18
console.log(c1.colors, c2.colors) //[ 'aaa' ] [ 'red', 'blue', 'black', 'blue' ]

js

22. vue2 是如何检测数组的变化

使用了函数劫持的方法,重写了数组的方法,Vue将data中的数组进行了原型链重写,指向了自己定义的原型链方法。当调用数组api时,可以通知依赖更新。如果数组中包含引用类型,那么会对数组中的引用类型再次遍历进行监控。

23. nextTick

在下次dom更新循环之后,执行延迟回调。在修改完数据之后立即使用这个方法,获取更新后的DOM。

个人理解:就是你想在dom刷新之后立即获取数据,

顺序 Promise、MessageChannel、setTimeout

为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)

官网文档:

可能你还没有注意到,Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部对异步队列尝试使用原生的 Promise.thenMutationObserversetImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。

例如,当你设置 vm.someData = 'new value',该组件不会立即重新渲染。当刷新队列时,组件会在下一个事件循环“tick”中更新。多数情况我们不需要关心这个过程,但是如果你想基于更新后的 DOM 状态来做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员使用“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们必须要这么做。为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数将在 DOM 更新完成后被调用。例如:

<div id="example">{{message}}</div>var vm = new Vue({  el: '#example',  data: {    message: '123'  }})vm.message = 'new message' // 更改数据vm.$el.textContent === 'new message' // falseVue.nextTick(function () {  vm.$el.textContent === 'new message' // true})

23. DNS协议

ARP协议是将IP地址转化为MAC地址,那么DNS协议就是将域名转化为IP地址。(也可以将IP地址转化成域名)

即IP地址是面向主机的,而域名是面向用户的。

23.1 域名服务器:

根域名服务器:最高

顶级域名服务器:

权限域名服务器:负责一个区的域名解析工作

本地域名服务器:

23.2 查找的方法:分为递归查询和迭代查询

\1. 主机先向本地域名服务器进行递归查询
\2. 本地域名服务器采用迭代查询,向一个根域名服务器进行查询
\3. 根域名服务器告诉本地域名服务器,下一次应该查询的顶级域名服务器的IP地址
\4. 本地域名服务器向顶级域名服务器进行查询
\5. 顶级域名服务器告诉本地域名服务器,下一步查询权限服务器的IP地址
\6. 本地域名服务器向权限服务器进行查询
\7. 权限服务器告诉本地域名服务器所查询的主机的IP地址
\8. 本地域名服务器最后把查询结果告诉主机

注意: 本机向本地服务器进行递归查询,本地服务器采用迭代查询

24. ES6新特性

24.1 let & const
24.2 解构赋值
24.3 模板字符串 可以出现换行符
24.4 简化对象写法
24.5 箭头函数
24.5 rest参数
24.6 扩展运算符

(1)数组的合并

(2)数组的克隆

(3)将伪类数组转化为真正的数组

24.7 Symbol 表示对一无二的值
24.8 Set
24.9 Map
24.10 Promise

25. css新增特性

25.1 选择器:伪类选择器

Ele:nth-child()

Ele:first-child()

Ele:last-child()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BNYiNhoH-1639491671631)(https://user-gold-cdn.xitu.io/2017/11/15/15fbf40815f2e26b?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)]

25.2 盒模型:box-sizing

有三个:border-box||content-box||inherit

25.3 flex布局
25.4 背景和边框
背景:

背景绘制区域

边框:

边框背景:border-image

边框圆角:border-radius

25.5 文字效果

字体 font-face

@font-face{	font-family: myFirstFont;	src: url('Sansation_Light.ttf')		,url('Sansation_Light.eot'); /* IE9 */}font-family 与 src是必须的font-stretch  font-style font-weight是非必选的

js

text-overflow:ellipsis(设置省略号) 和 clip(直接裁剪)

设置透明:(两种方式)

  1. 文字透明可以用 color:rgba(R,G,B,A)

    R:红色值。正整数 (0~255)G:绿色值。正整数 (0~255)B:蓝色值。正整数(0~255)A:透明度。取值0~1之间
    

    js

  2. 对元素设置透明

opacity:value;  value:指定不透明度,从0.0(完全透明)到1.0(完全不透明)。opacity属性具有继承性,会使容器中的所有元素都具有透明度;
25.6 过渡
transition:为变化的元素设置透明transition-property: width;transition-duration: 1s;transition-timing-function: linear;transition-delay: 2s;
25.6 动画
animation:动画名称,一个周期花费时间,运动曲线(默认ease),动画延迟(默认0),播放次数(默认1),是否反向播放动画(默认normal),是否暂停动画(默认running)
25.7 2D/3D转换

transform: tranlate 移动||scale 缩放程度|| skew||rotate旋转度数

可以通过伪元素来设置0.5px的边框

25.8 阴影
box-shadow: 水平阴影的位置 垂直阴影的位置 模糊距离 阴影的大小 阴影的颜色 阴影开始方向(默认是从里往外,设置inset就是从外往里);
25.9 反射
box-reflect:方向【above | below | right | left] 偏移量,遮罩图片
25.10 文字

换行:

word-break:normal | break-all | keep-all

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N0FHl8vS-1639491671632)(https://user-gold-cdn.xitu.io/2017/11/15/15fbf40a3835c6dc?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)]

超出隐藏:text-overflow: clip| ellipsis | string

文字阴影:text-shadow

25.11 渐变
25.12 滤镜

26. HTML5新增特性

26.1 语义化标签
新增标签:<section><article><aside><header><footer><nav><details><figure><summery><mark><time><command><progress>进度条新增多媒体元素:<video><audio><embed><track><source>新表单元素:<output><keygen><datalist>画板元素:<canvas>input新增类型:color、date、datetime、datetime-local、email、month、number、range、search、tel、week

js

语义化是根据内容选择合适的标签。便于开发者阅读。

优点:有利于SEO优化,有利于爬虫抓取更多的信息。方便其它设备的解析。

28. 路由模式

分为两种:hash、history

29. 行内元素与块级元素

行内元素:span、i、b、strong、em、input

块级元素:div、ul、li、form、dd、dt、nav

行内块元素:img

30. 宽松相等(==)比较时的隐式转换规则

30.1 布尔类型和其他类型的相等比较

规则1:只要 布尔类型参与比较,该布尔类型会被首先转化为 数字类型

规则2:根据toNumber规则,true转为1,false转为0

例子:

  false == 0 // true  true == 1 // true  true == 2 // false
30.2 数字类型和字符串类型的相等比较

规则1:当**数字类型****和 字符串类型做相等比较时,字符串类型会被转化为 数字类型

  0 == '' // true  1 == '1' // true  1e21 == '1e21' // true  Infinity == 'Infinity' // true  true == '1' // true  false == '0' // true  false == '' // true
30.3 基本类型与引用类型做比较时 会进行隐式转换
规则如下:
对象的ToPrimitive操作会先调用valueOf方法,并且a的valueOf方法返回一个原始类型的值,所以ToPrimitive的操作结果就是valueOf方法的返回值10。

31. js中的函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HVFLg2cI-1639491671633)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210824225847935.png)]

数组的长度:len

普通函数:

3、shift() 和 unshift()

shift():删除原数组第一项,并返回删除元素的值;如果数组为空则返回undefined 。
unshift:将参数添加到原数组开头,并返回数组的长度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bimJpZDP-1639491671634)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210825224653028.png)]

pop:剪切掉数组的最后一项,并且返回剪切的那一项

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2PxyZsY0-1639491671634)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210824230031995.png)]

push:在数组最后添加一个元素,返回值为数组的长度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sOZmP1rj-1639491671635)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210824230648808.png)]

slice(不改变原数组): 可以传入一个或者两个参数

slice(m , n)

slice(m): 传入一个元素的时候

(1)当m>=len时,返回值为空数组

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uogHhCit-1639491671636)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210824232157042.png)]

(2)当0<=m<len,返回 从索引为m的元素到最后一个元素的数组

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EepUBBDU-1639491671637)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210824232434165.png)]

(3)当 -len<m<0时,先将m+len,变为了(2)的情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-puop6FWH-1639491671637)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210824233144196.png)]

**(4)当m<-len时,**返回整个数组

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DGBq40mp-1639491671638)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210824233331371.png)]

当为两个参数的时候,slice(m,n)

复制从索引为m的元素到索引为n的元素

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mW20q0oE-1639491671639)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210824233626204.png)]

split 可以实现删除、插入、替换的操作

**删除(两个参数):**arr.splice(1,3) 表示从索引为1的元素开始删除3个数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QWnSXv5o-1639491671640)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210825222813375.png)]

**插入(大于等于三个参数):**arr.splice(1, 1, 99, 99) 在索引为1的位置删除1一个元素,并插入两个99的元素。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3QL0Kjy1-1639491671640)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210825223211968.png)]

concat() 一般用来合并数组

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qIMPsK5c-1639491671641)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210825223823297.png)]

indexOf()和 lastIndexOf():

indexOf:数组中某个第一次出现的索引

lastIndexOf:数组中某个数最后一次出现的索引

如果数组中不存在就返回-1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-feigrVUJ-1639491671642)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210825225503447.png)]

索引:

some:只要一个元素符合条件就返回true

every:所有元素都符合条件才返回true

fliter:

map:

foreach:

reduce:

32.http与https

32.1 http与https的主要区别:

(1)https协议需要到CA申请证书

(2)http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl/tls加密传输协议。

(3)http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443

(4)http的连接很简单,是无状态的;HTTPS协议是由SSL/TLS+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

33.静态作用域与动态作用域

34.手写函数

34.1 手写new

new做了什么事情:

1.创建一个object对象

2.将传入的第一个参数从argument中取出来,并且使用

3.将新建对象的显示原型指向取出的对象的隐式原型

4.通过apply执行看看是否有返回值

35.判断类型

问题1:数据类型判断的方法?instanceof判断复杂数据类型的原理是什么?toString()和constructor有了解过吗?

答:①简单类型用typeof,复杂类型用instanceof。用typeof判断简单类型,除了null会返回object,其他都会返回正确的结果,如果判断复杂类型通通返回object。用instanceof可以检测某个实例是否是某个对象类型,如果用它来检测简单数据类型则始终返回false,因为基本类型不是对象。②MDN:instanceof 运算符 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。③查了MDN:默认情况下,toString() 方法被每个 Object 对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 “[object type]”,其中 type 是对象的类型

36.react与vue的异同

相同点:

1.都使用了虚拟dom

2.单一数据源

3.都支持ssr

4.组件化开发

不同点:

1.react单项绑定,vue双向绑定

2.react使用all in js,使用jsx将html、css、js融合;vue还是按照传统的将他们分开来写。

3.状态管理,react使用Redux,vue使用vuex

37.react的生命周期

38.react的类组件与函数组件的区别

类组件:

state储存数据,setState修改数据,其中数据会与state合并。

会使用生命周期函数,帮助完成一些功能

调用函数时,需要绑定this

函数组件:

使用useState来设置变量,返回一个对象,其中有两个参数,一个是设置的数据,一个是修改数据的函数

是否有this

是否有生命周期

是否有状态state

39.Redux

ReactComponent

reducers(可以有多个)

ActionCreator(可以有多个)

Store(只能有一个)

40.useState useMemo

41.purComponent

42.vue2与vue3

43.vue的双向绑定原理

44.vuex

state

getter

mutations

actions

modules

45.跨域

46.箭头函数与普通函数的区别

1.箭头函数都是匿名函数

2.箭头函数this的指向为定义是外层非箭头函数的this指向

3.箭头函数不能使用arguments,取而代之使用rest参数解决

4.call、apply、bind不能修改箭头函数的this指向。

47.观察者模式与发布订阅者模式的区别

**观察者模式:**观察者(Observer)直接订阅(Subscribe)主题(Subject),而当主题被激活的时候,会触发(Fire Event)观察者里的事件。

**发布订阅模式:**订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Topic),当发布者(Publisher)发布该事件(Publish topic)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。

观察者更像是:观察者 依赖某个 被观察者 ,当被观察者 发生改变的时候,通知观察者改变

发布订阅者:其中包括 发布者,订阅者 和 调度中心,理解 微博博主就像是一个发布者,关注的人像是订阅者,而微博本身是 调度中心,博主发送新内容的时候,调度中心 将 新内容推送给 订阅的人

48.webScoket

与http的区别:

http只能由客户端发送,服务端响应返回数据,而webScoket建立连接之后,客户端和服务端都可以主动发送消息。

建立连接:

//创建实例
const ws = new webScoket(url[, protocols])

url
建立连接的url
protocols 

49. CSS 样式隔离

1.all in js 像React一样将html、css、js融合起来
2.css module vue中的scoped 给每一个dom都设置了一个独立的Hash,对于他们上边所挂载的css样式通过hash绑定即可。

50. 深拷贝

1.JSON.parse(JSON.stringify({obj}))

2.回调函数

function deepColen(obj,set=new Set()){
    if(typeof obj==='object'){
        let res=obj intanceof Array ? []:{}
        // 解决循环引用的问题
        set.setItem(res,obj)
        for(let key in obj){
            res[key]=deepColen(obj[key],set)
        }
        return res
    }else{
        return obj
    }
}

3.使用Object.assign(),如果为一层则可以进行深拷贝,如果是多层则不能进行深拷贝

var sourceObj1 = { b: {name:'fxk',obj:{name:1}} };
var sourceObj11 = 5;
var obj=Object.assign({}, sourceObj1);

sourceObj1.name=11
sourceObj1.b.obj.name=1111111

console.log(obj); //{ b: { name: 'fxk', obj: { name: 1111111 } } }

51. es6有哪些新特性

symbol,Set、Map、Promise、let与const块级作用域、filter、reduce、箭头函数、解构赋值、扩展运算符、模板字符串

52.hash与history两种模式的区别

hash模式的特点

hash表示的是地址栏URL中#符号(也称作为锚点), hash虽然会出现在URL中, 但是不会被包含在Http请求中, 因此hash值改变不会重新加载页面.

由于hash值变化不会引起浏览器向服务器发出请求, 而且hash改变会触发hashchange事件, 浏览器的进后退也能对其进行控制, 所以在HTML5之前, 基本都是使用hash来实现前端路由.

不利于seo的优化, hash 只能修改 # 后面的部分,所以只能跳转到与当前 url 同文档的 url

history模式的特点

利用了HTML5新增的pushState()replaceState()两个api, 通过这两个api完成URL跳转不会重新加载页面

同时history模式解决了hash模式存在的问题. hash的传参是基于URL的, 如果要传递复杂的数据, 会有体积限制, 而history模式不仅可以在URL里传参, 也可以将数据存放到一个特定的对象中

vue-router实现

hash模式和history模式实现vue-router跳转api的区别

apihashhistory
pushwindow.location.assignwindow.history.pushState
replacewindow.location.replacewindow.history.replaceState
gowindow.history.gowindow.history.go
backwindow.history.go(-1)window.history.go(-1)
forwardwindow.history.go(1)window.history.go(1)

54. 如何加快页面渲染速度(懒加载、CDN、压缩、雪碧图,treeshrinking)

55. 前端性能优化

56.src与href的区别

1.href标识超文本引用,用在link和a等元素上,href是引用和页面关联,是当前元素和引用资源之间建立联系

2.src表示引用资源,表示替换当前元素,用在img,script,iframe身上,src是页面内容不可缺少的一部分

src是source的缩写,是指向外部资源位置,指向的内部会迁入到当前标签所在的位置;请求src资源时会将其指向的资源下载并应用到文档中。

当浏览器解析到这一句的时候会识别该文档为css文件,会**下载**并且**不会停止对当前文档的处理**

57.link与@import的区别

区别1(功能):link是XHTML标签,除了加载Css,还可以定义RSS等其他事务;@import属于CSS范畴,只能加载CSS

区别2(加载):link引用CSS时,在页面载入时同时加载;@import需要页面网页完全载入以后加载

区别3(兼容问题):link是XHTML标签,无兼容问题;@import是在CSS2.1提出的,低版本浏览器不支持。

区别4(是否支持js控制DOM改变样式):link支持,import不支持

58.flex 属性 flex-grow、flex-shrink、flex-basis

flex-grow:定义项目的放大比例:

  1. 默认为0,即使 存在剩余空间,也不会放大
  2. 所有项目的flex-grow为1,等分剩余空间(自动放大占位)
  3. flex-grow为n的项目,占据的空间(放大的比例)是flex为1的n倍

flex-shrink:定义项目缩小比例:

1.默认为1

2.如果所有的项目的flex-shrink为1:当空间不足时,缩小的比例相同

3.flex-shrink为n的项目,空间不足时缩小的比例时flex-shrink为1的n倍

flex-basis:在主轴上占据的宽度或高度,优先级比width和height高

默认值auto,即项目原本大小

设置后项目占据对固定空间

flex的默认值 0 1 auto (不放大会缩小)

flex为none : 0 0 auto (不放大也不缩小)

flex为auto : 1 1 auto 放大且缩小

flex为一个非负数字n:

flex:n 即 n 1 0%

flex:n1 n2分别为flex-grow和flex-shrink的值

flex为一个非负数字n和一个长度或百分比L:分别为flex-grow和flex-basis的值,

flex:n L;= flex-grow:n;

​ flex-shrink:1;

​ flex-basis:L;

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值