面试题收集

245 阅读24分钟

一,【css部分面试题】

01,选择器权重

!important>行内样式>id选择器>类选择器>标签选择器>通配符>继承

选择器平级的情况下,下面的css权重,会高于上面的css权重

02,继承

css中,继承是指的是给父元素设置一些属性,后代元素会自动拥有这些属性

无继承的属性

  • 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

03,css动画

04,常见布局

05,flex

06,px,em,rem,vw

px:绝对单位,页面按精确像素展示

em:相对单位,基准点为父节点字体的大小,如果自身定义了font-size按自身来计算,整个页面内1em不是一个固定的值

rem:相对单位,可理解为root em, 相对根节点html的字体大小来计算

vh、vw:主要用于页面视口大小布局,在页面布局上更加方便简单

EM是相对于元素自身字体大小的一个单位,而REM是相对于根元素(html)字体大小的一个单位

07,适配

rem是相对于根元素字体大小的一个单位,他可以用来解决移动端适配的问题,适配的原理就是利用媒体查询或js动态检测适配的宽度,不同宽度下设置对应的字体大小,根元素的字体大小发生了变化,所以用rem做单位的元素也就跟着一起变了,不过在日常开发中,也可用vw做适配,在一定程度上来说,rem在做适配这方面,就是对vw的一种模拟

08,css预处理

扩充了 Css 语言,增加了诸如变量、混合(mixin)、函数等功能,让 Css 更易维护、方便

本质上,预处理是Css的超集

包含一套自定义的语法及一个解析器,根据这些语法定义自己的样式规则,这些规则最终会通过解析器,编译生成对应的 Css 文件

09,常见css代码片段

清除浮动

溢出文本隐藏

居中

二,【js部分面试题】

01,闭包

  • 1.闭包是什么 :

    • 闭包 是 使用其他函数内部变量的 函数 (闭包 = 函数 + 其他函数内部变量)
    • 闭包 = 函数 + 上下文引用
  • 2.闭包作用 : 创建私有变量,解决全局变量污染

  • 缺点: 容易造成内层泄露,因为闭包中 的局部变量永远不会被回收

  • 应用场景:闭包随处可见。

  • 手写闭包

function fn() {

        var num = 10;

        function fun() {

        console.log(num);

        }

        return fun;

        }

var f = fn();

f();

02,原型链

每一个实例对象都有一个原型,而原型也是对象,也有自己的原型,以此类推形成的链式结构被称为原型链
对象访问原型链的规则是就近原则
主要作用是实现继承

实际应用:vue注册全局组件,之所以能够全局使用,就用到了原型链的继承,instanceof运算符也是通过原型链的作用实现判断数据

03,箭头函数和普通函数的区别

箭头函数是ES6新增的特性,用来简化普通函数的写法,规避普通函数this指向的痛点

  1. 箭头函数没有原型对象prototype,不能作为构造函数使用(不能被new)
  2. 箭头函数没有arguments,可以使用...拿到所有实参的集合数组
  3. 箭头函数中的this在定义时就已经确定,取决于父级的环境
  4. 箭头函数不能通过call,bind,apply方法修改this指向(会忽略第一个参数,其他功能正常使用)
  5. 箭头函数不能用作Generator函数,不能使用yeild关键字(function*)

自己的话:

箭头函数是ES6新增的特性,用来简化普通函数的写法,他与普通函数的区别主要有三点 1,没有this 2,可以简写 3,内部没有arguments,只能使用扩展符

04,this指向

05,防抖和节流

防抖定义:单位时间内,频繁触发事件,只会触发最后一次
使用场景:窗口大小改变、输入框内容校验等
防抖流程

  • 声明一个全局timeID储存定时器id
  • 每一次触发点击事件,先清除上一个定时器,以本次触发为准
  • 开启本次定时器
    代码:以输入框为例
//(1)声明一个全局timeID存储定时器id
        let timeID = null
        document.querySelector('input').addEventListener('input',function(){
            
            //(2)每一次触发事件,先清除上一次定时器,以本次触发为准
            clearTimeout(timeID)
            //(3)开启本次定时器
            /* 
            function函数: 定时器this指向window
            箭头函数 : 访问上级作用域this-> input
            */
            timeID = setTimeout( ()=>{
                console.log(`您的搜索内容为${this.value}`) 
            },500)
            
        })

节流定义:单位时间内,频繁触发事件,只会触发一次
使用场景:鼠标不断点击触发、监听滚动事件、下拉加载等
防抖流程

  • 声明一个全局变量记录当前触发时间
  • 每一次触发事件的时候,获取当前时间
  • 判断:当前时间 - 上一次时间 >= 节流间隔
  • 储存本次触发事件 代码:
//(1)声明一个全局变量记录 当前触发时间
        let lastTime = null

        //页面滚动条事件
        let i = 1
        window.onscroll = function(){
            //(2)获取现在时间
            let currentTime = Date.now()
            //(3)判断 当前时间 - 上一次触发事件 >= 节流间隔
            if( currentTime - lastTime >= 500 ){
                console.log(`第${i++}次触发`)
                lastTime = currentTime
            }
            
        }

相同点:降低高频事件

06, 递归及应用场景

  • 1.递归 : 在函数内部调用自己 (问题相同, 规模不同) 尾递归

  • 优点:结构清晰、可读性强

    缺点:效率低、调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。->性能

  • 2.递归应用 :

    • 浅拷贝与深拷贝
    • 遍历dom树
    • 数据扁平化处理

07,new的背后做哪些事情

  1. 创建一个新的对象
  2. 让构造函数的this指向这个新对象
  3. 执行构造函数(给对象赋值)
  4. 返回实例(返回这个对象)

08,bind、call、apply 区别

作用:修改this指向
相同点: 第一个参数都是用来修改this指向
都可以利用后续参数传参
不同点:
传参方式不同,call和bind单个传参,可以传任意多个参数,bind可以多次传参,apply通过数组或伪数组传参
执行机制不同,call和apply直接调用函数,bind不会立即执行,而是得到一个新函数

在开发中运用比较少,但是见过类似代码: 实际应用:
call可以用于万能数据检测
objject.prototype.tostring.call() 利用apply传参的特点将伪数组转换为真数组,求最大值
math.max.apply(null,arr) bind修改定时器this指向

09,继承

  • 原型链继承(适合继承方法)
    通过构造函数,实例对象,原型对象三者之间的关系
  • 构造函数继承(借助 call,继承属性)
    通过call的两个做用,调用函数,修改this指向完成
    调用构造函数,修改它的this指向,传入参数
  • 组合继承(原型继承+借用构造函数继承)
    原型继承解决了方法的继承问题,借用构造函数继承解决了属性继承问题
  • 原型式继承
  • 寄生式继承
  • 寄生组合式继承

继承属性和方法

10,promise的使用

promise三个状态: pending(默认) fulfilled(成功) rejected(失败)

  1. Promise的构造函数接收一个参数,是函数,并且传入两个参数:resolve,reject
  2. resolve 函数被执行时, 会将 promise 的状态从 pending 改成 fulfilled 成功
  3. reject 函数被执行时, 会将 promise 的状态从 pending 改成 rejected 失败
new Promise((resolve, reject) => { 
    resolve() 
    })
new Promise((resolve, reject) => { 
    reject() 
    })

作用:解决回调地狱的问题
原理:控制异步代码结果处理的顺序
可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作

自己的话:

promise它是es6新增加地特性,是一个函数,一般都当做构造函数来使用,用的时候要new一下来得到一个promise实例,它内部有三种状态,分别是pengding,成功和失败,成功会触发then,失败会触发catch

promise它主要是解决回调地狱的问题,不过我通常会用async和await,因为他们不仅可以解决回调地狱的问题还可以简化代码

promise.race() 接收多个promise实例,可以得到最先处理完毕的结果(可能是成功,也可能是失败)

promise.all([]) 两个请求之间没有先后逻辑顺序就用并发
它返回结果的顺序和书写的顺序是一一对应的
全部正确.then捕获结果
有一个错误.catch捕获结果

11,数组常用API

数组添加:
push()
push()方法接收任意数量的参数,并将它们添加到数组末尾,返回数组的最新长度
unshift()
unshift()在数组开头添加任意多个值,然后返回新的数组长度
splist()
splist()传入三个参数,分别是开始位置、0(要删除的元素数量)、插入的元素,返回空数组 concat()
concat()首先会创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回这个新构建的数组,不会影响原始数组
数组删除:
pop()
pop() 方法用于删除数组的最后一项,同时减少数组的length 值,返回被删除的项
shift()
shift()方法用于删除数组的第一项,同时减少数组的length 值,返回被删除的项
splist()
splist()传入两个参数,分别是开始位置,删除元素的数量,返回包含删除元素的数组
slice() slice() 用于创建一个包含原有数组中一个或多个元素的新数组,不会影响原始数组
更改数组:
splist()
splist()传入三个参数,分别是开始位置,要删除元素的数量,要插入的任意多个元素,返回删除元素的数组,对原数组产生影响
查找数组:
indexOf()
indexOf()返回要查找的元素在数组中的位置,如果没找到则返回 -1
includes()
返回要查找的元素在数组中的位置,找到返回true,否则false
find()
返回第一个匹配的元素
遍历数组:
map()
map()返回删除的元素,映射数组(将数组每一个元素处理之后,得到一个新数组)
filter()
filter()筛选,根据条件筛选数组,将符合条件的元素放入新数组中
forEach()
forEach()类似于for循环遍历数组
findIndex()
findIndex()找元素下标

详情:juejin.cn/post/710641…

12,高阶函数

13,函数柯里化

14,如何判断数据类型

typeof
typeof 操作符返回一个字符串,表示未经计算的操作数的类型
对于引用类型数据,除了function会被识别出来,其他都输出object类型
null也输出object类型
instanceof
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型
Object.prototype.toString.call()
万能数据检测类型 实际应用:在vue源码中大量使用

15,for...in,for...of的区别

  1. 功能不同:for...in可以遍历数组和对象,返回下标和元素,for...of通常用来遍历数组,返回元素
  2. for...in可以把原型遍历出来
  3. for...of想要遍历对象,必须搭配Object.keys()使用

16,深拷贝与浅拷贝

  • 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址
  • 深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址 在JavaScript中,实现浅拷贝的方法有:
  1. Object.assign
  2. Array.prototype.slice(), Array.prototype.concat()
  3. 使用拓展运算符实现的复制

实现深拷贝的方法:

  1. 推荐 json : let obj = JSON.parse( JSON.stringify( 对象 ) )
  2. 递归

17,事件委托

所谓的事件委托,就是把子元素绑定的事件,统一委托(绑定)到祖先身上
原理是事件冒泡
性能高
后续新增的元素同样具有事件绑定的效果
自定义属性解决按需事件

自己的话

事件委托,就是通过事件冒泡的原理,把子元素绑定的事件,统一委托(绑定)到祖先元素身上,这样做的好处有两点,1是性能高,2是对后续新增的元素同样具有事件绑定的效果.

事件委托在jquery的时代还是有应用场景的,但现在流行前端框架,我们比较少的使用

18,函数传参,传递简单数据类型和复杂数据类型有怎样的差异

简单数据类型传递的是值的拷贝,函数内部对参数的修改不会影响外部
复杂数据类型传递是引用地址,函数内部对参数内容的修改会影响外部,对参数引用地址的修改不会影响外部

三,【浏览器部分面试题】

01,eventLoop

  • eventloop又叫事件循环,是单线程语言js在运行胆码时不被阻塞的一种机制
  • js代码的执行分为同步代码和异步代码,当碰到同步代码时直接在执行栈中执行
  • 当碰到异步代码并且时机符合时(例如定时器事件到了),就会把异步代码添加到任务队列中
  • 当执行栈中的同步代码执行完毕后,就去任务队列中把异步代码拿到执行栈中执行
  • 这种反复轮训任务队列并把异步代码拿到执行栈中执行的操作,就是eventloop

02,url回车

03,重绘和回流

04,http和https的区别

05,三次握手4次挥手

06,跨域

07,hash模式和history模式

总:hash 和 historyvue-router 的两种模式
通过触发代码内的监听事件从而改变页面内容,不会刷新页面,不会往服务端发请求 Hash模式:
1.url路劲会出现#字符
2.hash路由跳转只有#后的路径发生改变,不会刷新页面

3.Hash值的改变会触发hashchange事件
History模式:
1.整个地址重新加载,可以保存历史记录,方便前进后退
2.使用HTML5(旧浏览器不支持)和HTTP服务端配置,没有后台配置的话,页面刷新会出现404
3.如果是用户在当前用histroy模式操作切换页面的话,URL会被改变、浏览器不会刷新页面也不会往服务端发请求,但会触发代码内的监听事件从而改变页面内容,所以无需用到服务器也可以自由切换页面了。但是这里有个很核心的点就是URL会改变,即有新的URL诞生,所以如果这时用户主动刷新页面(F5),浏览器发送给服务端的是新的URL,所以服务端要做适配,配置一个合理的规则让这些URL返回的都是同一个index.html

自己的话:

两者都是用来实现前端路由跳转的,他们之前的区别有三点,1:兼容性,hash兼容性较好,能够兼容到ie8,history最低能兼容到ie10,2:实现原理不同,hash模式是通过监听onhashchange事件做的处理,在事件的回调里面做一些路由匹配的操作,history模式是利用h5新增的API实现的。3:刷新页面时,对于后端的表现不一样,hash路由是有一个#号的,页面刷新时,#号后面的内容不会发送到服务端,而history没有#号的,页面刷新时,对于后端来说每次都会拿到一个不同的请求地址,所以需要后端进行配置,否则页面就会报404

后端配置方法:匹配到相关请求,同一返回index.html,index.html加载的有路由相关的代码,所以也就转换为由前端路由来处理

08,本地储存

  • 1.相同点

    • 作用一致 : 用于存储数据
    • localStorage和sessionStorage存储大小在5M左右
    • cookie 数据根据不同浏览器限制,大小一般不能超过 4k
  • 2.不同点: 存储方式不同

    • localStorage : 硬盘存储 (永久存储,页面关闭还在,存在硬盘)
    • sessionStorage :内存存储 (临时存储,页面关闭了就消失)
    • cookie 设置过期时间,与浏览器是否关闭无关 缺点:太小,操作不便,配合插件js-cookie使用
    • indexedDB 异步,储存数量大,>250m
  • 3.localStorage与sessionStorage如何存储引用类型数据(数组和对象)

    • 转json存储

    4 实际应用场景:登录时存储token

本地储存的方式有四种,分别是localStorage,sessionStorage,cookie,indexedDB

09,缓存

10,常见web攻击

四,【VUE部分面试题】

01,响应式原理

1.vue主要是通过:数据劫持 + 依赖收集(这里用到了发布者-订阅者模式)的方式来实现的。

2.Vue2.x是借助Object.defineProperty()实现的,而Vue3.x是借助Proxy`实现的,

3 vue2. 原理:3.1通过Object.defineProperty为对象添加属性,可以设置对象属性的gettersetter函 数。(遍历生成对应的setter,getter函数)

3.2 通过点语法获取属性都会执行getter函数,在这个函数中我们会把调用此属性的依赖通过watcher收集到一个集合中,也就是订阅者 ;

3.3 修改属性时,会触发这里定义的setter函数,在次函数中会去通知集合中的依赖更新,做到数据变更驱动视图变更,也叫做发布者。

vue2缺点:

  1. 深度监听需要一次性递归 ---> 模板不需要的数据也会进行递归,浪费性能
  2. 无法监听对象新增与删除的属性 ---> 因为递归已经完成,无法重新获取
  3. vue2中对数组有特殊处理:直接通过下标修改数组元素的值,视图是不更新的,不是object.defineProperty做不到,而是vue2没有这样做,因为数组下标是会变化的,这样做没有意义

4 vue3.中的响应式与2.x的核心思想一致,只不过数据的劫持使用Proxy(一次性的添加getter和setter函数)而不是Object.defineProperty,Proxy相比Object.defineProperty在处理数组和新增属性的响应式处理上更加方便。

02,diff算法

比较新旧虚拟dom,给旧的真实dom打补丁,得到新的真实dom
同级比较(4个指针),深度优先(递归)
比较key 或 tag节点,如果相同,比较子节点,直到没有子节点为止
4次比对,当前的结点去旧的列表中找,找到,就复用,找不到,就新建

它的作用是比较不同,比较新旧虚拟dom
特点是同级比较(四个指针),深度优先(递归)
先说深度优先,在有key的情况下先比较key,没有key就比较tag结点,如果相同,就利用递归比较他们的子节点,直到没有子节点为止.
再说同级比较,他有四个指针,分别指向新旧dom的头和尾,会进行四次比对,用当前结点去旧的列表中找相同,找到了,就复用,找不到,就新建
最后得到一个新的dom

03,render函数

04,key

先说一下vue的vue实现流程:

vue将用户编写的模板编译成渲染函数render,渲染函数根据当前data里的数据生成虚拟dom,虚拟dom通过diff算法将新的虚拟dom与旧的虚拟dom进行比对,实现最小化的局部更新真实dom

在这个过程中,key是每个虚拟dom唯一id,key属于diff算法的优化策略,比较新旧dom时,会优先比较key,从而更快更准确的找到虚拟dom的节点

  • 高效更新虚拟dom
  • diff算法的优化策略
  • 比较新旧dom时,优先比较key

05,vnode

06,单个组件生命周期&父子组件

  • 创建前:beforeCreate,创建之后created。
  • 挂载前:beforeMount,挂载之后mounted。
  • 更新前:beforeUpdate,  更新后:updated。
  • 销毁前:beforeDestroy, 销毁后:destroyed。
    详解:juejin.cn/post/702853…

07,keep-alive

08,nextTick

立即能够获取更新后的dom元素,本质是一种优化策略。

vue在更新Dom时是异步执行的,当数据发生变化时,vue会开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新,而$nextTick就应运而生了,我们可以使用它,来立即获取更新后的DOM元素。

原理:

  1. 把回调函数放入callbacks(异步操作队列)等待执行
  2. 将执行函数放到微任务或者宏任务中
  3. 事件循环到了微任务或者宏任务,执行函数依次执行callbacks中的回调

应用:

  1. created中获取DOM的操作需要使用它

09,$set

10,插槽

11,组件通讯

1,常用父子传值
props
$emit / v-on
slot插槽
2,不常用父子传值
$parent / $children
ref / $refs
3,语法糖传值
v-model
.sync
4,跨级通信
eventBus
Vuex
provide / inject
$attrs / $listeners
5,访问根组件
$root

详情:juejin.cn/post/712078…

12,v-if和v-for的区别

这个问题只存在vue2中,在vue3已经被解决了:

  • v-if不能和v-for一起使用的原因是v-for的优先级比v-if高,一起使用会造成性能浪费

  • 解决方案有两种,把v-if放在v-for的外层或者把需要v-for的属性先从计算属性中过滤一次

  • v-if和v-for的优先级问题在vue3中不需要考虑,vue3更新了v-if和v-for的优先级,使v-if的优先级高于v-for

13,v-if和v-show的区别

首先,他们的作用都是控制元素是否可见,使用方法也是一样
但是他们的控制手段不同,v-show是通过添加样式display-none实现隐藏,而v-if是直接通过控制元素的创建和销毁实现隐藏显示

实际应用:v-if 登录模式的切换 v-show tab栏切换

14,computed和watch的区别

计算属性computed

  1. 计算属性是一个函数,返回值(return)就是计算属性得到的结果;
  2. 一个计算属性对应一个或多个data中的属性;
  3. 第一次使用计算属性的时候,会把计算结果缓存
  4. 后续再次使用这个计算属性的时候,如果该计算属性用到的数据没有变化,就直接读取缓存中的结果,不会重新计算;如果该计算属性用到的数据发生了变化,重新计算结果,存到缓存中; 实际应用:
  5. 不支持异步,当computed内有异步操作时无效,无法监听数据的变化。

监听属性watch

  1. 监听器与data中的属性同名,当属性的值发生改变的时候,监听器被触发执行;
  2. 一个监听器只对应一个data中的属性;
  3. 不支持缓存,数据发生改变,直接触发响应的操作,不会创建变量保存结果;
  4. watch支持异步
  5. watch中有两个参数deep和immediate:deep深度监听,用来发现对象内部值的变化,因为watch只会监听数据的值是否改变,不会监听地址的变化,如果需要监听引用类型的数据变化,需要深度监听;immediate组件加载立即触发回调函数执行。

15,自定义指令

16,组件封装

组件封装是前端工作人员的日常工作,组件的类型有以下三种:
(1)工具类型的,与具体的项目无关,例如轮播图组件、element-ui......
(2)项目业务相关的组件,与具体项目紧密相关,可能会在项目中使用多次。或者时公司同一系列的项目中使用,比如图片上传组件,凡是涉及到上传或者修改图片的都可以使用
(3)项目复杂页面的子组件,不会使用多次,比如一些需要弹框表单的组件,因为页面信息比较多,通过组件拆分,比较好管理和维护
在实际开发中
之前做过后管系统,使用了element-ui,所以没有机会封装工具类型的组件,但是因为很多地方都需要上传或修改头像,于是封装了一个图片上传的组件。
在有些员工信息的地方,需要弹框加表格来收集用户信息,于是封装了许多子组件与父组件搭配使用

17,vue.use

18,vuex

vuex是一个全局状态管理库,它解决了非关系组件之间数据传递和共享的问题,一般在使用的时候它里面有常见的六个配置项,分别是state,mutations,actions,getters,modules

它的触发流程是这样的:例如说点击按钮发请求,希望把请求的数据储存到state中,我们可以在actions中发请求,把请求到的数据通过mutations储存到state中,因为state中的数据是响应式的,所以一旦state中的数据发生变化,页面当中,用到state中数据的也会发生变化

如果组件之间关系清晰,数据不需要重复使用,我们可以直接通过父传子,祖辈传后辈等方式进行数据传递

19,pinia

20,v-model和.sync

在vue2中

1其本质是语法糖。v-model即可以作用于表单元素,又可作用于自定义组件,无论是哪一种情况(vue2, vue3),它都是一个语法糖,最终会生成一个属性和一个事件

原理: 在input框里面通过v-bind动态绑定一个value,然后在input框里面通过@input方法去动态获取input输入的值,然后重新给变量赋值就可以了。

在vue3中:

modelValue属性和onUpdate:modelValue事件。

  1. 给子组件传递一个属性:modelValue,
  2. 监听子组件上的事件update:modelValue,在回调函数中,将从子组件回传的值保存

实际应用场景:收集用户输入的数据。以及父子组件传值中都有用到。

21,常见修饰符

22,路由守卫

23,组件中的data为什么要定义成一个函数而不是一个对象

每个组件都是 Vue 的实例。组件共享 data 属性,当 data 的值是同一个引用类型的值时,改变其中一 个会影响其他 ,而如果data是一个函数是,他得到的是一个返回后的新函数,改变其中一个时他不会影响到其他。

24,vue3新特性

性能更高了
体积更小了
对ts的支持更好了
组合式API

四,【TypeScript部分面试题】

五,【业务部分面试题】

01,图片懒加载

是一种常见的性能优化的手段 核心思路是:
图片不在可视区的时候不设置src属性,只有当它在可视区内的时候才设置
如何判断:
1.经过窗口位置的计算
2.用浏览器api:intersectionObserver 判断是否在可视区中

在实际开发中,我们可以通过intersection 实现,或者用vueuse中的api

02,数据懒加载

03,功能组件

04,路由权限

05,按钮权限

2,第一次加载页面会触发哪几个钩子函数

:当页面第一次加载时会触发beforeCreate,created,beforeMount,mounted这四个钩子函数
:created可以获取到“data”里面的变量,mounted可以获取到DOM
在实际开发中
在项目中,当用户登录时都会看到一个基础信息页面,所以这种情况都是在created中调用,之前做过一个图表项目,它必须要获取DOM后才会执行,这时就需要在mounted中使用了

3,js的数据类型

值类型(基本类型) :字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)
引用数据类型:对象(Object)、数组(Array)、函数(Function)