1、闭包是什么?利弊?如何解决弊端?
闭包是什么:
- JS中内层函数可以访问外层函数的变量,外层函数无法操作内存函数的变量的特性。我们把这个特性称作闭包。
闭包的好处:
- 隔离作用域,保护私有变量;有了闭包才有局部变量,要不然都是全局变量了。 让我们可以使用回调,操作其他函数内部; 变量长期驻扎在内存中,不会被内存回收机制回收,即延长变量的生命周期;
闭包的弊端:
- 内层函数引用外层函数变量,内层函数占用内存。如果不释放内存,过多时,易引起内存泄露。
解决办法:
- 无法自动销户,就及时手动回收,使用后将函数的引用赋null。
2、深度拷贝
1、深拷贝与浅拷贝的区别?
-
拷贝的层级不同,深拷贝是指每一层数据的改动都不会影响原对象和新对象,浅拷贝只有第一层的属性变动不互相影响,深层的数据变动还会互相影响。
-
浅拷贝:Object.assign
-
深拷贝:JSON.stringify和JSON.parse
3、浏览器存储,他们的区别?
-
localStorage:永久保存,除非主动删除数据,否则数据永远不会消失,存储空间5M
-
sessionStorage:会话存储,只要这个浏览器窗口没有关闭,即使刷新页面或者进入同源另一个页面,数据依然存在,关闭页签/浏览器时清空
-
cookie:设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭,存放数据大小为4K左右, 有个数限制(各浏览器不同),一般不能超过20个,缺点是不能储存大数据且不易读取
强缓存是指浏览器在第一次请求资源时,会将资源的响应头中的缓存控制字段(如Expires、Cache-Control)记录下来,然后在下次请求资源时,会先检查本地缓存,如果本地有缓存并且缓存未过期,就直接使用本地缓存,不再向服务器发送请求。
协商缓存是指浏览器在第一次请求资源时,会将资源的响应头中的缓存控制字段(如Last-Modified、ETag)记录下来,然后在下次请求资源时,会向服务器发送一个条件请求,服务器会根据资源的最后修改时间或者唯一标识来判断资源是否有更新,如果资源未更新,服务器会返回304 Not Modified状态码,告诉浏览器直接使用本地缓存。
同源策略(Same-Origin Policy)是一种浏览器安全策略,用于限制一个网页文档或脚本如何与来自不同源的资源进行交互。同源策略要求网页文档或脚本只能与同一来源的资源进行交互,而不能与其他来源的资源进行直接交互。同源策略的限制包括:
- 限制不同源的网页文档或脚本之间的互相访问。
- 限制不同源的网页文档或脚本对共享的浏览器资源(如Cookie、localStorage等)的访问。
- 限制不同源的网页文档或脚本对共享的网络请求(如AJAX请求)的访问。
浏览器的垃圾回收机制通常包括以下几个主要的部分:
- 标记-清除算法(Mark-and-Sweep):这是最常见的垃圾回收算法之一。它通过在内存中标记所有活动对象,然后清除未被标记的对象来进行垃圾回收。这个过程会在程序执行时定期进行,通常在空闲时间进行,以避免对程序性能造成影响。
- 引用计数:这是另一种常见的垃圾回收算法。它通过跟踪每个对象的引用次数,当引用次数为零时,就会将该对象进行清除。然而,引用计数算法可能会存在循环引用的问题,导致无法正确回收内存。
- 分代回收:现代浏览器通常采用分代回收的策略,将内存对象分为新生代和老生代两个部分,针对不同生命周期的对象采用不同的回收策略,以提高回收效率。
浏览器的垃圾回收机制是一种自动管理内存的机制,用于检测和清理不再被程序所引用的内存,以避免内存泄漏和提高内存利用率。
### 4、如何改变this指向?区别?
- call、apply与bind区别:前两个可以自动执行,bind不会自动执行,需要手动调用
- call、bind与apply区别:前两个都有无数个参数,apply只有两个参数,而且第二个参数为数组
- 箭头函数没有自己的this,它的this是继承而来,默认指向在定义它时所处的对象(宿主对象)
### 5、如何区分数据类型?
let a = [1,2]
Object.prototype.toString.call(a) // '[object Array]'
6、防抖/节流的区别?
- 区别:防抖只会在最后一次事件后执行触发函数,节流不管事件多么的频繁,都会保证在规定时间段内触发事件函数。
### 7、v-if和v-show区别?
- v-if控制Dom是否存在,v-show控制样式
8、vuex是什么?
-
vuex是一个状态管理工具,集中式的管理所有组件的状态数据
state: { \\\\this.store.state.name name: '女孩', number: 1, }, getters: { \\\\\\\\修饰器 this.store.getters.getMessage
}, mutations: { \\\\$store.commit
}, actions: { \\\\\异步操作 this.$store.dispatch
}, modules: {}
9、hash模式和history模式的区别
- hash路由再地址栏URL上有“#”,而history路由没有;
- 进行回车刷新操作时,hash路由会加载到地址栏对应的页面,而history路由一般会出现404错误
- hash模式是通过监听hashChange事件来实现的,history模式是通过pushState方法+popstate事件来实现的
10、Number的最大存储空间
- Number类型的最大值为2的53次方
11、vue2和vue3的区别
-
1:根节点不同
vue2中必须要有根标签 vue3中可以没有根标签,包裹在一个fragement虚拟标签中 -
2:生命周期的变化
创建前:beforeCreate -> 使用setup() 创建后:created -> 使用setup() 挂载前:beforeMount -> onBeforeMount 挂载后:mounted -> onMounted 更新前:beforeUpdate -> onBeforeUpdate 更新后:updated -> onUpdated 销毁前:beforeDestroy -> onBeforeUnmount 销毁后:destroyed -> onUnmounted 异常捕获:errorCaptured -> onErrorCaptured -
3:v-if和v-for的优先级
在vue2中v-for的优先级高于v-if,可以放在一起使用,但是不建议这么做,会带来性能上的浪费 在vue3中v-if的优先级高于v-for,一起使用会报错 -
4:响应式原理不同
vue2通过Object.definedProperty()的get()和set()来做数据劫持、结合和发布订阅者模式来实现,Object.definedProperty()会遍历每一个属性。 vue3通过proxy代理的方式实现。 proxy的优势:不需要像Object.definedProperty()的那样遍历每一个属性,有一定的性能提升proxy可以理解为在目标对象之前架设一层“拦截”,外界对该对象的访问都必须通过这一层拦截。这个拦截可以对外界的访问进行过滤和改写。 -
5:插槽方式不同
<slot name="person"></slot> <slot :data="data"></slot> vue2 slot="person" slot-scope="data" vue3 v-slot:person #data 具名插槽使用方式不同:vue2使用slot='',vue3使用v-slot:'' 作用域插槽使用方式不同:vue2中在父组件中使用slot-scope="data"从子组件获取数据,vue3中在父组件中使用 #data 或者 #default="{data}"获取 -
6:样式穿透
vue2中: /deep/ .类名{} ::v-deep .类名{} vue3中: :deep (.类名{}) ::v-deep(.类名{}) -
7:diff算法不同
vue2中的diff算法
遍历每一个虚拟节点,进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方。 用patch记录的消息去更新dom 缺点:比较每一个节点,而对于一些不参与更新的元素,进行比较是有点消耗性能的。 特点:特别要提一下Vue的patch是即时的,并不是打包所有修改最后一起操作DOM,也就是在vue中边记录变更新。(React则是将更新放入队列后集中处理)。
vue3中的diff算法
在初始化的时候会给每一个虚拟节点添加一个patchFlags,是一种优化的标识。 只会比较patchFlags发生变化的节点,进行识图更新。而对于patchFlags没有变化的元素作静态标记,在渲染的时候直接复用。
- 8:组合式API和选项式API
- 在vue2中
采用选项式API,将数据和函数集中起来处理,将功能点切割了当逻辑复杂的时候不利于代码阅读。 - 在vue3中
采用组合式API,将同一个功能的代码集中起来处理,使得代码更加有序,有利于代码的书写和维护。
12、微任务和宏任务的本质区别。
-
常见宏任务: setTimeout()、 setInterval()、 setImmediate()、
-
常见微任务: promise.then()、promise.catch()、 new MutaionObserver()、 process.nextTick()
-
宏任务特征:有明确的异步任务需要执行和回调;需要其他异步线程支持。
-
微任务特征:没有明确的异步任务需要执行,只有回调;不需要其他异步线程支持。
-
微任务一般比宏任务先执行,并且微任务队列只有一个,宏任务队列可能有多个
13:Dom事件流的顺序?什么是事件委托?
-
当页面上的一个元素被点击时,先从document向下一层层捕获到该元素。然后再向上冒泡,一层层触发。
-
事件委托是将事件写在父级元素上,e.target是事件捕获时那个最小的元素,即选中的元素。所以可以根据e.target操作选中的元素。这样不需要给每个子元素绑定事件,代码更加简约。
14、重绘和重排(回流/重构/重载)是什么?如何优化?
- 样式的调整会引起重绘,比如字体颜色、背景色调整等
- Dom的变动会引起重排,比如定位改动、元素宽高调整
- 避免循环插入dom,比如table的行。可以js循环生成多个dom后,一次性插入。
15、单页面应用是什么?优缺点?如何弥补缺点
- 单页面对一个入口DOM通过路由去更改内容,整个应用只有一个html页面
- 用户体验好,没有页面切换就没有白屏情况;
- 首屏加载慢,不利于SEO
- 通过压缩、路由懒加载缓解首屏慢;通过SSR 服务器端渲染解决SEO问题;
16、判断一个值是什么类型有哪些方法?
- typeof 运算符
- instanceof 运算符
- Object.prototype.toString 方法
17、你有对 Vue 项目进行哪些优化?
- (1)代码层面的优化 v-if 和 v-show 区分使用场景、 computed 和 watch 区分使用场景、 v-for 遍历必须为 item 添加 key,且避免同时使用 v-if 长列表性能优化、 事件的销毁、 图片资源懒加载、 路由懒加载、 第三方插件的按需引入、 优化无限列表性能、 服务端渲染 SSR or 预渲染、
- (2)Webpack 层面的优化 Webpack 对图片进行压缩、 减少 ES6 转为 ES5 的冗余代码、 提取公共代码、 模板预编译、 提取组件的 CSS、 优化 SourceMap、 构建结果输出分析、 Vue 项目的编译优化、
- (3)基础的 Web 技术的优化 开启 gzip 压缩、 浏览器缓存、 CDN 的使用、
18、网页从输入网址到渲染完成经历了哪些过程?
输入网址; 发送到DNS服务器,并获取域名对应的web服务器对应的ip地址; 与web服务器建立TCP连接; 浏览器向web服务器发送http请求; web服务器响应请求,并返回指定url的数据(或错误信息,或重定向的新的url地址); 浏览器下载web服务器返回的数据及解析html源文件; 生成DOM树,解析css和js,渲染页面,直至显示完成;
19、原型和原型链
- 每个对象都有
__proto__属性,但是只有函数对象且「非箭头函数」才有prototype属性。 - 每个构造函数都有一个prototype指向原型对象,原型对象有一个constructor属性指回构造函数,而实例有一个内部指针指向原型。如果原型是另一个类型的实例呢?那就意味着这个原型本身有一个内部指针指向另一个原型,相应地另一个原型也有一个指针指向另一个构造函数。这样就在实例和原型之间构造了一条原型链
- 原型链的尽头是
Object.prototype.__proto__,为null。 - 访问对象的一个属性,先在自身查找,如果没有,会访问对象的
__proto__,沿着原型链查找,一直找到Object.prototype.__proto__。 - 每个函数都有
prototype属性,会指向函数的原型对象。 - 所有函数的原型对象的
__proto__,会指向Object.prototype
20、线程与进程的区别?
- 一个程序至少有一个进程,一个进程至少有一个线程.
- 线程的划分尺度小于进程,使得多线程程序的并发性高。
- 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
- 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
- 多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
21、promise解决回调地狱问题
通过调用resolve和reject改变当前promise对象的状态。
ts相关知识点
22、画一条0.5px的直线
height: 1px;
transform: scale(0.5);
background: red;
23、画一个三角形?
width: 0;
height: 0;
border-width: 100px;
border-style: solid;
border-color: transparent #0099CC transparent transparent;
transform: rotate(90deg); /*顺时针旋转90°*/
24、js继承的几种方法? 1.原型链继承
function Parent() {
this.name = 'Parent';
}
function Child() {
this.age = 10;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child = new Child();
console.log(child.name); // 输出 'Parent'
2.构造函数继承
function Parent(name) {
this.name = name;
}
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
var child = new Child('Child', 10);
console.log(child.name); // 输出 'Child'
3.组合继承
function Parent(name) {
this.name = name;
}
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child = new Child('Child', 10);
console.log(child.name); // 输出 'Child'
4.原型式继承
function createObj(o) {
function F() {}
F.prototype = o;
return new F();
}
var parent = {
name: 'Parent'
};
var child = createObj(parent);
console.log(child.name); // 输出 'Parent'
5.寄生式继承
function createObj(o) {
var obj = Object.create(o);
obj.sayHello = function() {
console.log('Hello');
};
return obj;
}
var parent = {
name: 'Parent'
};
var child = createObj(parent);
console.log(child.name); // 输出 'Parent'
25、new代表什么
在JavaScript中,`new` 是一个关键字,用于创建一个新的对象实例。当使用 `new` 关键字来调用一个函数时,会发生以下几个步骤:
1. 创建一个空对象
2. 将这个空对象的原型指向构造函数的原型(即`Parent.prototype`)
3. 将构造函数的作用域赋给新对象(因此`this` 指向这个新对象)
4. 执行构造函数中的代码,为这个新对象添加属性和方法
5. 如果构造函数没有明确返回一个对象,则返回这个新对象
因此,`new Parent()` 创建了一个新的 `Parent` 对象实例,并将其赋值给一个变量(或者直接使用)。
26、小程序
1、生命周期
onLoad——页面加载,调一次
onShow——页面显示,每次打开页面都调用
onReady——初次渲染完成,调一次,比onload先执行
onHide——页面隐藏,当 navigateTo 或底部 tab 切换时调用
onUnload——页面卸载,当 redirectTo 或navigateBack 时调用
onPullDownRefresh——下拉刷新,请求返回后,调用 wx.stopPullDownRefresh 停止下拉刷新的状态
onReachBottom————上拉加载,page+=1
enablePullDownRefresh:true
1.1、app的生命周期
onLaunch: 初始化小程序完成时触发,且全局只触发一次;
onShow: 小程序初始化完成(启动)或从后台切换到前台显示时触发;
onHide: 小程序从前台切换到后台隐藏时触发(如切换到其他app中);
onError: 小程序发生脚本错误或者api调用失败的时候,会触发 onError 并带上错误信息;
onPageNotFound:小程序要打开的页面不存在的时候触发;
onUnhandledRejection:小程序有未处理的 Promise 拒绝的时候触发;
onThemeChange:系统切换主题的时候触发。
2.1、父传子
// 父组件.js
Page({
data: {
message: 'Hello, this is a message from parent component'
}
})
<child-component message="{{message}}"></child-component>
// 子组件.js
Component({
properties: {
message: {
type: String,
value: ''
}
},
methods: {
showMessage() {
console.log(this.properties.message);
}
}
})
<view>{{message}}</view>
父向子传值使用的是属性绑定,子组件中的properties对象进行接收父组件传递过来的值。
2.1.1、父调用子
<!-- 父组件 -->
<child-component id="child"></child-component>
<button bindtap="onShowChildClick">调用子组件方法</button>
Page({
onShowChildClick() {
// 通过组件id选择子组件
const childComponent = this.selectComponent('#child').showChildMessage();
}
})
// 子组件.js
Component({
methods: {
showChildMessage() {
console.log('This is a message from child component');
}
}
})
2.2、子传父
<!-- 子组件 -->
<button bindtap="sendDataToParent">传递数据给父组件</button>
Component({
methods: {
sendDataToParent() {
this.triggerEvent('sendData', { message: 'Hello from child component' });
}
}
})
<!-- 父组件.wxml -->
<child-component bindsendData="onReceiveDataFromChild"></child-component>
Page({
onReceiveDataFromChild(event) {
console.log(event.detail.message); // 输出:Hello from child component
}
})
2.2.1、子调用父
<!-- 子组件 -->
<button bindtap="showMessageToParent">调用父组件方法</button>
Component({
methods: {
showMessageToParent() {
this.triggerEvent('showMessage');
}
}
})
<!-- 父组件.wxml -->
<child-component bindshowMessage="showMessageFromChild"></child-component>
Page({
showMessageFromChild() {
console.log('This is a message from child component');
}
})
3、参数传值的方法 给 HTML 元素中添加 data-*属性来传递需要的值,之后通过 e.currentTarget.dataset 或 onload 的 param 参数获取
11、bindtap 和 catchtap 的区别是什么 bindtap 不会阻止冒泡事件,catchtap 阻止冒泡
13、包的操作,发布的时候是选择某个包来发吗 分包:主包添加跳转路径,分包放内容,在 app.json 配置 subpakeages 声明项目分包结构。代码 包总包大小不能超过20M,单个主包/分包大小不能超过 2M。 开启独立分包 独立于主包和其他分包运行的一种特殊分包 开发者通过在app.json的subpackages字段中对应的分包配置项中定义independent:true字段声明对应分包为独立分包。 从独立分包页面进入小程序时无需下载主包,当用户进入普通分包或主包页面的时候才会下载主包资源。 可以选择一些从公众号进入的页面,或是调用webview的页面配置到我们的独立分包。因为不用下载主包,可以很大程度上提高独立分包页面的启动速度。 13.1、打包原则 声明 subpackages 后,将按 subpackages 配置路径进行打包,subpackages 配置路径外的目录将被打包到主包中。也就是没指定分包的文件都会被打包到主包。 主包也可以有自己的 pages,即最外层的 pages 字段。 subpackage 的根目录不能是另外一个 subpackage 内的子目录。也就是不能在分包内放置另外一个另外一个分包,两者必须是平级的关系。 tabBar 页面必须在主包内。 独立分包运行时,App 并不一定被注册,因此 getApp() 也不一定可以获得 App 对象: getApp 支持 [allowDefault] 在 App 未定义时返回一个默认实现。当主包加载,App 被注册时,默认实现中定义的属性会被覆盖合并到真正的 App 中。 getApp({allowDefault: true}) 低版本兼容 在低于 6.7.2 版本的微信中运行时,独立分包视为普通分包处理,不具备独立运行的特性。
8、几种跳转,小程序内的页面跳转 wx.navigateTo——保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面(参数必 须为字符串) wx.redirectTo——关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面 wx.switchTab——跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面,路径后不能带参数 wx.navigateBack——关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取 当前的页面栈,决定需要返回几层 wx.reLaunch——关闭所有页面,打开到应用内的某个页面 通过 navigator 跳转
2、小程序的双向绑定和 vue 哪里不一样 小程序直接 this.data 的属性是不可以同步到视图的,必须调用 this.setData({}) 1px = 2rpx
4.小程序的优点 无需下载 打开速度快 开发成本低 为用户提供良好的安全保障。发布有一套严格的审查流程,不能通过审查的程序无法发布上线 服务请求快
5.小程序的缺点 依托微信,不能开发后台管理功能 大小限制不能超过 2M,不能打开超过 5 个层级的页面
6:简述小程序原理 小程序分为两个部分 webview 和 appService,webview 用来展现 UI,appService 用来处理业 务逻辑、数据及接口调用,它们在两个进程中运行,通过系统层 JSBridge 实现通信,完成 UI 渲 染、事件处理。
7、小程序和 Vue 写法的区别 循环遍历:wx:for="{{files}}" wx:for-item="row" wx:for-index="idx" wx:key="idx",vue 是 v-for=“item in list” 调用 data 模型:小程序是 this.data.unifo,vue 是 this.unifo 给模型赋值:小程序是 this.setData({unifo:1}),vue 是直接 this.unifo=1
9、如何自定义组件 先创建一个 components 文件夹,用来存放所有自定义组件的,目录结构依然是 js,wxml,json,wxss 基本配置: .json——进行自定义组件声明 { “component”: true } 使用组件: 假如在 index.wxml 中使用这个自定义的组件,首先在 index.json 中进行声明 { “usingComponents”: { “toastdemo”: “/components/toastdemo/toastdemo” } }
10、如何实现下拉刷新 先在 app.json 或 page.json 中配置 enablePullDownRefresh:true page 里用 onPullDownRefresh 函数,在下拉刷新时执行 在下拉函数执行时发起数据请求,请求返回后,调用 wx.stopPullDownRefresh 停止下拉刷新的状态
12、小程序顶部自定义导航怎么写 在 app.json 的 window 对象中定义导航的样式 navigationStyle:“custom”
14、小程序的微信支付是哪个 API,参数是哪些及怎么获取的 wx.requestPayment
15、说几个常用的 API wx.login wx.request wx.navigateTo wx.redirectTo wx.switchTab wx.naviageteBack
16、授权验证登录怎么做,用户退出后下次进入还需要再次授权吗 一次性授权:wx.login 获取到一个 code,拿这 code 去请求后台得到 openId, sessionKey, unionId。 调 wx.getUserInfo
17、小程序页面间有哪些传递数据的方法 使用全局变量实现数据传递:在 app.js 文件中定义全局变量 globalData将需要存储的信息存放在里面 使用的时候,直接使用 getApp()拿到存储的信息 使用 wx.navigateTo 和 wx.redirectTo 的时候,可以将部分数据放在 url 里,并在新页面 onLoad 的 时候初始化 wx.navigateTo({ url: '../client_sort/client_sort?cust=' + encodeURIComponent(JSON.stringify(this.data.row))+'&urlid='+this.data.id, }); 18、小程序关联微信公众号如何确定用户的唯一性 unionid 是相同且唯一的