闭包:
function fn7() {
var name2 = "小白"; // name 是一个被 fn7 创建的局部变量
function displayName() { // displayName() 是内部函数,一个闭包
console.log(name2); // 使用了父函数中声明的变量
}
displayName();//立即执行
}
fn7(); //正常打印
function fn8() {
var name3 = "小贱";
function displayName() {
console.log(name3);
}
return displayName;
}
var myFn8 = fn8();//这一步函数displayName还没有运行,被转换为表达式放到全局中。
myFn8();
第二个全局console.log(name3);->可以获取到,主要目的还是想用函数局部变量;全局只能读取不能修改
防抖节流
防抖:### 防抖是将多次执行变为最后一次执行,在n秒后执行,小于n秒不执行。(例如搜索,抖动就兜着它,啥时静止啥时执行,例如:输入框输入,)
节流:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。(避免短时间内高频触发)比如事件监听(懒加载)
防抖只执行最后一次(会清除定时器);节流是按规定时间来(不会清除定时器);
节流:
function throttle(fn,wait){
var timer = null;
return function(){
var context = this;
var args = arguments;
if(!timer){
timer = setTimeout(function(){
fn.apply(context,args);
timer = null;
},wait)
}
}
}
function handle(){
console.log(Math.random());
}
window.addEventListener("mousemove",throttle(handle,1000));
Promise对象
三个状态:pendding状态、resolved状态、rejected状态
var promise = new Promise((resolved,rejected)=>{
resolve(res);
reject(err);
})
promise.then(res=>{}).catch()
promise.all([])->只有所有的Promise都成功才成功,只有一个失败就失败(顺序的)
promise.race([])->谁先完成就输出谁(无序的)
promise.any() ->全部失败才返回失败
new Promise一旦建立理解执行,先执行宏任务->微任务(同时还需要promise内部的状态发生变化,才会执行,不变化不执行)
同步任务 、 异步任务(宏任务(setTimeout)优先 微任务(Promise))
Vue双向绑定原理
// 可以直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。
Object.defineProperty(obj, prop, descriptor)
obj: 需要操作的对象
prop: 需要操作的属性
descriptor: 属性描述符
let obj = {name: 'meihao'};
// 给obj对象动态新增一个name属性, 并且name属性的取值必须是test
Object.defineProperty(obj, 'name', {
// 可以通过value来告诉defineProperty方法新增的属性的取值是什么
value: 'test',
// 默认情况下通过defineProperty新增的属性的取值是不能修改的
// 如果想修改, 那么就必须显示的告诉defineProperty方法
writable: true,
// 默认情况下通过defineProperty新增的属性是不能删除的
// 如果想删除, 那么就必须显示的告诉defineProperty方法
configurable: true,
// 默认情况下通过defineProperty新增的属性是不能迭代的
// 如果想迭代遍历, 那么就必须显示的告诉defineProperty方法
enumerable: true
});
obj.name = 'test02'; // 修改
delete obj.name // 删除
for(let key in obj){ // 遍历
console.log(key, obj[key]);
}
get/set方法
**defineProperty除了可以动态修改/新增对象的属性以外,还可以在修改/新增的时候给该属性添加get/set**方法。
通过**defineProperty给某个属性添加了get/set方法,那么以后只要获取这个属性的值就会自动调用get, 设置这个属性的值就会自动调用set**。
如果设置了**get/set方法, 那么就不能通过value直接赋值, 也不能编写writable:true**。
let obj = {};
let oldValue = 'mei';
Object.defineProperty(obj, 'name', {
// value: oldValue,
// writable: true,
configurable: true,
enumerable: true,
get(){
console.log("get方法被执行了");
return oldValue;
},
set(newValue){
if(oldValue !== newValue){
console.log("set方法被执行了");
oldValue = newValue;
}
}
});
console.log(obj.name);
/*
get方法被执行了
mei
*/
obj.name = 'test';
/*
set方法被执行了
*/
监听对象值变化
class Observer {
// 只要将需要监听的那个对象传递给Observer这个类
// 这个类就可以快速的给传入的对象的所有属性都添加get/set方法
constructor(data) {
this.observer(data);
}
observer(obj){
if(obj && typeof obj === 'object') {
// 遍历取出传入对象的所有属性, 给遍历到的属性都增加get/set方法
for(let key in obj){
this.defineRecative(obj, key, obj[key])
}
}
}
defineRecative(obj, attr, value) {
this.observer(value); // 如果属性的取值又是一个对象, 那么也需要给这个对象的所有属性添加get/set方法
Object.defineProperty(obj, attr, {
get(){
return value;
},
set:(newValue)=>{
if(value !== newValue) {
// 如果给属性赋值的新值又是一个对象, 那么也需要给这个对象的所有属性添加get/set方法
this.observer(newValue);
value = newValue;
console.log('监听到数据的变化');
}
}
})
}
}
// 测试
let obj = { name: 'zs' };
new Observer(obj);
obj.name = {a: 'abc'}; // 监听到数据的变化
obj.name.a = 'test'; // 监听到数据的变化
移动端字体的缩放
<script>
(function() {
//在窗口各宽情况时,动态计算出html的font-size
var PAGE_MAX_WIDTH = 750, BASE_FONT_SIZE = 40;
var DOC_ROOT_STYLE = document.documentElement.style;
var timer = null;
window.addEventListener('load', resizeFontSize);
window.addEventListener('resize', function() {
clearTimeout(timer);
timer = setTimeout(resizeFontSize, 100);
});
timer = setTimeout(resizeFontSize, 300);
resizeFontSize();
function resizeFontSize() {
DOC_ROOT_STYLE.fontSize = Math.min( (document.documentElement.clientWidth) / PAGE_MAX_WIDTH * BASE_FONT_SIZE, BASE_FONT_SIZE) + 'px';
}
})();
</script>
或者
1 将设计图片等分为指定份数,求出每一份的大小
例如: 750设计图片分为7.5份, 那么每一份的大小就是100px
2 将目标屏幕也等分为指定份数,求出每一份的大小
例如: 375屏幕也分为7.5份, 那么每一份的大小就是50px
3 用原始元素尺寸 / 原始图片每一份大小 * 目标屏幕每一份大小 = 等比缩放后的尺寸
例如: 设计图片上有一个150*150的图片, 我想等比缩放显示到375屏幕上
那么: 150 / 100 * 50 = 1.5*50 = 75px(原理)
通过 <meta name="viewport">的initial-scale属性来缩小
注意点: 缩放视口后视口大小会发生变化
let scale = 1.0 / window.devicePixelRatio;
let text = `<meta name="viewport" content="width=device-width, initial-scale=${scale}, maximum-scale=${scale}, minimum-scale=${scale}, user-scalable=no">`;
document.write(text); // 这个设置像素比
document.documentElement.style.fontSize = window.innerWidth / 7.5 + "px"; // 这个解决适配
JS数据类型
基本数据类型:Number、String、Boolean、Null、undefined、Symbol(命名冲突的用不相等)、bigInt(谷歌提出的,安全存储,操作大整数)
引用数据类型:Object(包含了function、array、date)
如何检测数组
(arr instanceof Array)、
(arr.constructor == Array)、
(Array.isArray(arr))、
(Object.getPrototypeOf(arr) == Array.prototype)、
(Array.prototype.isPrototypeOf(arr))、
(Object.prototype.toString.cal(arr) == '[object Array]')
v-for 能级优先于 v-if
同时出现,每次循环都要执行一遍v-if ,浪费性能
给v-for外面套一层div,来绑定 v-if命令
jsonp只能支持GET请求
cookie
cookie:4kb sessionStory和localStory 5M
sessionStory 只有在同一浏览器的同一窗口下才能够共享,window.open打开新标签窗口,数据复制一份,互不影响。(同一窗口)
做免登录(cookie最好)
原型
①__proto__和constructor属性是对象所独有的;
② prototype属性是函数所独有的(箭头函数没有prototype属性)。但是由于JS中函数也是一种对象,所以函数也拥有__proto__和constructor属性
*每个函数都有:prototype属性(指向原型对象)和constructor属性(指向谁创建出它的对象)
*每个函数的原型也有一个constructor属性,指向这个函数
一个对象.__proto__.__proto__.__proto__--------->直到找到最顶层的原型.__proto__为null,还没找到属性的话,就为undefined
token过期处理
响应的状态码为401 error.response.status === 401
一种是跳到登录页重新登录
第二种是用刷新token的接口完成刷新。
ES6
ES6函数、Class类、Map()、const/let、箭头函数、模板字符串
箭头函数无this,他的this值得是离他最近作用域的this指向。
Class本质上是function,同样可以看成一个块,只能写在调用者前面;new 生成实例化对象时,自动会调用该构造函数。constructor 里的this指向new Person()生成的新对象,并且给新对象添加属性
class B extends{} 子类没有this,必须执行super()继承父类的this---->即调用了父类的constructor函数
深拷贝、浅拷贝、去重
深拷贝:复制出来两个毫无关联 var newArr = [...oldArr](伪深拷贝,只能一维数组) JSON.stringfy();
手动实现深拷贝就要使用递归=>
// 标准的深拷贝
function deepClone(source){
// 判断复制的目标是数组还是对象
const targetObj = source.constructor === Array ? [] : {};
// 遍历目标
for(let keys in source){
if(source.hasOwnProperty(keys)){
// 如果值是对象,就递归一下
if(source[keys] && typeof source[keys] === 'object'){
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{
// 如果不是,就直接赋值 (基本类型)
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
浅拷贝:重新定义变量,重新赋值;slice();Obj.assign();
new 关键字做了什么
1. 创建一个空的对象
2. 链接到原型
3. 绑定this指向,执行构造函数
4. 确保返回的是对象
用代码解释一下:
let obj = new Object();
obj.__proto__= Person.prototype; // 设置原型链
let result = Person.call(obj); // 让Person的this指向obj,并执行Person函数体(执行了类的constructor函数)
// 判断Person的返回值类型:
// 如果是值类型,返回obj。如果是引用类型,就返回这个引用类型的对象
if (typeof(result) == "object"){
person1 = result;
}
else{
person1 = obj;;
}
Vue2中无法监听数组和对象的某些变化问题
Object.defineProperty(obj, key, value)这种方式监听数据的
实际上人家可以监听得到,只是vue2出于性能原因,数组和对象不确定性,会递归得去循环vue得每一个属性,要写很多try catch,将这些特性修改掉了,弃用了。
但调用数组的pop、push、shift、unshift、splice、sort、reverse等原型方法时是可以监听到数组的变化的。
***数组不能监听的情况
(1) 直接通过下标赋值 arr[i] = value
(2) 直接修改数组长度 arr.length = newLen
代替做法:
(a)修改值
Vue.set(arr, index, newvalue)
vm.$set(arr, index, newvalue)
arr.splice(index, 1, newvalue)
(b) 修改数组长度
arr.splice(newLen)
***对象不能监听的情况
属性的新增和删除
obj.newkey=newvalue
delete obj.key
代替做法:
// 新增
Vue.set(obj, newkey, newvalue)
vm.$set(obj, newkey, newvalue)
obj = Object.assign({}, obj, {newkey1: newvalue1, newkey2: newvalue2})
// 删除
Vue.delete(obj, key)
vm.$delete(obj, key)
Vue3是通过proxy直接代理整个对象来实现的,而不是像Object.defineProperty针对每个属性(所以就会涉及递归)
所以,只需做一层代理就可以监听同级结构下的所有属性变化,包括新增属性和删除属性
Vue3的proxy---------------------------->
let handler = {
get (target,key) { //target就是obj key就是要取obj里面的哪个属性
console.log('收集依赖')
// return target[key]
//Reflect 反射 这个方法里面包含了很多api
return Reflect.get(target,key)
},
set (target,key,value) {
console.log('触发更新')
// target[key] = value //这种写法设置时如果不成功也不会报错 比如这个对象默认不可配置
Reflect.set(target,key,value)
}
}
let proxy = new Proxy(obj,handler)
//通过代理后的对象取值和设置值
proxy.arr //收集依赖
proxy.name.name //收集依赖(只有一个)
proxy.name = '123' //触发更新
JS递归
递归必须要由两部分组成:
1、函数里面调用自身
2、要有if终止条件,只有在某个条件成立时才允许执行递归,否则不允许调用自身。
深拷贝:
function clone(o) {
var temp = {}
for (var key in o) {
if (typeof o[key] == 'object') {
temp[key] = clone(o[key])
} else {
temp[key] = o[key]
}
}
return temp
}
组件递归:
在组件里面调用自身。
<div v-if="item.children && item.children.length" class="item-childen">
<my-tree :treeData="item.children"></my-tree>
</div>
冒泡排序(比较相邻两个大小)
// 声明一个数组
var arr = [5,8,9,3,1,122,35,2,55];
var t = 0;
for(var i = 0;i < arr.length-1;i++)
for(var j = 0;j < arr.length-i-1;j++){
if(arr[j]>arr[j+1]){
t = arr[j+1];
arr[j+1] = arr[j];
arr[j] = t;
}
}
sort() 不传参时默认为升序,且是按字符串比较的方式排序;传参时,其参数为函数,且该函数带俩参数
arr.sort()--->默认是升序,他的排序默认是ASCII码值(自动转化为字符串),解决办法如下:
在传入一个函数:
var arr = [8,90,9,16];
// 升序
console.log(arr.sort(function (a, b) {
return a - b;
})); // [8, 9, 16, 90]
// 降序
console.log(arr.sort(function (a, b) {
return b - a;
})); // [90, 16, 9, 8]
数组去重
const newArr = [...new Set(arr)];
Vue 父组件和子组件生命周期、mixins
父组件create->子组件create->子组件mounted->父组件mounted
mixins顺序:
主要分下面几种情况:
1.对于created,mounted 等生命周期函数 mixin文件中的代码先执行,组件中的后执行
2.对于data中定义的字段,组件中定义组件覆盖mixin中同名字段
3.对于 method中的同名方法,组件内的同名方法覆盖mixin中的方法
Vue3
1、setup执行时机
setup
beforeCreate
created
2、setup注意的点
setup中this为undefined
setup函数只能是同步不能是异步
3、简单响应数据ref();数组和对象用reactive;ref本质底层还是reactive,系统会自动根据做转换;ref(xx)->reactive({value:xx});
Vue在解析数据之前,会自动判断这个数据是否是ref类型的,如果是就自动添加.value,如果不是就不自动添加.value
4、setup函数定义的变量和方法最终都需要return出去,不然无法再模板中使用