笔记

3,704 阅读7分钟
闭包:

blog.csdn.net/qq_35942348…

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);->可以获取到,主要目的还是想用函数局部变量;全局只能读取不能修改
防抖节流

1675302982859.jpg

防抖:### 防抖是将多次执行变为最后一次执行,在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))

blog.csdn.net/qq_42033567…

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'; // 监听到数据的变化

www.cnblogs.com/meihao1203/…

移动端字体的缩放
<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数据类型
基本数据类型:NumberStringBooleanNullundefinedSymbol(命名冲突的用不相等)、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中thisundefined
setup函数只能是同步不能是异步
3、简单响应数据ref();数组和对象用reactive;ref本质底层还是reactive,系统会自动根据做转换;ref(xx)->reactive({value:xx});
   Vue在解析数据之前,会自动判断这个数据是否是ref类型的,如果是就自动添加.value,如果不是就不自动添加.value
4、setup函数定义的变量和方法最终都需要return出去,不然无法再模板中使用