叶秋手写面试题

68 阅读1分钟
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        /*  
            html5新特性:
                语义化标签:
                    <header>
                        定义文档的头部区域

                    <footer>
                        定义文档的尾部区域

                    <nav>
                        定义文档的导航区域

                    <section>
                        定义文档的节
                    <article>
                        定义文章
                    <aside>
                        定义页面以外的内容(侧边栏)
                    <figure>
                        定义自包含内容,图表
                    <main>
                        定义文档内容

                    webwork(开启多线程)
                    websocket(实时通信)

                    音频(audio,video:音频和视频)

                    localStorage和seessionStorage


            animation:(动画)
                1.使用@keyframes关键字定义动画过渡的效果,0%代表开始,100%代表结束
                @keyframes myAnimation {
                    0% {
                        transform: scale(0.8);
                    }
                    50% {
                        transform: scale(100%);
                    }
                    100%{
                        transform: scale(0.88);
                    }
                }
                
                2.定义动画效果animation下面的参数
                    名字    动画持续时间    执行速度(后续有)    开始时间    循环次数(infinite)           播放方向        控制动画播放的状态(runing代表播放,paused代表停止播放)
        animation: name        duration     timing-function   delay      iteration-count             direction       anmation-play-state

                timing-function:(ease:逐渐变慢;linear:匀速;ease-in: 加速;ease-out:减速;ease-in-out: 先加速后减速)
                direction:(normal:默认值向前播放;alternate:偶数向前,奇数向后播放)

                除了百分比:还可以使用from和to关键字来实现
                @keyframes mayAnimation {
                    from {

                    }
                    to {

                    }
                }

            响应式布局:
                1.百分比布局:
                    i:有父元素相对于父元素
                    ii:无父元素相对于视口
                    iii:或者继承于父元素
                
                2.rem布局:
                    rem是指相对于根元素字体大小的单位,rem只是一个相对单位(em是一个相对单位)
                    1.设置完美视口
                    2.获取当前文档宽度
                    3.设置rem值 = 当前文档宽度 * 100 / (设计稿宽度)
                    4.设置当前文档的跟标签字体宽度 = rem值 + px
                    5.设置元素宽度 = 设计元素宽度/100 rem;

                3.媒体查询:
                    @media screen and (min-widht:1200px) {
                        html {
                            font-size: 20px;
                        }
                    }   
                    @media screen and (max-width: 1200px) {
                        html {
                            font-size: 10px;
                        }
                    }
                4.vw 和 vh 
                    vw 相对于视窗的宽度,1vw等于视口宽度的1%;即视窗宽度是100vw
                    vh 相对于视窗高度,1vh等于视口高度的1%;即视窗高度是100vh
                    1.视口宽度:为x;设计稿宽度为y
                    2.目的:你要书写的是设计稿元素宽度(这个代表着视口中元素宽度)
                    3.设置设计稿元素宽度 = (X/Y)*设计稿尺寸*vw;

            CSS:
                1.垂直居中:
                    定位方法:(父元素:position:relative;子元素:postion:absolute;top:50%;left:50%;margin-top:子元素高度一半;margin-left:-子元素宽度一半 ;)
                    绝对定位:(父元素: position: relative; 子元素: position: absolute; top: 0; bottom: 0;left:0;right:0; margin: auto;)
                    transform:(父元素:position:relative;子元素:position: absolute; transform:translate(-50%, -50%);)
                    flex: (父元素: justify-content: center; align-items: center; display: flex)

                2.flex:
                父元素:(justify-content; align-items;) (可选值: flex-start;flex-end;space-between: 空隙在中间;space-around;center;baseline:基线对齐;stretch:与父元素顶线对齐)
                子元素:(align-self;flex: 可以写数字123;或者1px 2px 3px)
                轴: flex-direction: (可以为row或者column;row-reverse;column-reverse)
                换行方式:flex-wrap:(可以nowrap;wrap;wrap-reverse)
                多行排列:align-content: (可以是flex-start;flex-end;center;space-between;space-around;stretch)
                flex-basis: 设置子元素:表示在不伸缩情况下子容器原始尺寸。主轴为横向时候代表宽度,纵向时候代表高度 (可选值: 20px)
                flex-grow: 设置子元素: 表示弹性伸展比列。可选值(1;2)
                flex-shrink: 设置子元素:表示子元素弹性收缩的比列: (子元素1: flex-shrink:1;子元素2: flex-shrink:2;超出部分按1比2比列给子元素中减去)
                order: 给子元素进行排序: (数值越小排序越靠前;默认值为0;可以为负数)

                3.deep:::deep();/deep/;id属性;

                3.rem:(postcss-pxtorem;和px2rem-loader;两个webpack来处理rem适配问题)
                rem原理:1.设置完美视口,html文档宽度等于屏幕宽度
                        2.获取html文档宽度 const htmlWidth = document.documentElement.clientWidth;
                        3.设置rem值:(设计稿宽度为375px) const htmlFontSize(rem)= (htmlWidth * 100)/375; (获取设计稿每一个px对应的文档宽度)
                        4.document.documentElement.style.fontSize = htmlFontSize + 'px'; (根标签字体等于: 1rem的数值 px)
                        5.其他元素设置: 设计稿值/100 rem;

                        (理解: 完美视口下html宽度;设计稿宽度;这个两个需要形成一一对应关系;htmlFontSize这个代表的是设计稿中的1px代表的文档宽度*100;
                        但是我们写的元素肯定是按设计稿的宽度书写;但是又得对应上文档的宽度;
                        当我们书写20rem的时候,那么就是20*rem(20是设计稿宽度;rem是每一个px设计稿宽度对应的文档宽度。那么得到的结果就是设置的设计稿对应下的文档宽度)
                        )
                4.vw
                5.less
                6.eslint:
                7.三角形:
        */
       /*
            版本控制:
                git:
       */
       /*
            JS:
                浅拷贝:(Object.assign()以及下面的方法)
                    直接对单层对象拷贝(for in 遍历赋值)
                    var a1 = {
                        'id':'01',
                        'name':'aaa',
                        'sex': new Array('man','woman') //这里sex也是一个对象
                    }
                    var a2 = {};
                    for (let key in a1){
                        a2[key] = a1[key];
                    }
                    a2.sex[0]= '1111'; // 改变sex的第一个属性值
                    console.log(a1);
                    console.log(a2);
                深拷贝:(1.创建判断数据类型函数,2.创建拷贝函数(如果是数组和对象直接新建数组和对象;如果是普通类型则直接返回;3.在拷贝函数中for in 遍历 执行新生成的对象或数组 等于 拷贝函数的调用(递归)))
                    var obj1 = {
                        name: "laowang",
                        age: 18,
                        do: function () {
                            console.log("study");
                        },
                        hobby: ["唱", "跳", "rap", [1, 2, 3]]
                    };

                    function checkType(obj) {
                        return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
                    }

                    //深拷贝函数
                    function deepClone(obj) {
                        //刚进入函数需要判断 当前要拷贝的值是什么类型
                        if (checkType(obj) === 'object') {
                            //如果当前拷贝的是一个对象,则需要定一个新的对象
                            var newObj = {};
                        } else if (checkType(obj) === "array") {
                            //如果当前拷贝的是一个数组,则需要定一个新的数组
                            var newObj = [];
                        } else {
                            //如果当前拷贝的值既不是数组也不是对象,则直接返回即可
                            return obj;
                        }

                        //开始拷贝
                        // ["唱", "跳", "rap", [1, 2, 3]]

                        for (var key in obj) {
                            //在拷贝之前 需要判断当前的obj[key]是一个对象还是数组,如果是对象或数组,则需要重新拷贝 再赋值
                            newObj[key] = deepClone(obj[key]);
                        }

                        return newObj;
                    }

                    var obj2 = deepClone(obj1);

                    console.log(obj2 === obj1);
                    console.log(obj2.hobby === obj1.hobby)
                    console.log(obj2.hobby[3] === obj1.hobby[3])

                JSON.Stringfy:(函数和undefined和null会丢失;object中的RegExp、Error对象序列化后得到空对象;时间对象会变为字符串)

                闭包:(1.函数嵌套函数,闭包就是内部嵌套的函数;2.闭包就是包含被引用变量的closure对象,在嵌套的内部函数中)
                    产生闭包的情况:
                        函数嵌套;内部函数引用了外部函数的变量;调用了外部函数;
                    闭包的作用:
                        延长了局部变量的生命周期;可以在外部操作局部变量
                    闭包的生命周期:
                        产生: 只要内部函数被定义就产生了
                        死亡: 内部函数成为垃圾对象的时候 闭包结束
                    闭包的缺点:
                        函数的局部变量占用内存时间过长,容易导致内存泄露
                        解决:1.减少使用闭包,2.及时清理闭包
                    
                         function fn1() {
                            debugger;
                            var a = 1;
                            var b = 2;

                            function fn2() {
                                console.log(a);
                            }
                            return fn2; //把当前函数return出去 就可以看到closure对象
                        }
                        fn1(); 


                        function fn() {
                            var a = 1;

                            function add() {
                                a++;
                                console.log(a);
                            }
                            return add;
                        }

                        var f = fn();
                        f(); //2
                        f(); //3
                        f(); //4
                        f(); //5

                        var f2 = fn();
                        f2(); //2
                        f2(); //3
                        f(); //6

                        //以后再也不用f了,要及时释放
                        f = null; //内部函数没有被引用了 变成了垃圾对象
                    
                原型链:(原型和原型链)
                       原型:(显示原型和隐式原型)
                            显示原型:每一个函数都有一个prototype属性(prototype其实是一个指针,默认指向一个空对象;prototype指向的对象被成为当前函数的显示原型,也成为原型对象;每一个原型对象都有一个constructor属性,指向其构造函数)
                            隐式原型: 每一个函数都有一个prototype属性,即显示原型;每一个实例对象都有一个__proto__属性,即隐式原型;对象的隐式原型指向其构造函数的显示原型;
                            注意:1.当函数定义的时候,就已经有显示原型了,是一个空对象 (constructor属性除外),但是只有当前函数实例化对象可以使用
                                 2.隐式原型在创建对象的时候就自动添加了,当访问对象的属性的时候可能会沿着隐式原型查找
                                 3.只有实例化对象才可以访问构造函数的原型对象(显示原型)
                
                                 A instanceof B:(判断B是否在A的原型链上)

                       原型链:  
                            1.                
                                                prototype
                                              ---------->      
                            构造函数(Person)                构造函数原型对象(Person.prototype)     
                                              <----------
                                                 constructor
                            
                            2.                                                                                                              constructor
                                                __proto__                                     __proto__                                   --------->  Object函数(new Object())
                            实例化对象(person)  ------->  构造函数原型对象(Person.prototype)   ------->    Object原型对象(Object.prototype) 
                                                                                                                                          ---------->  null
                                                              prototype                                                                            __proto__
                            3.对象函数Object(new Object()) -------->   对象函数原型(Object.prototype)

                作用域:(作用域和执行上下文)
                      作用域: 一段代码所在的区域
                      作用域在函数声明的时候就确定了,是静态的。
                      执行上下文是函数执行的时候才确定的,是动态的

                      作用域链:(作用域链的最前端一定是当前执行环境的变量对象,末端一定都是全局变量对象)
                            1.函数在创建的时候就会创建一个包含全局变量对象的作用域链接(scope chain)
                            2.作用域链:保证对执行环境有权访问的所有变量和函数的有序访问
                            3.搜索标识符的过程是从作用域链的最前端向最末端逐级搜索过程,如果搜索不到可能会报错。
                      
                手写new:
                        
                节流:(一段时间只能触发一次;核心:需要计算上一次触发时间和当前时间差;当前时间减去你的上一个触发时间,如果小于你设置的一段时间值,则return,大于你设置的一段时间则执行执行函数;执行函数使用.call改变以下this指向)
                    //通用的节流函数(一段时间内只触发第一次)

                    //把真正的逻辑代码的事件函数提炼出来
                    function move(e) {
                        //直接写逻辑代码
                        console.log("逻辑代码");
                        console.log(this);
                        console.log(e);
                    }
                    oBox.onmousemove = throttle(move, 100);
                    function throttle(fn, time) {
                        var lastTime = 0;
                        return function () {
                            //获取当前的时间
                            var nowTime = Date.now();
                            if (nowTime - lastTime < time) {
                                return;
                            }
                            //在这个函数中 this是真正指向事件调用者Box的
                            //当间隔时间到达time的时候 才调用fn

                            fn.call(this, arguments[0])
                            //并且把当前时间变成上一次的时间
                            lastTime = nowTime;
                        }
                    }
                防抖:(防止事件重复触发: 每次重新点击都会重新计算时间,多少秒后触发;核心:就是setTimeout 延时计时器;计时器触发前要把之前没有触发的计时器清除)
                    var oIpt = document.getElementById("ipt");

                    function getData(e) {
                        console.log("ajax请求")
                        console.log(this);
                        console.log(e)
                    }

                    oIpt.oninput = debounce(getData, 1300);

                    //通用防抖函数
                    function debounce(fn, time) {
                        var timer = null;
                        return function () {
                            //每次进入函数的时候,如果上一次的计时器没有执行,则直接给清掉,重新设置新的计时器
                            clearTimeout(timer);
                            //每次进到函数中的时候 让延迟time时间 执行函数fn
                            //这个位置的this才是真正的事件触发的对象
                            var _this = this;
                            console.log(this,'this是谁');
                            var arg = arguments[0];
                            timer = setTimeout(function () {
                                fn.call(this, arg);
                            }, time)
                        }
                    }
                
                继承:(1.构造函数的实例对象指向构造函数的原型对象,当我们将子类构造函数的原型对象指向父类的实例对象的时候,我们就可以通过子类实例对象来访问父类原型对象上的方法,这就是组合继承;2.注意当我们修改构造函数的原型对象指向的时候,原型对象上的constructor属性会被自动删除,需要重新设定)
                    //构造函数+原型 组合继承

                    //父类
                    function Person(name, age) {
                        this.name = name;
                        this.age = age;
                    }
                    Person.prototype.eat = function () {
                        console.log("eat方法")
                    }

                    //子类
                    function Teacher(name, age, type, classNum) {
                        //构造函数继承
                        //调用父类,并让父类的this指向当前子类的this
                        Person.call(this, name, age);
                        this.type = type;
                        this.classNum = classNum
                    }

                    //原型链继承
                    //不能直接把Person.prototype赋值给子类,这样改变子类就会影响父类
                    //父类的实例化对象,也能访问父类的原型对象,故把父类的实例化对象赋值给子类的原型对象
                    // 当手动修改构造函数的原型对象的时候,他的contructor属性会自动被删除,那么就需要重新设置他的contructor属性(修正构造器指向);
                    Teacher.prototype = new Person();
                    //修正构造器指向
                    Teacher.prototype.constructor = Teacher;

                    Teacher.prototype.do = function () {
                        console.log("备课")
                    }
                    var teacher1 = new Teacher("haijing", 40, "html5", 0223);
                    console.log(teacher1)
                    console.log(teacher1.eat)
                    console.log(teacher1.constructor)

                事件轮询机制:(1.代码分类:同步代码,异步代码;2.执行顺序:先同步代码,后异步代码;3.事件模型:事件管理模块,回调队列)
                    1.代码分类:
                            同步代码:绑定事件、设置计时器,for循环等等
                            异步代码:事件回调函数,计时器回调函数,ajax回调函数

                    2.JS先执行同步代码,再执行异步代码

                    3.事件模型主要组成部分:1.事件管理模块,2.回调队列
                    4.事件轮询过程:
                        i:先执行同步代码,将异步代码的回调函数交给事件管理模块管理
                        ii:事件管理模块中的事件发生了,就会把回调函数交给回调队列(callback queue)
                        iii:当同步代码执行完毕后,会遍历回调队列中的函数执行。

                this指向: (call,apply,bind)
                    什么是this:
                        1.一个关键字,是一个引用变量。
                        2.函数中可以出现this(全局的this指向window)
                        3.this指向其所在函数的调用者,如果没有调用者则指向window
                        4.this的指向是在函数调用的时候确定的
                    
                    this的指向:(其实就是看函数的调用方式)
                        1.this 默认绑定(函数默认调用)
                        2.this指向调用函数上下文(函数是上下文调用的)
                        3.注意隐式丢失现象(通过一个上下文对象拿到了一个函数,但是没有调用,而是赋值给了他人)
                        4.this指向实例化对象(函数实例化调用)
                        5.强制绑定,this指向call,apply,bind修改的对象(函数是call,apply,bind调用)

                    如果判断this指向:
                        1.先看函数是否是被call,apply,bind调用
                        2.看函数是不是实例化调用
                        3.看函数是否是上下文对象调用(注意隐式丢失现象)
                        4.如果以上都不是,则直接指向window

                    console.log(this);
                    //self是window的引用
                    console.log(self);
                    console.log(window.self);

                    //默认绑定
                    function fn() {
                        console.log(this);
                    }

                    fn(); //window

                    var obj = {
                        name: "xiaowang",
                        do: function () {
                            fn(); //默认调用 this指向window

                            setTimeout(fn, 1000) //fn也是默认调用window
                        },
                        fn: fn
                    }
                    obj.do(); //
                    obj.fn(); //fn函数是obj调用的 所以fn的this指向obj

                    var f2 = obj.fn;
                    f2(); //window

                    function Person() {
                        console.log(this)
                    }
                    var p1 = new Person();
                    fn.call(p1); //fn的this指向p1

                    var x = 1;
                    var obj = {
                        f: function () {
                            console.log(this.x)
                        },
                        x: 2
                    }
                    obj.f(); //2
                    var f1 = obj.f;
                    f1(); //1

                    call:
                        在使用一个指定的this的值,和指定若干个参数的前提下,调用了某个函数
                        作用:1.调用函数 2.改变函数上下文(this指向)
                        fn.call(thisArg, arg1,arg2,arg3...);
                            thisArg(指定的this值)
                            如果fn需要参数,则依次在call的第二个函数开始传参数

                        执行的this值的可能性:
                            null,undefined:把this指向window;
                            基本类型:把this指向当前值的包装对象
                            对象类型:把this指向当前对象

                        function fn(a, b) {
                            console.log(this, a + b);
                        }

                        fn.call(undefined, 1, 2); //window 
                        fn.call(null, 1, 2); //window
                        fn.call(1, 1, 2); //Number(1)
                        fn.call("h", 1, 2); //String("1")
                        fn.call(true, 1, 2); //Boolean(true)
                        fn.call([], 1, 2); //[]
                        fn.call({}, 1, 2); //{}
                        fn.call(new Date, 1, 2); //new Date
                        fn.call(Math, 1, 2); //math
                        fn.call(/\s/gi, 1, 2); //当前正则
                        fn.call(function () {}, 1, 2); //当前函数

                    手写call:
                        //所有的函数都能使用 所以myCall应该放在Function的构造函数上
                        //参数context是指定的this指向
                        Function.prototype.myCall = function (context) {
                            //context可能有三种类型 1.null和undefined 2.基本类型值  3.对象类型
                            //首先判断null和undefined类型,如果符合,context应该是window
                            if (context === null || context === undefined) {
                                context = window;
                            }

                            //再次判断context是基本类型值的时候,context应该是这个值的包装对象
                            if (typeof context !== 'object' && typeof context !== 'undefined' && typeof context !== "function") {
                                // Object方法可以把一个基本类型变成他的包装对象
                                context = Object(context);
                            }
                            //获取用户调用fn函数应该传递的参数,这个参数是myCall函数的第二个实参之后所有的参数,使用arguments获取
                            // console.log(arguments)
                            //截取我们需要的参数
                            var argArr = Array.from(arguments).slice(1);
                            // console.log(argArr)

                            //1.在这里 this 就是 调用myCall的函数fn
                            //2.context就是指定的this指向
                            //myCall的作用是 调用fn 并且把fn的this指向context  故可以书写 context.fn();

                            //先给context扩展一个方法 这个方法就是this
                            //给context扩展的方法名可能会重名,所以用一个独一无二的值
                            var uniqKey = Date.now().toString(36);
                            context[uniqKey] = this;
                            //调用context的这个方法
                            // [1,2,3]
                            // context[uniqKey]();
                            eval("context[uniqKey](" + argArr.toString() + ")")

                            //用完context的uniqKey的属性 要删掉 否则影响原来的对象
                            delete context[uniqKey];
                        }

                        var obj = {
                            name: "laowang"
                        }

                        function fn(a, b) {
                            console.log(a + b);
                            console.log(this);
                        }

                        fn.myCall(obj, 1, 2, 3);



                        fn.myCall(undefined, 1, 2); //window 
                        fn.myCall(null, 1, 2); //window
                        fn.myCall(1, 1, 2); //Number(1)
                        fn.myCall("h", 1, 2); //String("1")
                        fn.myCall(true, 1, 2); //Boolean(true)
                        fn.myCall([], 1, 2); //[]
                        fn.myCall({}, 1, 2); //{}
                        fn.myCall(new Date, 1, 2); //new Date
                        fn.myCall(Math, 1, 2); //math
                        fn.myCall(/\s/gi, 1, 2); //当前正则
                        fn.myCall(function () {}, 1, 2); //当前函数
                    
                    apply:
                        语法与call()方法的语法几乎完全相同,唯一的区别在于,apply的第二个参数必须是一个包含多个参数的数组(或者类数组对象)
                        
                        function fn(a, b) {
                            console.log(this, a + b);
                        }

                        fn.apply(undefined, [1, 2]); //window 
                        fn.apply(null, [1, 2]); //window
                        fn.apply(1, [1, 2]); //Number(1)
                        fn.apply("h", [1, 2]); //String("1")
                        fn.apply(true, [1, 2]); //Boolean(true)
                        fn.apply([], [1, 2]); //[]
                        fn.apply({}, [1, 2]); //{}
                        fn.apply(new Date, [1, 2]); //new Date
                        fn.apply(Math, [1, 2]); //math
                        fn.apply(/\s/gi, [1, 2]); //当前正则
                        fn.apply(function () {}, [1, 2]); //当前函数
                        var arr = [2, 3, 4];

                        function fn1(a, b, c) {
                            console.log(a + b + c)
                        }
                        console.log(arr.toString())

                        eval("fn1(" + arr.toString() + ")")

                    bind:
                        bind语法和call一样
                        但是bind并不会去调用函数,而是改变函数的this执行,并返回改变this指向后的函数
                        bind会返回一个函数,返回的是改变了this指向的fn

                        function fn(a, b) {
                            console.log(this, a + b);
                        }
                        var re = fn.bind([], 1, 2);
                        console.log(re);
                        console.log(re === fn) //false
                        re();

                        var obj = {
                            name: "laowang",
                            do: function () {
                                // console.log(this) //obj
                                setTimeout((function () {
                                    console.log(this);
                                }).bind(this), 1000)
                            }
                        }
                        obj.do();

                        fn.bind(undefined, 1, 2)(); //window 
                        fn.bind(null, 1, 2)(); //window
                        fn.bind(1, 1, 2)(); //Number(1)
                        fn.bind("h", 1, 2)(); //String("1")
                        fn.bind(true, 1, 2)(); //Boolean(true)
                        fn.bind([], 1, 2)(); //[]
                        fn.bind({}, 1, 2)(); //{}
                        fn.bind(new Date, 1, 2)(); //new Date
                        fn.bind(Math, 1, 2)(); //math
                        fn.bind(/\s/gi, 1, 2)(); //当前正则
                        fn.bind(function () {}, 1, 2)(); //当前函数
                    
                        
                冒泡和捕获:(冒泡:从父子元素中目标元素向外触发事件到最外层元素(document);捕获:从父子元素最外层(document)向内触发到目标元素)
                        1.冒泡和捕获只能使用DOM2绑定事件,(addEventListener('事件名称',()=>{}, true))
                        2.第三个参数true为事件捕获,默认false为事件冒泡

                cookie和sessionStorage和localStorage:
                        1.生命周期:
                            cookie:可设置失效时间,没有设置的话,默认是关闭浏览器后
                            localStorage: 永久保存,除非手动清除。
                            sessionStorage: 仅仅在当前网页绘话下,关闭浏览器或浏览器后被清除。
                        2.存储数据大小:
                            cookie:4kb左右
                            localStorage和sessionStorage:可以保存5Mb
                        3.http请求:
                            cookie:每次都会携带在http请求头中,如果使用cookie保存过多的数据会带来性能问题
                            localStorage和seessionStorage:仅仅在客户端保存,不参与服务器通信。
                        4.易用性:
                            cookie:需要自己封装,原生的cookie接口不友好
                            localStorage和sessionStorage:原生接口可以接受,可以再次封装对Object和Array更好支持。
                        
                indexDB(数据库)

                js前端缓存:(web缓存:浏览器缓存和http缓存(核心:强制缓存和协商缓存))
                        1.注意,我们的缓存主要是针对html,css,img等静态资源,常规情况下,我们不会去缓存一些动态资源,因为缓存动态资源的话,数据的实时性就不会不太好,所以我们一般都只会去缓存一些不太容易被改变的静态资源
                        2.减少不必要的网络传输,节约宽带(就是省钱)
                          更快的加载页面(就是加速)
                          减少服务器负载,避免服务器过载的情况出现。(就是减载)
                        3.强制缓存:(如果浏览器判断请求的目标资源有效命中强缓存,则可以直接从内存中读取目标资源,无需与服务器做任何通讯)
                            i:通常使用响应头中Expires字段(设置一个强缓存时间)去实现强缓存。(Expires已经废弃,而使用cache-control)
                            ii:cache-control:的六个属性
                                max-age决定客户端资源被缓存多久。
                                s-maxage决定代理服务器缓存的时长。
                                no-cache表示是强制进行协商缓存。
                                no-store是表示禁止任何缓存策略。
                                public表示资源即可以被浏览器缓存也可以被代理服务器缓存。
                                private表示资源只能被浏览器缓存。
                        4.协商缓存:
                            1.首先需要在服务器读取文件修改的时间
                            2.将读取出来的修改时间赋值给响应头的last-modified字段。
                            3.最后设置Cache-control:no-cache
                                当客户端读取到last-modified的时候,会在下次的请求标头中携带一个字段:If-Modified-Since
                                那么之后每次对该资源的请求,都会带上If-Modified-Since这个字段,而务端就需要拿到这个时间并再次读取该资源的修改时间,让他们两个做一个比对来决定是读取缓存还是返回新的资源。
                                1.读取返回给客户端的修改文件时间
                                2.读取第一次返回给客户端的修改时间
                                3.两个时间相同,没改过;
                                4.设置最后修改时间字段last-modified字段。

                http请求到页面显示发生了什么?(http协议,https协议)
                    1.当用户通过客户端输入url地址,首先要通过dns解析把域名解析为ip,通过ip才能找到对应的服务器
                    2.dns解析是从浏览器中找url对应的ip,如果浏览器没有缓存,那么就会到dns服务器上寻找域名对应的ip
                        //会先对//static.360buyimg.com这个域名解析dns解析,解析结果保存到浏览器缓存中
                        <link  rel="dns-prefetch"  href="//static.360buyimg.com" >
                    3.网络协议ip协议几乎是最底层协议,ip协议的基础上有tcp协议,通过dns找到ip后,才可以发送tcp协议,tcp协议是确保网络链接的协议
                    4.TCP协议的三次握手和四次挥手
                        三次握手:
                            i:客户端通过ip找到服务器
                            ii:服务器回复客户端
                            iii:客户端携带数据再次访问服务器

                        四次挥手:
                            i:客户端向服务器发送请求告诉服务器我要断开了
                            ii:服务器接收到信息后,回复我知道了
                            iii:等服务器处理完数据后,告诉客户端
                            iiii:客户端收到服务端发送过来的请求消息后,向服务器表示ok,断开
                        
                    5.http链接服务器并返回响应(可能是html文件)
                    6.解析dom
                        1.将html解析为dom树
                        2.将css解析成css树
                        3.将dom树和css树形成渲染树,开始渲染页面
                        5.js执行(js执行可以是异步的,js会阻塞浏览器解析;js执行可以修改dom树结构,可以修改css规则)
                        6.调用系统api进行绘制,页面就出来了。

                跨域: jsonp;cores(允许所有请求跨域);proxy代理()
                    1.同源策略:(浏览器的安全机制:协议(http://);域名:www.baidu.com;端口号:8080;)三个都相同代表同一个ip
                    2.允许跨域的三个标签(script;link;img)
                    3.跨域解决方案:
                        jsonp没有跨域限制
                        cors后端解决跨域问题(Access-Control-Allow-Origin)可以设置所有跨域请求
                        proxy代理:
                            module.exports = {
                              devServer: {
                                port: 8000,
                                proxy: {
                                  "/api": {
                                    target: "http://localhost:8080"
                                  }
                                }
                              }
                            };
                        

                es6:(箭头函数;promise;map;set;数组方法,...运算符;字符串方法;解构赋值;class(自定义事件机制))
                    1.箭头函数:
                            i:只能用赋值式写法,不能用声明式写法
                            ii:参数只有一个可以不加括号
                            iii:函数体只有一句话,可以不加花括号
                            iiii:可以不写return
                            iiiii:箭头函数没有this,他的this只想他的外层函数,如果没有指向window(window.setTimeout,window.setInterval的this都指向window)
                    
                    2.箭头函数和普通函数的区别:
                            i:外形区别:箭头函数使用箭头定义,普通函数没有箭头
                            ii:箭头函数都是匿名函数,普通函数可以具名函数也可以匿名函数
                                匿名: let func = () =>{}
                                具名: function fn() {}
                            iii:箭头函数不能用于构造函数,不能new调用(构造函数可以通过new调用创建实例化对象)
                            iiii:箭头函数this指向他的外层普通函数或者window,普通函数this指向调用者
                            iiiii:箭头函数不可以绑定arguments,取而代之的是rest(...运算符)解决
                            iiiiii:箭头函数没有prototype原型对象。
                    2.数据类型(map,set)

                        set:
                            i:无序,没有重复的类似数组
                            ii:Set是一个构造函数,来生成set数据结构
                            iii:for of 可以用来遍历Set类数组
                            iiii: [...new Set(array)] 浅拷贝数组

                            操作方法:
                                操作方法:
                                    add(value):向集合添加一个新的项
                                    delete(value):从集合中移除一个值
                                    has(value):如果值在集合中存在,返回true,否则false
                                    clear(): 移除集合里所有的项

                                遍历方法:
                                    keys():返回一个包含集合中所有键的数组
                                    values():返回一个包含集合中所有值的数组
                                    entries:返回一个包含集合中所有键值对的数组(感觉没什么用就不实现了)
                                    forEach():用于对集合成员执行某种操作,没有返回值
                        map:
                            属性:
                                size:返回字典所包含的元素个数
                            操作方法:
                                set(key, val): 向字典中添加新元素
                                get(key):通过键值查找特定的数值并返回
                                has(key):如果键存在字典中返回true,否则false
                                delete(key): 通过键值从字典中移除对应的数据
                                clear():将这个字典中的所有元素删除
                            遍历方法:
                                keys():将字典中包含的所有键名以数组形式返回
                                values():将字典中包含的所有数值以数组形式返回
                                forEach():遍历字典的所有成员
                ES6(字符串方法)
                        1.charAt:返回在指定位置的字符
                            var str = "HELLO WORLD";
                            var n = str.charAt(2)   //L
                        2.concat:连接两个或者更多字符串,并返回新的字符串
                            var str1 = "Hello ";
                            var str2 = "world!";
                            var n = str1.concat(str2); // Hello world!
                        3.endsWith:判断当前字符串是否以指定的字符串结尾(区分大小写)
                            let str = "Hello world";
                            str.endsWith("world")   // 返回 true
                            str.endsWith("World")   // 返回 false
                        4.indexOf:返回某个指定的字符串值在字符串中首次出现的位置。
                            var str="Hello world, welcome to the universe.";
                            var n=str.indexOf("welcome"); // 13
                        5.includes:查找字符串中是否包含指定的子字符串
                            var str = "Hello world, welcome to the Runoob。";
                            var n = str.includes("Runoob"); //true
                        6.lastIndexOf:从后向前搜索字符串,并从起始位置(0)开始计算返回字符串最后出现的位置(没有找到返回-1)
                            var str="I am from runoob,welcome to runoob site.";
                            var n=str.lastIndexOf("runoob",20);//28    20代表从第20个字符开始找最后出现的位置
                        7.查找到一个或多个正则表达式匹配。
                            var str="The rain in SPAIN stays mainly in the plain"; 
                            var n=str.match(/ain/g); // [ain,ain,ain]
                        8.复制字符串指定次数,并将他们连接在一起返回
                            var str = "Runoob";
                                str.repeat(2);// RunoobRunoob
                        9.replace:在字符串中查找匹配的子串,并替换与正则表达式匹配的子串。
                            var str="Visit Microsoft! Visit Microsoft!";
                                var n=str.replace("Microsoft","Runoob");  // Visit Runoob!Visit Microsoft!
                        10.replaceAll:在字符串中查找匹配的子串,并替换与正则表达式匹配的所有子串
                            var str="Visit Microsoft! Visit Microsoft!";
                            var n=str.replaceAll("Microsoft","Runoob");// Visit Runoob!Visit Runoob!
                        11.search:查找与正则表达式相匹配的值
                            var str="Visit Runoob!"; 
                            var n=str.search("Runoob");// 6
                        12.slice:提取字符串片段,并在新的字符串中返回被提取的部分
                            var str="Hello world!";
                            var n=str.slice(1,5);// ello
                        13.split:把字符串分割为字符数组
                            var str="How are you doing today?";
                            var n=str.split(" ");// How,are,you,doing,today?
                        14.startsWith:查看字串是否以指定的子字符串开头
                            var str = "Hello world, welcome to the Runoob.";
                            var n = str.startsWith("Hello");// true
                        15.substr:从起始索引号提取字符串中指定数据的字符
                            var str="Hello world!";
                            var n=str.substr(2,3)// llo
                        16.提取字符串中两个指定索引号之间的字符
                            var str="Hello world!";
                            document.write(str.substring(3)+"<br>");
                            document.write(str.substring(3,7)); // lo world!
                                                                // lo w
                        17.toLowerCase:把字符串转换为小写
                            var str="Runoob";
                            document.write(str.toLowerCase());
                        18.toUpperCase:把字符串转换为大写
                            var str="Runoob";
                            document.write(str.toUpperCase());
                        19.trim:去除字符串两边的空白
                            var str = "       Runoob        ";
                            alert(str.trim());
                        20.valueOf:返回某个字符串对象的原始值
                            var str="Hello world!";
                            document.write(str.valueOf());// Hello world!
                        21.toString:返回一个字符串
                            var str = "Runoob";
                            var res = str.toString(); // Runoob
                            const object = {name: '小王', age: 23};
                            object.valueOf();// {name: '小王', age: 23}
                            object.toString();// [Object,Object]

                AMD和CMD和ES6模块化:
                    AMD:代表的就是require.js(依赖前置,引入模块就声明了模块,会提前加载模块)
                    CMD:代表的是module.exports(依赖就近:只有模块用到的时候才加载模块,使用require()引入模块);
                    ES6:模块化代表的import export(默认暴露,分别暴露,统一暴露)

                微任务和宏任务:
                        宏任务:setTimeout;setInterval;PostMessage;setImmediate;
                        微任务:Promise.then;Object.obsersve;mutationObserver;process.nextTick();
                        
                状态码:(30开头一般式请求重定向,需要重新请求地址,400一般客户端错误,500一般服务器错误);
                    200: '请求被正确处理并返回了结果',
                    201: '新增或修改数据成功',
                    202: '请求已进入任务队列,被异步处理',
                    203: '令牌或登录状态失效',
                    204: '删除数据成功',
                    301: '请求的资源被永久重定向到新的位置,将从新的地址重新请求',
                    302: '请求的资源被临时重定向到新的位置',
                    400: '请求参数错误,服务器没有对数据做新建或修改',
                    401: '无访问权限,用户名、密码、令牌错误',
                    403: '得到访问授权,但访问是被禁止',
                    404: '访问的是不存在的资源',
                    405: '请求方式不正确',
                    406: '请求的数据格式不是服务接收的类型',
                    410: '请求的资源被永久删除',
                    422: '服务器创建对象时发生错误',
                    500: '服务器不可用,未返回正确的数据',
                    502: '服务器网关错误', 
                    503: '服务器过载或维护中',
                    504: '服务器响应超时', 
       */
        /*
            webpack(入口,输出,loader,plugins,mode)
            webpack优化:
                js压缩:plugin:terser-webpackplugin
                css压缩: css-minimizer-webpack-plugin
                html压缩  HtmlWebpackPlugin
                文件压缩:  compression-webpack-plugin
                图片压缩:  image-webpack-loader
                Tree shaking: 树摇
                代码分离:
                内联chunk:

            webpack: (hot)
        */
       /*
        vue2:
            1.数据双向绑定:(视图层,model层,数据模型层)
                mvvm:
                    实现mvvm主要是两方面(目的是:data更新view,view更新data;mvvm中是有一个中间层viewmodel,view不会直接跟data层交互,而是通过viewmodel更新数据和更新view层)
                
                vue数据双向绑定是通过(数据劫持结合发布订阅模式)来实现的。
                vue的数据劫持对于普通对象的(深层对象是通过递归调用来实现的,数组是改写了数组的方法:push,pop,shift,unshift,splice,sort,reverse)
                1.我们需要一个监听器(Observer)用来监听所有属性,当属性发生变化就需要告诉订阅者watcher看是否需要更新。
                2.订阅者有很多个,需要一个消息订阅器dep(来统一收集这些订阅者,然后在observer和watcher之间统一管理)
                3.我们需要一个解析器(compile)对每个节点元素进行扫描和解析,初始化视图;将相关指令对应初始化一个订阅者watcher,并且替换模板数据或者绑定相应函数,当订阅者watcher接收到相应属性变化,就会执行对应的更新函数,从而更新视图。

            2.v-model:(input value)
                input标签是绑定@input事件和value值
                当value值发生改变时候,会将值给到input,在input事件中设置$event.target.value = 值;执行后就会更新dom的value值(视图层更新数据层和数据层更新视图层)
            3.v-if和v-show
                v-if主要是对dom元素进行了移出;v-show是设置了display:none;
                频繁操作时候使用v-show;偶尔操作使用v-if

            4.生命周期:
                vue2声明周期:
                    1.beforeCreate
                    2.Created
                    3.beforeMount
                    4.mounted
                    5.beforeUpdate
                    6.updated
                    7.beforeDestroyed
                    8.destroyed
                    9.activated
                    10.deactivated
                    11.errrorcapture
            
            5.路由守卫:(beforeRouteEnter)
                1.全局守卫:
                    beforeEach(to from next)
                    beforeResolve(to from next)
                    afterEach(to from)
                2.独享守卫:
                  beforeEnter(to, from, next)
                3.路由组件内守卫:
                  beforeRouteEnter(to, from, next) {
                    不可以访问this
                  }
                  beforeRouteUpdate(to, from, next) {
                    可以访问this
                  }
                  beforeRouteLeave(to, from, next){
                    可以访问this
                  };
            6.computed和watch和methods
                  computed:用来计算属性,它会根据你所依赖的数据动态显示新的计算结果,计算结果会被缓存,只有依赖值改变时候,才会重新计算。
                  watch:是当某个数据变化时候,使用watch来观察变化后进行数据操作,没有缓存。
                  methods是一个对象属性函数,对发生的在dom中的事件进行反应,可以在computed或者watch中调用他们。
            7.mounted和created发送请求的时机
                  1.created中不可以访问dom元素,mounted中可以访问dom元素
                  2.created发送ajax请求比mounted中快
                  3.mounted中访问dom元素可以可使用this.$refs获取dom元素,但是子组件不一定会挂载,可以使用$nextTick()中获取。

            8.父子组件内部beforeMount和mounted
                  父beforeMount
                  子beforeMount
                  子mounted
                  父mounted

            9.data中属性为啥是函数不是对象。
                  data中是函数利用工厂模式,不同的vue组件实例都有一个特定的data函数管理数据不会互相影响,而如果是对象,会导致数据属性发生污染。

            10.单页面和多页面区别:
                        单页面                                          多页面
                  1.组成:一个页面和多个页面片段组成                     多个完整页面组成
                  2.资源公用(css.js) 公用,只需要外壳部分加载           不共用,每个页面都需要加载
                  3.刷新方式:页面局部刷新或更改                         整页面刷新
                  4.url模式 hash模式                                   history模式
                  5.页面片段切换快,用户体验好                          页面切换加载慢,流畅度不够,用户体验差
                  6.数据传递容易                                        依赖url传递参数,cookie,localStorage
                  7.搜索引擎优化(SEO)不利于seo                        容易实现
                  8.开发成本较高,需要借助专业框架                       较低,但是页面重复代码较多
                  9.维护容易                                            相对复杂

            11.vue通信方式:(props,自定义事件,全局事件总线,ref,$parent和$root和,attrs与listener,provide和inject,vuex)
                  
            12.vuex:(state,getter,mutations,actions)
                  1.actions主要是用于响应组件中的动作,通过commit()来触发mutation中函数调用,间接更新state,不是必须存在的
                  2.mutation主要用于操作修改数据,是必须存在的
                  3.actions可以异步操作,可以向后台提交数据或者接受后台数据。
                  4.mutation是同步操作,不能写异步代码,只能单纯操作state,用于将数据信息写在全局数据状态中缓存,不能异步操作。

            13.mixins:(数据状态不清楚)
                  
            14.slot(插槽:默认插槽;具名插槽;作用域插槽)
            15.keep-alive(activated;deactivated);

            16.nextTick(原理;下一次dom更新会执行;重点);
            
            17.路由模式(history和hash)
            18.MVVM和MVCM
            19.Object.defineProperty
            20.vue template 到render过程发生了什么。(vue模板编译)
            21.vue初始化页面闪动问题:
            22.vue2data属性新增和删除失去响应式,处理办法
            23.vue虚拟dom
       */
      /*
      vue性能优化:
            1.v-if和v-show
            2.computed和watch使用
            3.v-for必须添加key
            4.长列表性能优化,只是展示的数据使用Object.freeze()对数据进行冻结失去响应式
            5.事件销毁
            6.图片懒加载
            7.路由懒加载
            8.第三方插件按需引入
            9.优化列表展示(虚拟列表)
            10.服务端渲染(更好的SEO和首屏加载更快)
            11.webpack对图片压缩
            12.优化sourceMap
            13.提取组件css到单独的文件
            14.开启压缩
            15.浏览器缓存
            16.图片的手动压缩
      */
      /*
        TS:
        
      
      
      
      
      
      */
     /*
        vue3:
        0.setup(在beforeCreate之前执行,接收两个参数(props:组件传入的属性;context:))
        const user = reactive({ name: 'sz', age: '123'});
        export default defineCompent({
            setup(props, context) {
                const { name } = props; // 不可以直接使用ES6解构,这样会使得数据失去响应式,可以使用toRefs
                console.log(name);
                                        //context中提供了this中最常用的三个属性:
                                        // attrs,slot,emit;分别对应vue2中$attr slot插槽和$emit发射事件

                ...toRefs(user);
            }
        });
        1.ref(用来基本数据类型的响应式数据设置,数据处理时候.value进行使用) ref(1)
        2.reactive(处理引用对象,对象和数组;无需使用.value处理数据) reactive({ name: 'zs', age: 18})
        3.toRefs
        4.watch(监听特定数据,并在回调函数中执行,默认是惰性的,只有数据变更才执行回调)
            watch(source,callback,[option])
                source:支持string,object,funtion,array
                callBack:执行回调函数
                option:支持deep、immediate、和flush选项

                // 监听reactive数据,修改age值时会触发 watch的回调
                const state = reactive({ name: 'sz', age: '11'});
                watch(
                () => state.age,
                (curAge, preAge) => {
                    console.log("新值:", curAge, "老值:", preAge);
                }
                );

                // 监听ref数据
                const year = ref(0);
                watch(year, (newVal, oldVal) => {
                console.log("新值:", newVal, "老值:", oldVal);
                });

                //监听多个数据(合并监听多个数据源)
                watch([() => state.age, year], ([curAge, newVal], [preAge, oldVal]) => {
                console.log("新值:", curAge, "老值:", preAge); console.log("新值:", newVal,
                "老值:", oldVal); });

                // 监听复杂对象数据
                const state = reactive({
                    room: {
                        id: 100,
                        attrs: {
                        size: "140平方米",
                        type: "三室两厅",
                        },
                    },
                    });
                    watch(
                    () => state.room,
                    (newType, oldType) => {
                        console.log("新值:", newType, "老值:", oldType);
                    },
                    { deep: true }
                    );
                    如果不使用第三个参数deep:true, 是无法监听到数据变化的。 前面我们提到,默认情况下,watch 是惰性的, 那什么情况下不是惰性的, 可以立即执行回调函数、 给第三个参数中设置immediate: true即可。

                    //停止监听:(某个时间停止监听)
                    const stopWatchRoom = watch(() => state.room, (newType, oldType) => {
                            console.log("新值:", newType, "老值:", oldType);
                        }, {deep:true});

                        setTimeout(()=>{
                            // 停止监听
                            stopWatchRoom()
                        }, 3000)
        5.watchEffect(无需传入依赖,自动收集依赖)
                    watchEffect(() => {
                    console.log(state);
                    console.log(year);
                }
                );
                1.watchEffect 不需要手动传入依赖
                2.watchEffect 会先执行一次用来自动收集依赖
                3.watchEffect 无法获取到变化前的值, 只能获取变化后的值
        6.computed
        7.生命周期钩子函数
            0.setup
            1.beforeCreate
            2.created
            3.onBeforeMount
            4.onMounted
            5.onBeforeUpdate
            6.onUpdated
            7.onBeforeUnmount
            8.onUnmounted
            9.onActivated
            10onDeactivated
            11.onErrorCaptured
            // 用于调试的钩子函数
            12.onRenderTriggered
            13.onRenderTracked
        8.vue3自定义hook(hook就是一个组合式封装的函数,代码复用)
                
                // hook函数文件

                import { ref, Ref, computed } from "vue";
                    type CountResultProps = {
                    count: Ref<number>;
                    multiple: Ref<number>;
                    increase: (delta?: number) => void;
                    decrease: (delta?: number) => void;
                    };

                    export default function useCount(initValue = 1): CountResultProps {
                    const count = ref(initValue);

                    const increase = (delta?: number): void => {
                        if (typeof delta !== "undefined") {
                        count.value += delta;
                        } else {
                        count.value += 1;
                        }
                    };
                    const multiple = computed(() => count.value * 2);

                    const decrease = (delta?: number): void => {
                        if (typeof delta !== "undefined") {
                        count.value -= delta;
                        } else {
                        count.value -= 1;
                        }
                    };

                    return {
                        count,
                        multiple,
                        increase,
                        decrease,
                    };
                    }

                // hook函数引入组件使用

                    <template>
                    <p>count: {{ count }}</p>
                    <p>倍数: {{ multiple }}</p>
                    <div>
                        <button @click="increase()">加1</button>
                        <button @click="decrease()">减一</button>
                    </div>
                    </template>

                    <script lang="ts">
                    import useCount from "../hooks/useCount";
                    setup() {
                        const { count, multiple, increase, decrease } = useCount(10);
                            return {
                                count,
                                multiple,
                                increase,
                                decrease,
                            };
                        },

        9.vue2和vue3响应式对比:


        /*
        npm是如何做版本管理的:
        遵循semver规范(1.9.1)(主版本号:做了不兼容的api修改;次版本号:向下兼容新增;修订号:向下兼容修正)
        */
        /*react:
        useMemo和useCallback的区别?(两个最好配套使用)
        1.useCMemo缓存的结果是回调函数的返回值
        2.useCallback缓存的结果是函数。
            
        */
       /*CSS3(css盒子模型)
       */
      /*事件模型(如果在过程中出发一个click事件会怎样)*/
      /*map和weakmap和Object
         1.map和object区别:
            1.map默认没有任何键,object有一个原型上的键名可能和自己设置的冲突
            2.map键可以是任意值,包括函数,对象或者任意基本类型,object必须是string或者symbol
            3.map的key是有序的,迭代的时候map可以插入顺序返回键值,object键是无顺序的
            4.map键值对数量可以轻易通过size获取,object键值手动计算
            5.map是有iterate接口的,可以直接被迭代,objet需要获取键值再迭代
            6.map删减键值性能好
        2.map和weakmap的区别:
            1.map集合键值键可以是任意值,包括函数,对象或者任意基本类型,而weakmap键必须是对象(null除外)
            2.map有size属性set方法get方法,has方法,weakmap不支持forEach方法和size属性和clear方法
            3.weakmap中键值对象是弱引用,垃圾回收机制不会考虑回收,因此不需要手动删除引用
      */
     /*CSS不常用属性(css3)
     1.transform变形
     2.animation(动画)
     3.transition(过渡)


     */
    /*js组合继承*/
    /*new关键字做了什么?(案例查看随笔文件)
    1.创建了一个空对象,并且将空对象的__proto__指向构造函数的原型
    2.执行构造函数,并将this指向新创建的对象。
    3.返回新对象
    
    */
    /*
    爬楼梯问题
    */
   /*
   vue指令相关
   */
  /*自定义导航栏(a到b b是新的标签页,b打开了好多页面,如何返回a)
  */
 /*
    自定义一个联动下拉框
 */
/*
不知道多少个子集,并且异步请求,如何定义props和逻辑*/
/*
axios请求原理,如何封装
*/
/*埋点*/
/*
浏览器循环机制,强制缓存,协商缓存,浏览器缓存。
浏览器缓存机制:
    1.浏览器页面加载时候查看是否有缓存,有则不向服务器发送请求,没有则则向服务器发送请求
    2.有缓存则会判断缓存标识是否过期,没有过期则使用强制缓存,返回缓存结果
    3.如果缓存标识过期,则携带标识码向服务器发送请求,如果资源无更新则返回304,协商缓存生效继续使用缓存结果
    4.反之资源更新了则返回200并将结果存入到浏览器中。
    
*/
/*
    vue自己封装axios需要实现哪些功能
*/
/*git的缓存功能*/
/*diff算法*/
/*promise和async和await*/
/*rem和em区别
rem是相对于根元素进行计算,而em是相对于当前元素或父元素的字体大小
*/
/*webpack中loader和plugin的区别
plugin主要用来处理loader不能处理的问题,(loader主要用来打包文件,而plugin可以实现单独抽离css,热更新,可以开启多进程加快代码构建)
*/
/*rem适配,js数据类型,本地数据存储,小程序的一些方法*/ 
    </script>
</body>
</html>