题````····

@1、数组几种去重的方法:


var arr = [1,1,2,a,a,f,e,a]
一、利用ES6 Set去重
function unique(arr){
    return Array.form(new Set(arr))
}

console.info(unique(arr))
二、利用indexof
function unique(arr){
    if (!Array.isArray(arr)){
        return
    }
    var array = []
    for (var i = 0; i < arr.length; i++) {
        if (array.indexOf(arr[i]) === -1) {
            array.push(arr[i])
        }
    }
    return array;
}
console.info(unique(arr))
三、利用includes
function unique(arr){
    if (!Array.isArray(arr)){
        return
    }
    var array = []
    for (var i = 0; i < arr.length; i++) {
        if (!array.includes(arr[i])) {
            array.push(arr[i])
        }
    }
    return array;
}
console.info(unique(arr))
四、利用循环套循环,splice去重
function unique(arr){
    for(var i=0;i<arr.length;i++){
        for(var j=1;j<arr.length;j++){
            if (arr[i] == arr[j]){
                arr.splice(j,1)
                j--
            }
        }
    }
    return arr
}
console.info(unique(arr))
五、利用sort()
function unique(arr){
    if (!Array.isArray(arr)){
        return
    }
    arr = arr.sort()
    var array = arr[0]
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] !=arr[i-1]) {
            array.push(arr[i])
        }
    }
    return array;
}
console.info(unique(arr))
六、利用filter去重
function unique(arr){
    retrun arr.filter(function(item, index, arr){
        return arr.indexof(item,0) == index
    })
}
console.info(unique(arr))

2、promise

1)Promise的实例有三个状态:

  • Pending(进行中)
  • fulfilled (已完成)
  • Rejected(已拒绝)

(2)Promise的实例有两个过程

  • pending -> fulfilled : Resolved(已完成)
  • pending -> rejected:Rejected(已拒绝)

        1、new实例化一个promise对象,promise的构造函数中传递一个参数,是一个函数,该函数用于处理异步任务

        2、传入两个参数:resolve和reject,分别代表异步成功后的回调函数和失败后的回调函数;

        3、通过promise.then()处理返回结果;

第一步:model层的接口封装
const promise = new Promise((resolve,reject)=>{
    // 这里做异步任务
    setTimeout(function(){
        var data ={retCode:’0000’,msg:’成功’}
        If(data.retCode == ‘0000’){
            resolve(data)
        } else{
            reject({retCode:-1,msg:’失败’})
        }
    },100)
})
第二步:业务层的接口调用,这里的data就是resolve和reject传递过来的
promise.then(data => {
    console.info(data)//resolve获得的正常的数据
}).catch(data => {
    console.info(data)// reject获得的异常结果
})

/*

封装promise接口调用

*/

function queryData(url){
    return new promise((resolve,reject)=>{
        var xhr = xmlHttpRequest()
        xhr.onreadystatechange = function(){
            If (xhr.readystate !=4 ) return
            If (xhr.readystate == 4&&xhr.status == 200){
                resolve(xhr.responseText)
            }else {
                reject(服务器错误)
            }
        }
        xhr.open(‘get’,url)
        xhr.send(null)
    })
}
queryData(‘后台接口’)

3、vue 组件传值

通过props和$emit()

1)父组件向子组件传值:

:users=”users”    (父)

props:{

        users:{

                type:Array

        }

}

2) 子组件向父组件传值

子组件:绑定一个点击事件,点击事件里面用 this.$emit(‘titleChange’,’子向父传值’);

父组件:v-on:titleChange=”updateTitle”,updateTitle事件里面的值就是子组件传过来的值

3) $emit/$on(同族跨级通信)

var event = new vue()

event.$emit(事件名,数据);

event.$on(事件名,data=>{})

4) $parent方法:子组件可以直接访问该组件的父实例;

5) ref:给子组件加上ref属性 <result ref=’result’></resulte>     父:this.$refs.result.data = this.data

ref 链:父组件要给子组件传值,在子组件上定义一个 ref 属性,这样通过父组件的 $refs 属性就可以获取子组件的值了,也可以进行父子,兄弟之间的传值($parent / $children与 ref类似)

常见使用场景可以分为三类:
父子通信:
父向子传递数据是通过 props,子向父是通过 events($emit);
通过父链 / 子链也可以通信($parent / $children);
ref 也可以访问组件实例;
provide / inject API;
$attrs/$listeners
vuex
兄弟通信:
事件总线Bus;
Vuex
跨级通信:
事件总线Bus;
Vuex;
provide / inject API
$attrs/$listeners

4、防抖、节流原理、区别

1)防抖-原理:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

  1. 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖;
  2. 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖;
  3. 文本编辑器实时保存,当无任何更改操作一秒后进行保存。

按钮提交:防止多次提交按钮,只执行最后提交的一次;  下拉触底加载下一页

function debounce(fn,delay){
 	Let delays = delay||500
    let timer;
    return function(){
        let th = this;
        let args = arguments
        if (timer){
            clearTimeout(timer)
        }
        timer = setTimeout(function(){
            timer = null;
            fn.apply(th,args)
        },delays)
    }
}

2)节流-原理:规定在一个单位时间内,只能触发一次函数。如果在这个单位时间内触发多次函数,只生效一次   搜索查询

  1. 鼠标连续不断地触发某事件(如点击),单位时间内只触发一次;
  2. 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断。例如:懒加载;
  3. 浏览器播放事件,每个一秒计算一次进度信息等。
function throttle(fn, wait) { 
    let last, timer; 
    let interval = wait || 200; 
    return function() { 
        let th = this, args = arguments; 
        let now = +new Date(); 
        if (now - last < interval) { 
        clearTimeout(timer); 
        timer = setTimeout(function() {
             last = now; fn.apply(th, args); 
        }, interval); 
        }else { 
            last = now; fn.apply(th, args); 
        }
    }
}

5、如何区别object和array

1) es6的Array.isArray([])
2) [] instanceof Array
3) [].constructor
4) object.prototype.toString.call([])
5)Array.prototype.isPrototypeof(arr)

6、axios原理

是一个基于promise的http库,可以用于浏览器、nodejs。xmlHttpRequest是浏览器内置的一个对象,它为客户端提供了在客户端和服务器之间传输数据的功能

axios 在对xhr对象进行封装的过程之中使用了promise 将这个promise的res函数赋值给了canceltoken 当canceltoken执行 这个promise的状态被决议时 xhr.abort() 此时axios的请求的promise也失败 请求取消

axios特性:

  1. 在node.js中发送请求会创建http请求
  2. 在浏览器会发送请求会创建xmlhttpRequest
  3. promise封装,故支持promiseAPI
  4. 支持拦截器interceptors,可以分别设置请求拦截和响应拦截,在发出请求和响应到达then之前进行判断处理。
  5. 转换请求数据和响应数据
  6. 取消请求:axios提供了取消请求的接口,实际上在源码中还是通过最关键的一步request.abort()来取消请求,使用isCancel来作为是否已经取消的标识,使用Cancel来作为重复取消时抛出的错误,使用CancelToken来作为取消请求的核心处理,在处理中采取promise异步的方法
  7. 自动转换json数据:
  8. 客户端支持防御xsrf攻击:首先axios会判断当前环境是否为标准的浏览器环境,如果是标准的浏览器环境的话,就会继续执行。对于XSRF攻击来说,最基本的就是跨域了,所以我们对发出请求的域和当前的域做同源判断,如果是跨域的话,就必须有凭证config.withCredentials || isURLSameOrigin(fullPath),凭证简单来说就是当前请求在跨域时是否可以带上cookie。
  9. axios中的发送字段的参数是data和params两个,两者的区别在于params是跟请求地址一起发送的,data的作为一个请求体进行发送
    params一般适用于get请求,data一般适用于post put请求

  10. 同时支持浏览器端和服务端的请求;vue用这一特性对vue的服务端渲染服务

 axios较其他网络请求库的优点

        1.简单易用,api接近于jquery,比原生的fetch之类简单
        2.浏览器兼容性好,都能兼容IE7,使用fetch的话就得自己处理兼容
        3.通用性好,能在node和浏览器中使用,api一致
        4.稳定大牌,vue官网文档中有推荐

基本原理
        1.axios还是属于xhr,因此需要实现一个ajax或基于http;
        2.需要结合promise对象对结果进行处理

缺点
        1.不支持jsonp,需要自己封装
        2.基于xhr实现,所以无法在service worker,web worker中使用

7、v-if和v-for哪个优先级更高?v-if、v-show

一、

永远不要把 v-if 和 v-for 同时用在同一个元素上;

vue2中v-for的优先级是高于v-if;在vue3中,则完全相反,v-if的优先级高于v-for

1)为了过滤列表中的项目:users.filter(e => e.active)

2)为了避免渲染本应该被隐藏的列表:此时把 v-if 移动至容器元素上 (比如 ul、ol)或者外面包一层template即可。

二、

控制手段不同:v-show 为该元素添加css-display:none,dom元素还存在;v-if:dom元素整体添加或者隐藏;

编译过程不同:v-show只是css切换,但是v-if 是切换有一个局部编译/卸载的过程,切换过程中合适的销毁或重建内部的事件监听和子组件;

编译条件:v-if为假时不做操作,为真时才操作。v-show不会触发生命周期,v-if 为true时,触发组件的beforeCreate 、create、beforeMount、mounted

v-if 与 v-show 都能控制dom元素在页面的显示

v-if 相比 v-show 开销更大的(直接操作dom节点增加与删除)

如果需要非常频繁地切换,则使用 v-show 较好

如果在运行时条件很少改变,则使用 v-if 较好

8、Vue虚拟Dom与diff算法、v-for为什么用key

虚拟dom:什么是虚拟dom,就是我们说的虚拟节点。用js对象来模拟真实dom的节点,该对象包含了真实dom的结构与属性,用于对比虚拟dom和真实dom的差异。从而进行局部渲染,达到优化性能的目的

1)比较只会在同层级进行, 不会跨层级比较;在diff比较的过程中,循环从两边向中间比较

通过新旧虚拟dom进行作为对比(diff),将变化的地方更新到真实的dom上。

2)vue中diff执行的时刻是组建实例执行其更新函数时,它会比对上一次的渲染结果oldVnode和新的渲染结果newVnode,此过程称为patch

3)diff过程整体遵循深度优先,同层比较的策略,两个节点之间会根据他们是否拥有子节点或者文本节点做不同的操作,比较两组子节点,是算法的重点。首先,假设头尾节点可能相同4次做比对,如果没有找到相同节点才会按照通用的办法遍历查找,查找结束才按照情况处理剩下的节点。借助key通常可以非常精确找到相同的节点,因此整个patch过程非常高效。

可以快速查找到节点,能够减少渲染次数,提高渲染性能

9、简述 Vue 的生命周期以及每个阶段做的事

每个vue组件实例被创建后都会经过一系列初始化步骤,比如:它需要数据观测、模版编译、挂载实例到dom上,以及数据变化时更新dom,这个过程会运行叫做生命周期钩子的函数,以便用户在特定的阶段添加自己的代码。

生命周期总共分为8个阶段:创建前后,载入前后,更新前后,销毁前后,以及一些特殊场景的生命周期。vue3中,新增了三个用于调试和服务器端渲染场景

beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed

beforeCreate:通常用于插件开发中执行一些初始化任务

created:组件初始化完毕,可以访问各种数据,获取接口数据等

mounted:dom已创建,可用于获取访问数据和dom元素;访问子组件等。

beforeUpdate:此时view层还未更新,可用于获取更新前各种状态

updated:完成view层的更新,更新后,所有状态已是最新

beforeunmount:实例被销毁前调用,可用于一些定时器或订阅的取消

unmounted:销毁一个实例。可清理它与其它实例的连接,解绑它的全部指令及事件监听器

10、keep-alive、mixins

1、概念:

keep-alive是Vue的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁。

2、作用:

用来缓存组件,避免多次加载相同的组件,减少性能消耗,提高用户体验。

mixins 是vue2.x提供的一种灵活的方式,可以向任意组件或者全局混入datamethodscomponentsdirectives等。mixins适用于全局公用数据、方法、组件等的注入的场景。

vue-router 插件的内部实现就是基于 mixins 

11、子组件可以直接改变父组件的数据么,说明原因

组件化开发过程中有个单项数据流原则

所有的 prop 使得父子之间形成了单向下行绑定:父级prop的更新会向下流动到子组件中,反之不行。这样会防止子组件意外改变父组件的状态,从而导致你的应用数据流向难以理解。另外,每次父组件发生变更时,子组件所有的的prop都会更新为最新的值。这意味着你不应该在子组件内部改变prop的值

如果确实想要改变父组件属性应该emit一个事件让父组件去做这个变更。注意虽然我们不能直接修改一个传入的对象或者数组类型的prop,但是我们还是能够直接改内嵌的对象或属性。

12、Vue要做权限管理该怎么做?控制到按钮级别的权限怎么做?

把所有页面路由信息存在数据库中,用户登录的时候根据其角色查询得到其能访问的所有页面路由信息返回给前端,前端再通过addRoutes动态添加路由信息按钮权限的控制通常会实现一个指令,例如v-permission,将按钮要求角色通过值传给v-permission指令,在指令的moutned钩子中可以判断当前用户角色和按钮是否存在交集,有则保留按钮,无则移除按钮。

13、setTimeout、Promise、Async/Await 的区别

1. setTimeout
console.log('script start')	//1. 打印 script start
setTimeout(function(){
    console.log('settimeout')	// 4. 打印 settimeout
})	// 2. 调用 setTimeout 函数,并定义其完成后执行的回调函数
console.log('script end')	//3. 打印 script start
// 输出顺序:script start->script end->settimeout

2. Promise
console.log('script start')
let promise1 = new Promise(function (resolve) {
    console.log('promise1')
    resolve()
    console.log('promise1 end')
}).then(function () {
    console.log('promise2')
})
setTimeout(function(){
    console.log('settimeout')
})
console.log('script end')
// 输出顺序: script start->promise1->promise1 end->script end->promise2->settimeout

3. async/await
async function async1(){
   console.log('async1 start');
    await async2();
    console.log('async1 end')
}
async function async2(){
    console.log('async2')
}

console.log('script start');
async1();
console.log('script end')

// 输出顺序:script start->async1 start->async2->script end->async1 end

14、算法

已知如下数组:

编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组

var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
方法一:
console.info(Array.from(new Set(arr.toString().split(','))).map(Number).sort((a,b) => a-b))
方法二:
console.log(Array.from(new Set(arr.flat(Infinity))).sort((a, b) => a - b))

扁平的数据结构,转成树

let arr = [
    {id: 1, name: '部门1', pid: 0},
    {id: 2, name: '部门2', pid: 1},
    {id: 3, name: '部门3', pid: 1},
    {id: 4, name: '部门4', pid: 3},
    {id: 5, name: '部门5', pid: 4},
]
function arrToTree (items){
	var result = []
	var itemMap = {}
	for (var item of items) {
		itemMap[item.id] = {...item,children:[]}
	}
	// console.info('itemMap', itemMap)
	for (var item of items) {
		var id = item.id
		var pid = item.pid
		var treeItem = itemMap[id]
		// console.info('treeItem', treeItem)
		if (pid == 0){
			result.push(treeItem)
		} else {
			// console.info('itemMap[pid]', itemMap[pid])
			if (!itemMap[pid]) {
				itemMap[pid] = {
					children:[]
				}
			}
			itemMap[pid].children.push(treeItem)
		}
	}
	console.info('result', result)
	return result
}
arrToTree(arr)

15、谈谈你对TCP三次握手和四次挥手的理解

1、浏览器的地址栏输入URL并按下回车。

2、浏览器查找当前URL是否存在缓存,并比较缓存是否过期。

3、DNS解析URL对应的IP。

4、根据IP建立TCP连接(三次握手)。

5、HTTP发起请求。

6、服务器处理请求,浏览器接收HTTP响应。

7、关闭TCP连接(四次挥手)。

8、渲染页面,构建DOM树。

16、router.afterEach和router.beforeEach

1) router.afterEach是全局导航钩子函数,进入到某个路由后,触发的函数。

应用场景:切换每个页面的title、切换页面的时候,滚动到最上面

router.afterEach((to,form,next) => {
    document.title = to.title
    window.scrollTo(0,0)
})

2) router.beforeEach是路由的局部导航守卫

获取token,判断页面是否有token,如果有,放行,没有,则跳转到登录页面,提示登录失败

router.beforeEach((to,from,next) => {
    let token = localstorage.getItem('token')
    if (to.path = '/login' || token){
        next()
    } else {
        setTimeout(function(){
            next({
                path:'/login'
            })
        },500)
    }
})

17、jsBridge

主要给JavaScript提供调用native的功能接口,让混合开发的前端可以方便的调用native功能(地理位置、摄像头)。但是jsBridge不只是这么简单,是native和非native之前的桥梁,核心是构建native和非native消息通信的通道

JavaScript调用native的方式主要两种:注入API、拦截URL SCHEME

注入API的原理:通过web view提供的接口,向javaScript的context中注入对象或方法,让javascript调用时直接执行相应的native代码逻辑,达到JavaScript调用native的目的。

18、js事件循环队列

同步任务:在主线程上排队完成任务,只有前一个任务执行完成之后,才能执行后一个任务。

异步任务:不进入主线程,进入任务队列,只有任务队列通知主线程,某个异步任务可以执行了,任务才能进入主线程。

事件循环:同步和异步分别进入不同的执行环境,同步的进入主线程,异步的进入任务队列。主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。 上述过程的不断重复就是我们说的 Event Loop (事件循环)。

流程如下:

1、调用栈顺序调用任务----------------------(往调用栈里面push任务,顺序执行同步任务)

2、当调用栈发现异步任务时,将异步任务交给其他模块处理,自己继续进行下面的调用

3、异步执行完毕,异步模块将任务推入任务队列,并通知调用栈

4、调用栈在执行完当前任务后,将执行任务队列里的任务----(宏变微,微进主)

5、调用栈执行完任务队列里的任务之后,继续执行其他任务

任务队列的宏任务和微任务

宏任务,大概包括:script(整体代码), setTimeout, setInterval, setImmediate(NodeJs), I/O, UI rendering。

微任务,大概包括: process.nextTick(NodeJs), Promise, Object.observe(已废弃), MutationObserver(html5新特性)

19、简述computed和watch的使用场景

computed:vue是响应式基于发布订阅模式,依赖收集追踪

computed作用:(1)解决模版中放入过多的逻辑会让模版过重且难以维护的问题,例如:两个数据的拼接,或者字体颜色的判断。(2)他支持缓存,只有依赖的数据发生了变化,才会重新计算,例如模版中多次用到的数据拼接可以用计算属性,只执行一次。除非数据发生变化。(3)不支持异步,如果有异步的操作,无法监听数据的变化。(4)computed的值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data声明过,或者父组件传递过来的props中的数据进行计算的。

watch:(1)支持异步。(2)监听的函数接收两个参数,一个是新值,一个是输入之前的值。(3)支持异步监听。(4)监听data或者props传来的数据,发生变化时会触发相应操作。有两个参数:immediate:立即触发回调函数。

deep:深度监听,发现数据内部的变化,在复杂数据类型中使用,例如数组中的对象发生变化。需要注意的是,deep无法监听到数组和对象内部的变化。

总结:

(1)computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。

(2)watch 侦听器 : 更多的是观察的作用,无缓存性,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作。

使用场景:

computed:多对一,多个数据影响一个。当需要进行数值计算,并依赖其他数值时,可以用computed。可以用它的缓存属性,不需要每次获取值时都计算。

watch:是一对多,一个数据发生变化,执行相应操作会影响多个数据。当需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许执行异步操作 ( 访问一个 API ),限制执行该操作的频率,并在得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

20、深拷贝、浅拷贝

深拷贝和浅拷贝是根据复杂类型来说,浅拷贝只拷贝一层,深拷贝是层层拷贝。

1)浅拷贝:将原对象、原数组的引用,直接赋值给新对象、新数组。新对象只是对原数组的引用,并不复制对象本身。新、原数组还是共享同一个内存。

如果属性是一个基本数据类型,拷贝就是基本类型的值如果属性是引用类型,拷贝的就是内存地址

2)深拷贝:创建一个新的对象和数组,将原对象各项指数的值都拷贝过来,是值,不是引用。

深拷贝是把一个对象从内存中完全拷贝过来,从堆内存中开辟一个新区域,用来存新对象。修改新对象,不会影响原对象。

3)赋值:当我们把一个对象赋值给一个新的变量时,赋的是该对象在栈中的内存地址,而不是堆中的数据。也就是两个对象

   浅拷贝的实现方式:

   1、object.assign()

   2、lodash 里面的 _.clone

   3、...扩展运算符

   4、 Array.prototype.concat

   5、 Array.prototype.slice

var arr1 = [1,3,{
 user: 'aaa'
}]
var arr2 = arr1.concat();
arr2[0] = '一';
arr2[2].user = 'AAA';
console.log('arr1',arr1)
console.log('arr2',arr2)


var arr1 = [1,3,{
 user: 'aaa'
}]
var arr2 = arr1.slice();
arr2[0] = '一';
arr2[2].user = 'AAA';
console.log('arr1',arr1)
console.log('arr2',arr2)

   深拷贝的实现方式

   1、 JSON.parse(JSON.stringify())

   2、递归操作:

function deepCopy(obj){
 var result= Array.isArray(obj) ? [] : {}
 if (obj && typeof(obj) === 'object') {
  for (let i in obj) {
   if (obj.hasOwnProperty(i)){ // 思考:这句是否有必要?
    if (obj[i] && typeof(obj[i]) === 'object') {
     result[i] = deepCopy(obj[i])
    } else {
     result[i] = obj[i]
    }
   }
  }
 }
 return result
}
var obj1 = {
 a: 1,
 b: {
  c: 2
 }
};
var obj2 = deepCopy(obj1);
obj2.a = '一';
obj2.b.c = '二'
console.log('obj1', obj1)
console.log('obj2', obj2)

   3、cloneDeep

   4、Jquery.extend()

21、map和forEach的区别

相同点:(1)都是循环遍历数组的每一项;(2)每次执行匿名函数,都是有三个参数(item,index,arr);(3)匿名函数中的this都是指向window;(4)只能遍历数组

不同点:map会分配内存空间存储新数据并返回,但是forEach不会返回;

forEach允许callback更新原始数组的元素,map返回新数组

22、ES6新特性

1、var let const的区别

var 是全局变量、顶层变量、存在变量提升、一个变量进行多次声明,后面声明的变量会覆盖前面声明的变量(如果在函数中使用声明var,该变量是局部的。在函数内不使用var,则是全局的)

let 是只在所在代码块内有效,不存在变量提升。不允许在相同作用域中重复声明

const声明是一个只读常量,一旦声明,常量的值就不能改变。一旦声明变量,就必须初始化,不能留到以后赋值。如果之前用var、let 声明过变量,再用const声明,同样会报错

const 不是变量的值不能改变,而是变量所指向的内存地址所保存的数据不能改变:对于简单数据类型,值就保存在变量指向那个内存地址,等同于常量;对于复杂类型数据,变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的,并不能确保改变量的结构不变

区别:

1、变量提升:

var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined
let和const不存在变量提升,即它们所声明的变量一定要在声明后使用,否则报错
2、暂时性死区:

var不存在暂时性死区
let和const存在暂时性死区,只有等到声明变量的那一行代码出现,才可以获取和使用该变量
3、块级作用域:

var不存在块级作用域
let和const存在块级作用域
4、重复声明:

var允许重复声明变量
let和const在同一作用域不允许重复声明变量
5、修改声明的变量:

var和let可以
const声明一个只读的常量。一旦声明,常量的值就不能改变

23、常用方法

1、Object.keys(obj):

参数:要返回其自身属性的对象;返回值:一个表示给定对象的所有可枚举属性的字符串数组

处理对象,返回可枚举的属性数组
let person = {name:"张三",age:25,address:"深圳",getName:function(){}}
Object.keys(person) // ["name", "age", "address","getName"]
处理数组,返回索引值数组
let arr = [1,2,3,4,5,6]
Object.keys(arr) // ["0", "1", "2", "3", "4", "5"]
处理字符串,返回索引值数组
let str = "12345字符串"
Object.keys(str) // ["0", "1", "2", "3", "4", "5", "6", "7"]
常用技巧
let person = {name:"张三",age:25,address:"深圳",getName:function(){}}
Object.keys(person).map((key)=>{
  person[key] // 获取到属性对应的值,做一些处理
}) 

2、hasOwnProperty()方法用于检测一个对象是否含有特定的自身属性,返回一个布尔值

方法用来检测一个属性是否是对象的自有属性,而不是从原型链继承的。如果该属性是自有属性,那么返回 true,否则返回 false。换句话说,hasOwnProperty() 方法不会检测对象的原型链,只会检测当前对象本身,只有当前对象本身存在该属性时才返回 true。

hasOwnProperty() 的语法格式如下:
object.hasOwnProperty(propertyName);
参数说明:propertyName 参数表示要检测的属性名称。
返回值:返回一个布尔值。如果 propertyName 是自有属性,那么返回 true,否则返回 false。

3、判断两个对象是否相等

首先判断两个对象的长度,如果长度不相等,则对象不相等,flag为false。

如果长度相等,则遍历对象1,利用hasOwnProperty,查看对象1是否包含对象2中的属性和方法,如果不包含,则flag为false,是不相等。

hasOwnProperty()方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性

        方法一
        var obj1={
			'name':'tian',
			'age':'18'
		}
		var obj2={
			'name':'tian',
			'age':'18'
		}
		function compreObj(obj1,obj2) {
			
			//1.拿到所有的 obj1==key, obj2===key
			//2.拿到所有的 obj1==value, obj2====value
			//3.对比 obj1_keys  obj2_keys 
			//4.对比 obj1_values obj2_values
			var flag = false
			var key1 = Object.keys(obj1);
			var key2 = Object.keys(obj2);

			var value1 = Object.values(obj1);
			var value2 = Object.values(obj2);

			console.log(key1.toString());
			console.log(value1);

			if ((key1.toString() === key2.toString()) && 
                (value1.toString() === value2.toString())) {
				flag = true
			}
			return flag
		}
    方法2
      function compreObj(obj1,obj2){
			var flag = true
			function compre (obj1,obj2){
				if (Object.keys(obj1).length != Object.keys(obj2).length) {
					flag = false
				} else {
					for(var x in obj1){
						if (obj2.hasOwnProperty(x)) {
							if (obj1[x] !== obj2[x]) {
								flag = false
							}
						}else {
							flag = false
						}
					}
				}
				if (flag == true) {
					return true
				} else {
					return false
				}
			}
			return compre(obj1,obj2)
		}

24、作用域、闭包

作用域:程序中定义变量的区域,他决定了当前执行代码,对变量的访问权限

全局作用域:程序的最外层作用域,是一直存在的

局部作用域:只有函数被定义时才会创建,包含在父级函数作用域/全局作用域中

/* 全局作用域开始 */
var a = 1;

function func () { /* func 函数作用域开始 */
  var a = 2;
  console.log(a);
}                  /* func 函数作用域结束 */

func(); // => 2

console.log(a); // => 1

/* 全局作用域结束 */

能够访问其他函数内部变量的函数,叫做闭包。闭包就是函数内部定义的函数,被返回出去并在外部调用。

函数A中返回一个函数B,函数B中使用了函数A里面的变量。函数B称之为闭包。

function foo (){
    var a = 2
    function bar (){
        console.info(a)
    }
    return bar
}
var baz = foo()
baz() //这就形成了闭包

应用场景:

1、模拟私有属性

对象中所有的方法和属性都可以被访问,这就造成了安全隐患,内部的属性任何开发者都可以随意修改。虽然语言层面不支持私有属性的创建,但是我们可以用闭包的手段来模拟出私有属性:

function getGeneratorFunc(){
    var _name = 'lily'
    var _age = 12
    return function () {
        return {
            getName: function(){return _name}
            getAge: function(){return _age}
        }
    }
}
var obj = getGeneratorFunc()
obj.getName() // lily
obj._age; // undefined

2、防抖截流。

3、循环赋值

for(var i=0;i<10;i++){
    (function(j){
        setTime(function(){
            console.info(j)
        },10)
    })(i)
}
// 依次输出1-10

25、Vue和React对比

vue:

Vue核心特性:数据驱动(mvvm)

相同点:都有组件化思想、支持服务器端渲染、都有虚拟dom、数据驱动视图、都有支持native的方案:vue week;react:react native、都有自己的构建工具:vue:vue-cli、react:create React App

区别:1、数据变化的实现原理不同:react使用的是不可变数据;vue 使用的是可变数据

2、组件化通信的不同:react我们通过使用回调函数来进行通信;vue中子组件向父组件传递消息有两种方式:事件和回调函数

3、diff算法不同:react使用diff队列保存需要更新哪些dom,得到patch树,再统一操作批量更新dom;vue 使用双向指针,边对比,边更新dom

26、双向绑定v-model的实现原理

双向绑定最核心的方法,就是通过object.difineproperty()来实现对属性的劫持,达到监听数据变动的目的
先是从data里面的数据msg通过绑定到input控件和p标签上。然后input上通过v-on:input监听控件,触发change()。
调用方法都可以默认获取e事件,e.target.value是获取调用该方法的DOM对象的value值。把value值在赋给data里的msg,就是实现了双向数据绑定的原理了。

27、如何解决跨域的问题

什么是跨域?

  跨域是由于浏览器的同源策略造成的,是浏览器施加的安全限制。

什么是同源策略/SOP(Same origin policy)?

  同源策略是一种约定,是浏览器最核心最基本的安全功能,缺少同源策略,浏览器容易收到XSS、CSRF等攻击。

  同源策略是:拥有相同的协议、域名、端口号的网址间才可以相互访问资源。

  一个域的页面去访问另一个域的资源就形成了跨域。

解决跨域的方法:

  注意:1.如果是协议和端口造成的跨域问题,前端无法处理;

     2.是否跨域,仅仅通过URL的首部来判断,不会通过域名对应的IP地址是否相同来判断;

     3.跨域并不是请求发不出去,而是请求发出去了,也正常返回结果了,但是结果被浏览器拦截了。

1.利用JSONP方式解决跨域

  利用script标签没有跨域的限制,网页可以从其他来源动态的获取JSON数据,从而实现跨域。

  JSONP跨域仅支持GET请求,一定要服务器支持才可以实现。

  JSONP是非同源策略,AJAX属于同源策略。 

2.利用CORS(Cross-Origin Resource Sharing)技术,需要前后端同时支持

  前端浏览器在IE9以上,后端在响应报头添加Access-Control-Allow-Origin标签,从而允许指定域的站点访问当前域上的资源。

  res.setHeader("Access-Control-Allow-Origin","*");
 不过CORS默认只支持GET/POST这两种http请求类型,如果要开启PUT/DELETE之类的方式,需要在服务端在添加一个"Access-Control-Allow-Methods"报头标签。

3.利用H5的postMessage 方法和 onmessage 事件解决跨域问题

  可实现多窗口之间的数据传递

4.利用H5的websocket协议,实现浏览器与服务器的全双工通信,同时允许跨域通讯。

服务器代理

5.node作为中间件代理

6.或者Nginx作为反向代理

iframe配合的三种形式

7.iframe+document.domain

  该方法只适合子域相同,主域不同的情况,在两个页面都设置document.domain='子域',其中一个页面嵌套另一个页面,就可以进行窗口之间的通信了。

8.iframe+location.hash

  通过改变location.hash的值,并不会导致页面刷新,来传递值

9.iframe+window.name

28、常见Http请求头

 HTTP请求头提供了关于请求,响应或者其他的发送实体的信息。HTTP的头信息包括通用头、请求头、响应头和实体头四个部分。每个头域由一个域名,冒号(:)和域值三部分组成。

来解释一下这四部分是什么意思吧

        通用头标:即可用于请求,也可用于响应,是作为一个整体而不是特定资源与事务相关联。

        请求头标:允许客户端传递关于自身的信息和希望的响应形式。

        响应头标:服务器和于传递自身信息的响应。

        实体头标:定义被传送资源的信息。即可用于请求,也可用于响应。

根据上面的分类我们可以把他们分为:Request和Response两部分。

我们平时开发过程中并不会用到所有的,但是我们都应该了解一些,如果你想深入学习的话,来!我们开始:

HTTP Request Header 请求头

Accept:指定客户端能够接收的内容类型。

Accept-Charset:浏览器可以接受的字符编码集。

Accept-Encoding:指定浏览器可以支持的web服务器返回内容压缩编码类型。

Accept-Language:浏览器可接受的语言。

Accept-Ranges:可以请求网页实体的一个或者多个子范围字段。

AuthorizationHTTP:授权的授权证书。

Cache-Control:指定请求和响应遵循的缓存机制。

Connection:表示是否需要持久连接。(HTTP 1.1默认进行持久连接)

CookieHTTP:请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器。

Content-Length:请求的内容长度。

Content-Type:请求的与实体对应的MIME信息。

Date:请求发送的日期和时间。

Expect:请求的特定的服务器行为。

From:发出请求的用户的Email。

Host:指定请求的服务器的域名和端口号。

If-Match:只有请求内容与实体相匹配才有效。

If-Modified-Since:如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码。

If-None-Match:如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变。

If-Range:如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。

If-Unmodified-Since:只在实体在指定时间之后未被修改才请求成功。

Max-Forwards:限制信息通过代理和网关传送的时间。

Pragma:用来包含实现特定的指令。

Proxy-Authorization:连接到代理的授权证书。

Range:只请求实体的一部分,指定范围。

Referer:先前网页的地址,当前请求网页紧随其后,即来路。

TE:客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息。

Upgrade:向服务器指定某种传输协议以便服务器进行转换(如果支持。

User-AgentUser-Agent:的内容包含发出请求的用户信息。

Via:通知中间网关或代理服务器地址,通信协议。

Warning:关于消息实体的警告信息

HTTP Responses Header 响应头

Accept-Ranges:表明服务器是否支持指定范围请求及哪种类型的分段请求。

Age:从原始服务器到代理缓存形成的估算时间(以秒计,非负)。

Allow:对某网络资源的有效的请求行为,不允许则返回405。

Cache-Control:告诉所有的缓存机制是否可以缓存及哪种类型。

Content-Encodingweb:服务器支持的返回内容压缩编码类型。。

Content-Language:响应体的语言。

Content-Length:响应体的长度。

Content-Location:请求资源可替代的备用的另一地址。

Content-MD5:返回资源的MD5校验值。

Content-Range:在整个返回体中本部分的字节位置。

Content-Type:返回内容的MIME类型。

Date:原始服务器消息发出的时间。

ETag:请求变量的实体标签的当前值。

Expires:响应过期的日期和时间。

Last-Modified:请求资源的最后修改时间。

Location:用来重定向接收方到非请求URL的位置来完成请求或标识新的资源。

Pragma:包括实现特定的指令,它可应用到响应链上的任何接收方。

Proxy-Authenticate:它指出认证方案和可应用到代理的该URL上的参数。

refresh:应用于重定向或一个新的资源被创造,在5秒之后重定向(由网景提出,被大部分浏览器支持)

Retry-After:如果实体暂时不可取,通知客户端在指定时间之后再次尝试。

Serverweb:服务器软件名称。

Set-Cookie:设置Http Cookie。

Trailer:指出头域在分块传输编码的尾部存在。

Transfer-Encoding:文件传输编码。

Vary:告诉下游代理是使用缓存响应还是从原始服务器请求。

Via:告知代理客户端响应是通过哪里发送的。

Warning:警告实体可能存在的问题。

WWW-Authenticate:表明客户端请求实体应该使用的授权方案。

29、seo

30、es6语法糖

31、介绍路由的history

32.什么是vue生命周期?有什么作用?
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做 生命周期钩子 的函数,这给了用户在不同阶段添加自己的代码的机会。(ps:生命周期钩子就是生命周期函数)例如,如果要通过某些插件操作DOM节点,如想在页面渲染完后弹出广告窗, 那我们最早可在mounted 中进行

2.vue生命周期的8个阶段?
beforeCreate:在new一个vue实例后,只有一些默认的生命周期钩子和默认事件,其他的东西都还没创建。在beforeCreate生命周期执行的时候,data和methods中的数据都还没有初始化。不能在这个阶段使用data中的数据和methods中的方法
create:data 和 methods都已经被初始化好了,如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段中操作
beforeMount:执行到这个钩子的时候,在内存中已经编译好了模板了,但是还没有挂载到页面中,此时,页面还是旧的
mounted:执行到这个钩子的时候,就表示Vue实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。 如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行
beforeUpdate: 当执行这个钩子时,页面中的显示的数据还是旧的,data中的数据是更新后的, 页面还没有和最新的数据保持同步
updated:页面显示的数据和data中的数据已经保持同步了,都是最新的
beforeDestory:Vue实例从运行阶段进入到了销毁阶段,这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于可用状态。还没有真正被销毁
destroyed: 这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于不可用状态。组件已经被销毁了。
3.第一次加载页面会触发哪些钩子?
beforeCreate, created, beforeMount, mounted

33、created、mounted的区别

created在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成识图。
mounted在模板渲染成html后调用,通常初始化页面完成后,再对html的dom节点进行一些需要的操作

通常created使用的次数多,而mounted通常是在一些插件的使用或者组件的使用中进行操作,比如插件chart.js的使用。如果写入created中,你会发现无法对chart进行一些初始化配置,一定要等到html渲染完成才可以进行,这时候就需要用到mounted。

34、 vue获取数据在哪个周期函数
答:一般 created / beforeMount / mounted 皆可。
比如如果你要操作 DOM ,那肯定是 mounted 时候才能操作。
35. 请详细说下你对vue生命周期的理解?
答:总共分为8个阶段 :创建前/后,载入前/后,更新前/后,销毁前/后。
创建前/后(beforeCreate / created):在beforeCreated阶段,vue实例的挂载元素 $el和 数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,$el还没有。
载入前/后(beforeMount / mounted):在beforeMount阶段,vue实例的 $el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message 还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
更新前/后(beforeUpdate / updated):当data变化时,会触发beforeUpdate和updated方法。
销毁前/后():在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。

36、

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
创建库需要考虑以下几个方面: 1. 目类型:需要确定目类型,例如选择、填空、判断、简答等。 2. 难度等级:需要为每一道目确定难度等级,例如易、中、难等级。 3. 目内容:根据目类型和难度等级,编写相应的目内容。对于选择,需要提供多个选项和正确答案;对于填空,需要留出相应的空格并提供正确答案;对于简答,需要提供明确的问和参考答案。 4. 目数量:需要确定目的数量,确保库中包含足够数量的目,以便于随机生成。 可以使用Python中的数据结构来存储库,例如使用字典来存储每一道目的类型、难度等级、目内容和正确答案等。以下是一个简单的Python代码示例,用于创建一个包含10道加法库: ```python import random # 创建库 question_bank = [] # 随机生成10道加法 for i in range(10): # 随机生成两个数 num1 = random.randint(1, 9) num2 = random.randint(1, 9) # 生成目 question = '{} + {} = '.format(num1, num2) # 计算正确答案 answer = num1 + num2 # 添加目到库 question_bank.append({ 'type': '选择', 'difficulty': '易', 'question': question, 'options': [answer, answer+1, answer+2, answer+3], 'answer': answer }) # 输出库 for question in question_bank: print('类型:{}'.format(question['type'])) print('难度:{}'.format(question['difficulty'])) print('目:{}'.format(question['question'])) print('选项:{}'.format(question['options'])) print('答案:{}'.format(question['answer'])) print('------------------------') ``` 以上代码中,我们使用了Python中的列表来存储库中的所有目,并使用for循环随机生成10道加法。对于每一道目,我们都使用一个字典来存储其类型、难度等级、目内容、选项和正确答案等信息。最后,我们通过for循环输出库中的所有目。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值