一些重要基础的前端知识点
1.介绍一下标准的 CSS 的盒子模型?低版本 IE 的盒子模型有什么不同的?
答:
盒模型都是由四个部分组成的,分别是margin、border、padding和content。
标准盒模型和IE盒模型的区别在于设置width和height时,所对应的范围不同。标准盒模型的width和height属性的,范围只包含了content,而IE盒模型的width和height属性的范围包含了border、padding和content。一般来说,我们可以通过修改元素的box-sizing属性来改变元素的盒模型,IE盒模型(border-box)、W3C标准盒模型(content-box)
2.CSS 选择符有哪些?优先级排序怎样?
答:
(1)id选择器(#myid)
(2)类选择器(.myclassname)
(3)标签选择器(div,h1,p)
(4)后代选择器(h1 p)
(5)相邻后代选择器(子)选择器(ul>li)
(6)兄弟选择器(li~a)
(7)相邻兄弟选择器(li+a)
(8)属性选择器(a[rel="external"])
(9)伪类选择器(a:hover,li:nth-child)
(10)伪元素选择器(::before、::after)
(11)通配符选择器()
!important>内联>ID选择器>伪类=属性选择器=类选择器>元素(标签)选择器【p】>通用选择器()>继承的样式。
优先选择算法:
3.如何水平垂直居中
一般常见的几种居中的方法有:
对于宽高固定的元素
(1)我们可以利用margin:0auto来实现元素的水平居中。
(2)利用绝对定位,设置四个方向的值都为0,并将margin设置为auto,由于宽高固定,因此对应方向实现平分,可以实现水
平和垂直方向上的居中。
(3)利用绝对定位,先将元素的左上角通过top:50%和left:50%定位到页面的中心,然后再通过margin负值来调整元素
的中心点到页面的中心。
(4)利用绝对定位,先将元素的左上角通过top:50%和left:50%定位到页面的中心,然后再通过translate来调整元素
的中心点到页面的中心。
(5)使用flex布局,通过align-items:center和justify-content:center设置容器的垂直和水平方向上为居中对
齐,然后它的子元素也可以实现垂直和水平的居中。
对于宽高不定的元素,上面的后面两种方法,可以实现元素的垂直和水平的居中。
4.position 的值?
absolute
生成绝对定位的元素,相对于值不为static的第一个父元素的paddingbox进行定位,也可以理解为离自己这一级元素最近的
一级position设置为absolute或者relative的父元素的paddingbox的左上角为原点的。
fixed(老IE不支持)
生成绝对定位的元素,相对于浏览器窗口进行定位。
relative
生成相对定位的元素,相对于其元素本身所在正常位置进行定位。
static
默认值。没有定位,元素出现在正常的流中(忽略top,bottom,left,right,z-index声明)。
inherit
规定从父元素继承position属性的值。
5.清除浮动?
浮动元素可以左右移动,直到遇到另一个浮动元素或者遇到它外边缘的包含框。浮动框不属于文档流中的普通流,当元素浮动之后,
不会影响块级元素的布局,只会影响内联元素布局。此时文档流中的普通流就会表现得该浮动框不存在一样的布局模式。当包含框
的高度小于浮动框的时候,此时就会出现“高度塌陷”。
清除浮动是为了清除使用浮动元素产生的影响。浮动的元素,高度会塌陷,而高度的塌陷使我们页面后面的布局不能正常显示。
清除浮动的方式
(1)使用clear属性清除浮动。参考28。
(2)使用BFC块级格式化上下文来清除浮动。参考26。
因为BFC元素不会影响外部元素的特点,所以BFC元素也可以用来清除浮动的影响,因为如果不清除,子元素浮动则父元
素高度塌陷,必然会影响后面元素布局和定位,这显然有违BFC元素的子元素不会影响外部元素的设定。
6:BFC?如何创建BFC?
BFC指的是块级格式化上下文,一个元素形成了BFC之后,那么它内部元素产生的布局不会影响到外部元素,外部元素的布局也
不会影响到BFC中的内部元素。一个BFC就像是一个隔离区域,和其他区域互不影响。
一般来说根元素是一个BFC区域,浮动和绝对定位的元素也会形成BFC,display属性的值为inline-block、flex这些
属性时也会创建BFC。还有就是元素的overflow的值不为visible时都会创建BFC。
创建BFC
(1)根元素或包含根元素的元素
(2)浮动元素float=left|right或inherit(≠none)
(3)绝对定位元素position=absolute或fixed
(4)display=inline-block|flex|inline-flex|table-cell或table-caption
(5)overflow=hidden|auto或scroll(≠visible)
7.JavaScript 原型,原型链? 有什么特点?
在 js 中我们是使用构造函数来新建一个对象的,每一个构造函数的内部都有一个 prototype 属性值,这个属性值是一个对象,这个对象包含了可以由该构造函数的所有实例共享的属性和方法。当我们使用构造函数新建一个对象后,在这个对象的内部将包含一个指针,这个指针指向构造函数的 prototype 属性对应的值,在 ES5 中这个指针被称为对象的原型。一般来说我们是不应该能够获取到这个值的,但是现在浏览器中都实现了 proto 属性来让我们访问这个属性,但是我们最好不要使用这个属性,因为它不是规范中规定的。ES5 中新增了一个 Object.getPrototypeOf() 方法,我们可以通过这个方法来获取对象的原型。
当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。原型链的尽头一般来说都是 Object.prototype 所以这就是我们新建的对象为什么能够使用 toString() 等方法的原因。
特点:
JavaScript 对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。
8.谈谈this对象的指向问题
this 是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。在实际开发中,this 的指向可以通过四种调用模式来判断。
1.第一种是函数调用模式,当一个函数不是一个对象的属性时,直接作为函数来调用时,this 指向全局对象。
2.第二种是方法调用模式,如果一个函数作为一个对象的方法来调用时,this 指向这个对象。
3.第三种是构造器调用模式,如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。
4.第四种是 apply 、 call 和 bind 调用模式,这三个方法都可以显示的指定调用函数的 this 指向。其中 apply 方法接收两个参数:一个是 this 绑定的对象,一个是参数数组。call 方法接收的参数,第一个是 this 绑定的对象,后面的其余参数是传入函数执行的参数。也就是说,在使用 call() 方法时,传递给函数的参数必须逐个列举出来。bind 方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this 指向除了使用 new 时会被改变,其他情况下都不会改变。
这四种方式,使用构造器调用模式的优先级最高,然后是 apply 、 call 和 bind 调用模式,然后是方法调用模式,然后是函数调用模式。
9.什么是闭包,为什么要用它?
闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,创建的函数可以访问到当前函数的局部变量。
闭包有两个常用的用途。
闭包的第一个用途是使我们在函数外部能够访问到函数内部的变量。通过使用闭包,我们可以通过在外部调用闭包函数,从而在外
部访问到函数内部的变量,可以使用这种方法来创建私有变量。
函数的另一个用途是使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以
这个变量对象不会被回收。
其实闭包的本质就是作用域链的一个特殊的应用,只要了解了作用域链的创建过程,就能够理解闭包的实现原理。
10.跨域方法
首先我们如果只是想要实现主域名下的不同子域名的跨域操作,我们可以使用设置 document.domain 来解决。
(1)将 document.domain 设置为主域名,来实现相同子域名的跨域操作,这个时候主域名下的 cookie 就能够被子域名所访问。同时如果文档中含有主域名相同,子域名不同的 iframe 的话,我们也可以对这个 iframe 进行操作。
如果是想要解决不同跨域窗口间的通信问题,比如说一个页面想要和页面的中的不同源的 iframe 进行通信的问题,我们可以使用 location.hash 或者 window.name 或者 postMessage 来解决。
(2)使用 location.hash 的方法,我们可以在主页面动态的修改 iframe 窗口的 hash 值,然后在 iframe 窗口里实现监听函数来实现这样一个单向的通信。因为在 iframe 是没有办法访问到不同源的父级窗口的,所以我们不能直接修改父级窗口的 hash 值来实现通信,我们可以在 iframe 中再加入一个 iframe ,这个 iframe 的内容是和父级页面同源的,所以我们可以 window.parent.parent 来修改最顶级页面的 src,以此来实现双向通信。
(3)使用 window.name 的方法,主要是基于同一个窗口中设置了 window.name 后不同源的页面也可以访问,所以不同源的子页面可以首先在 window.name 中写入数据,然后跳转到一个和父级同源的页面。这个时候级页面就可以访问同源的子页面中 window.name 中的数据了,这种方式的好处是可以传输的数据量大。
(4)使用 postMessage 来解决的方法,这是一个 h5 中新增的一个 api。通过它我们可以实现多窗口间的信息传递,通过获取到指定窗口的引用,然后调用 postMessage 来发送信息,在窗口中我们通过对 message 信息的监听来接收信息,以此来实现不同源间的信息交换。
如果是像解决 ajax 无法提交跨域请求的问题,我们可以使用 jsonp、cors、websocket 协议、服务器代理来解决问题。
(5)使用 jsonp 来实现跨域请求,它的主要原理是通过动态构建 script 标签来实现跨域请求,因为浏览器对 script 标签的引入没有跨域的访问限制 。通过在请求的 url 后指定一个回调函数,然后服务器在返回数据的时候,构建一个 json 数据的包装,这个包装就是回调函数,然后返回给前端,前端接收到数据后,因为请求的是脚本文件,所以会直接执行,这样我们先前定义好的回调函数就可以被调用,从而实现了跨域请求的处理。这种方式只能用于 get 请求。
(6)使用 CORS 的方式,CORS 是一个 W3C 标准,全称是"跨域资源共享"。CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,因此我们只需要在服务器端配置就行。浏览器将 CORS 请求分成两类:简单请求和非简单请求。对于简单请求,浏览器直接发出 CORS 请求。具体来说,就是会在头信息之中,增加一个 Origin 字段。Origin 字段用来说明本次请求来自哪个源。服务器根据这个值,决定是否同意这次请求。对于如果 Origin 指定的源,不在许可范围内,服务器会返回一个正常的 HTTP 回应。浏览器发现,这个回应的头信息没有包含 Access-Control-Allow-Origin 字段,就知道出错了,从而抛出一个错误,ajax 不会收到响应信息。如果成功的话会包含一些以 Access-Control- 开头的字段。
非简单请求,浏览器会先发出一次预检请求,来判断该域名是否在服务器的白名单中,如果收到肯定回复后才会发起请求。
(7)使用 websocket 协议,这个协议没有同源限制。
(8)使用服务器来代理跨域的访问请求,就是有跨域的请求操作时发送请求给后端,让后端代为请求,然后最后将获取的结果发返回。
11.作用域与变量声明提升?
变量提升的表现是,无论我们在函数中何处位置声明的变量,好像都被提升到了函数的首部,我们可以在变量声明前访问到而不会报错。
造成变量声明提升的本质原因是 js 引擎在代码执行前有一个解析的过程,创建了执行上下文,初始化了一些代码执行时需要用到的对象。当我们访问一个变量时,我们会到当前执行上下文中的作用域链中去查找,而作用域链的首端指向的是当前执行上下文的变量对象,这个变量对象是执行上下文的一个属性,它包含了函数的形参、所有的函数和变量声明,这个对象的是在代码解析的时候创建的。这就是会出现变量声明提升的根本原因。
12.数组和对象有哪些原生方法,列举一下?
数组和字符串的转换方法:toString()、toLocalString()、join() 其中 join() 方法可以指定转换为字符串时的分隔符。
数组尾部操作的方法 pop() 和 push(),push 方法可以传入多个参数。
数组首部操作的方法 shift() 和 unshift() 重排序的方法 reverse() 和 sort(),sort() 方法可以传入一个函数来进行比较,传入前后两个值,如果返回值为正数,则交换两个参数的位置。
数组连接的方法 concat() ,返回的是拼接好的数组,不影响原数组。
数组截取办法 slice(),用于截取数组中的一部分返回,不影响原数组。
数组插入方法 splice(),影响原数组查找特定项的索引的方法,indexOf() 和 lastIndexOf() 迭代方法 every()、some()、filter()、map() 和 forEach() 方法
数组归并方法 reduce() 和 reduceRight() 方法
13.JavaScript 继承的几种实现方式?
我了解的 js 中实现继承的几种方式有:
(1)第一种是以原型链的方式来实现继承,但是这种实现方式存在的缺点是,在包含有引用类型的数据时,会被所有的实例对象所共享,容易造成修改的混乱。还有就是在创建子类型的时候不能向超类型传递参数。
(2)第二种方式是使用借用构造函数的方式,这种方式是通过在子类型的函数中调用超类型的构造函数来实现的,这一种方法解决了不能向超类型传递参数的缺点,但是它存在的一个问题就是无法实现函数方法的复用,并且超类型原型定义的方法子类型也没有办法访问到。
(3)第三种方式是组合继承,组合继承是将原型链和借用构造函数组合起来使用的一种方式。通过借用构造函数的方式来实现类型的属性的继承,通过将子类型的原型设置为超类型的实例来实现方法的继承。这种方式解决了上面的两种模式单独使用时的问题,但是由于我们是以超类型的实例来作为子类型的原型,所以调用了两次超类的构造函数,造成了子类型的原型中多了很多不必要的属性。
(4)第四种方式是原型式继承,原型式继承的主要思路就是基于已有的对象来创建新的对象,实现的原理是,向函数中传入一个对象,然后返回一个以这个对象为原型的对象。这种继承的思路主要不是为了实现创造一种新的类型,只是对某个对象实现一种简单继承,ES5 中定义的 Object.create() 方法就是原型式继承的实现。缺点与原型链方式相同。
(5)第五种方式是寄生式继承,寄生式继承的思路是创建一个用于封装继承过程的函数,通过传入一个对象,然后复制一个对象的副本,然后对象进行扩展,最后返回这个对象。这个扩展的过程就可以理解是一种继承。这种继承的优点就是对一个简单对象实现继承,如果这个对象不是我们的自定义类型时。缺点是没有办法实现函数的复用。
(6)第六种方式是寄生式组合继承,组合继承的缺点就是使用超类型的实例做为子类型的原型,导致添加了不必要的原型属性。寄生式组合继承的方式是使用超类型的原型的副本来作为子类型的原型,这样就避免了创建不必要的属性。
14.介绍一下 js 的节流与防抖?
函数防抖: 在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。
函数节流: 规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
15.异步编程?
js 中的异步机制可以分为以下几种:
第一种最常见的是使用回调函数的方式,使用回调函数的方式有一个缺点是,多个回调函数嵌套的时候会造成回调函数地狱,上下两层的回调函数间的代码耦合度太高,不利于代码的可维护。
第二种是 Promise 的方式,使用 Promise 的方式可以将嵌套的回调函数作为链式调用。但是使用这种方法,有时会造成多个 then 的链式调用,可能会造成代码的语义不够明确。
第三种是使用 generator 的方式,它可以在函数的执行过程中,将函数的执行权转移出去,在函数外部我们还可以将执行权转移回来。当我们遇到异步函数执行的时候,将函数执行权转移出去,当异步函数执行完毕的时候我们再将执行权给转移回来。因此我们在 generator 内部对于异步操作的方式,可以以同步的顺序来书写。使用这种方式我们需要考虑的问题是何时将函数的控制权转移回来,因此我们需要有一个自动执行 generator 的机制,比如说 co 模块等方式来实现 generator 的自动执行。
第四种是使用 async 函数的形式,async 函数是 generator 和 promise 实现的一个自动执行的语法糖,它内部自带执行器,当函数内部执行到一个 await 语句的时候,如果语句返回一个 promise 对象,那么函数将会等待 promise 对象的状态变为 resolve 后再继续向下执行。因此我们可以将异步逻辑,转化为同步的顺序来书写,并且这个函数可以自动执行。
Vue相关
15.Vue 的各个生命阶段是什么?
Vue 一共有8个生命阶段,分别是创建前、创建后、加载前、加载后、更新前、更新后、销毁前和销毁后,每个阶段对应了一个生命周期的钩子函数。
(1)beforeCreate 钩子函数,在实例初始化之后,在数据监听和事件配置之前触发。因此在这个事件中我们是获取不到 data 数据的。
(2)created 钩子函数,在实例创建完成后触发,此时可以访问 data、methods 等属性。但这个时候组件还没有被挂载到页面中去,所以这个时候访问不到 $el 属性。一般我们可以在这个函数中进行一些页面初始化的工作,比如通过 ajax 请求数据来对页面进行初始化。
(3)beforeMount 钩子函数,在组件被挂载到页面之前触发。在 beforeMount 之前,会找到对应的 template,并编译成 render 函数。
(4)mounted 钩子函数,在组件挂载到页面之后触发。此时可以通过 DOM API 获取到页面中的 DOM 元素。
(5)beforeUpdate 钩子函数,在响应式数据更新时触发,发生在虚拟 DOM 重新渲染和打补丁之前,这个时候我们可以对可能会被移除的元素做一些操作,比如移除事件***。
(6)updated 钩子函数,虚拟 DOM 重新渲染和打补丁之后调用。
(7)beforeDestroy 钩子函数,在实例销毁之前调用。一般在这一步我们可以销毁定时器、解绑全局事件等。
(8)destroyed 钩子函数,在实例销毁之后调用,调用后,Vue 实例中的所有东西都会解除绑定,所有的事件***会被移除,所有的子实例也会被销毁。
当我们使用 keep-alive 的时候,还有两个钩子函数,分别是 activated 和 deactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 actived 钩子函数。
- Vue 组件间的参数传递方式?
(1)父子组件间通信
第一种方法是子组件通过 props 属性来接受父组件的数据,然后父组件在子组件上注册监听事件,子组件通过 emit 触发事件来向父组件发送数据。
第二种是通过 ref 属性给子组件设置一个名字。父组件通过 parent 获得父组件,这样也可以实现通信。
第三种是使用 provider/inject,在父组件中通过 provider 提供变量,在子组件中通过 inject 来将变量注入到组件中。不论子组件有多深,只要调用了 inject 那么就可以注入 provider 中的数据。
(2)兄弟组件间通信
第一种是使用 eventBus 的方法,它的本质是通过创建一个空的 Vue 实例来作为消息传递的对象,通信的组件引入这个实例,通信的组件通过在这个实例上监听和触发事件,来实现消息的传递。
第二种是通过 refs 来获取到兄弟组件,也可以进行通信。
(3)任意组件之间
使用 eventBus ,其实就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。
如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候采用上面这一些方法可能不利于项目的维护。这个时候
可以使用 vuex ,vuex 的思想就是将这一些公共的数据抽离出来,将它作为一个全局的变量来管理,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。
17.computed 和 watch 的差异?
computed 是计算属性,依赖其他属性计算值,并且 computed 的值有缓存,只有当计算值变化才会返回内容。
watch 监听到值的变化就会执行回调,在回调中可以进行一些逻辑操作。
18.vue-router 中的导航钩子函数?
(1)全局的钩子函数 beforeEach 和 afterEach
beforeEach 有三个参数,to 代表要进入的路由对象,from 代表离开的路由对象。next 是一个必须要执行的函数,如果不传参数,那就执行下一个钩子函数,如果传入 false,则终止跳转,如果传入一个路径,则导航到对应的路由,如果传入 error ,则导航终止,error 传入错误的监听函数。
(2)单个路由独享的钩子函数 beforeEnter,它是在路由配置上直接进行定义的。
(3)组件内的导航钩子主要有这三种:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。它们是直接在路由组件内部直接进行定义的。 router 是“路由实例”对象包括了路由的跳转方法,钩子函数等。