test

头像
李帅醒
    阅读 10 分钟

    防抖:多次触发事件后,事件处理函数只执行一次,并且是在触发操作结束时执行。

        function debounce(fn) {
          // 4、创建一个标记用来存放定时器的返回值
          let timeout = null;
          return function() {除
            clearTimeout(timeout);
            var args = arguments;
            timeout = setTimeout(() => {
              fn.apply(this, args);
            }, 1000);
          };
        }
        sayDebounce(){
         console.log("防抖成功!");
        }
        btn.addEventListener("click", debounce(sayDebounce));

    节流: 触发函数事件后,短时间间隔内无法连续调用,只有上一次函数执行后,过了规定的时间间隔,才能进行下一次的函数调用

        var throttle = function(func, delay) {
            var prev = Date.now();
            return function() {
                var context = this;
                var args = arguments;
                var now = Date.now();
                if (now - prev >= delay) {
                    func.apply(context, args);
                    prev = Date.now();
                }
            }
        }
        function handle() {
            console.log(Math.random());
        }
        window.addEventListener('scroll', throttle(handle, 1000));
        // 处理函数
        function handle() {
            console.log(Math.random()); 
        }
        // 滚动事件
        window.addEventListener('scroll', debounce(handle, 1000));

    js 实现once 方法

    function runOnce(fn, context) { //控制让函数只触发一次
      return function () {
        try {
          fn.apply(context || this, arguments);
        }
        catch (e) {
          console.error(e);//一般可以注释掉这行
        }
        finally {
          fn = null;
        }
      }
    }
    var obj = {name: "狗子", age: 24};
    var canOnlyFireOnce = runOnce(function () {
      console.log("你好" + this.name);
    }, obj);
    canOnlyFireOnce(); //你好天涯孤雁
    canOnlyFireOnce(); // nothing

    实现bind 或者 call

    Function.prototype.bind= function(obj){
        var _self = this, args = arguments;
        return function() {
            _self.apply(obj, Array.prototype.slice.call(args, 1));
        }
    }
    Function.protype.call = function(context){
        context = context || window
        context.fn = this;
        const args = [...arguments].slice(1);
        const result = context.fn(...args);
        delete context.fn
        return result;
    }

    reduce实现map

        const reduceMap = (fn, thisArg /*真想去掉thisArg这个参数*/ ) => {
            return (list) => {
                // 不怎么愿意写下面这两个判断条件
                if (typeof fn !== 'function') {
                    throw new TypeError(fn + 'is not a function')  
                }
                if (!Array.isArray(list)) {
                    throw new TypeError('list must be a Array')
                }
                if (list.length === 0) return []
                return list.reduce((acc, value, index) => {
                    return acc.concat([ fn.call(thisArg, value, index, list) ])
                }, [])
            }
        }
        // 来使用下怎么样?
        reduceMap(x => x + 1)([ 1, 2, 3 ]) // [ 2, 3, 4 ]
        
        const mapAry1 = reduceMap(function(item) {
            console.log(this)
            return item + 1
        }, { msg: 'mapping' })([ 1, 2, 3 ]) 

    实现一个函数delay(alert,3,4000)(“hello”),每间隔4秒打印一次“hello”,打印三次。

      function delay(fn,nums,times){
        return async function(content){
          for(var i = 0; i<nums; i ++){
            await new Promise(resolve =>{
              setTimeout(()=>{
                fn.call(this, content);
                resolve(true);
              }, times)
            })
          }
        }
      }
      delay(console.log, 3, 4000)('hello');

    setTimeout 实现 setInterval

    function setInterval(func, t){
        var inter = function(){
            setTimeout(inter,t);
            try{
                func.call(null);
            }
            catch(e){
                throw e.toString();
            }
        }
        setTimeout(inter,t);
    };

    JS实现找出字符串中出现最多的字符和次数

          function getMost(str){
            var obj = {};
            for(var i =0; i< str.length; i ++){
              var char =  str.charAt(i);
              if(obj[char]){
                obj[char] ++;
              }else {
                obj[char] = 1; 
              }
            }
            var max = 0;
            var maxChar = null;
            for(var p in obj) {
              if (max <obj[p]){
                max = obj[p];
                maxChar = p;
              }
            }
            return `出现最多的字符:${maxChar},共出现${max}`
          }

    手写一个双向绑定,input输入的值响应在p标签上

    <body>
        <div id="app">
        <input type="text" id="txt">
        <p id="show"></p>
    </div>
    </body>
    <script type="text/javascript">
        var obj = {}
        Object.defineProperty(obj, 'txt', {
            get: function () {
                return obj
            },
            set: function (newValue) {
                document.getElementById('txt').value = newValue
                document.getElementById('show').innerHTML = newValue
            }
        })
        document.addEventListener('keyup', function (e) {
            obj.txt = e.target.value
        })
    </script>

    长度为N的数组,两数相加为sum

    function findGroup(arr,n,sum){
        if (sum == 0 && n == 0) {
            return true;
        } else if (n <= 0) {
            return false;
        }
        if (n > 0) {
            for (var i = 0; i < arr.length; i++) {
                var temp = arr.slice(i+1,arr.length);
                return findGroup(temp,n-1,sum-arr[i]) || findGroup(temp,n,sum);
            }
        }
    }
    function getNum(arr, sum) {
        if (!Array.isArray(arr)) return null;
        arr.sort();
        for (var i = 0; i < arr.length - 1; i++) {
            if (arr[i] > sum) continue;
            var restNum = sum - arr[i];
            // 考虑下为什么要 > i
            if (arr.indexOf(restNum) > i) return [arr[i], restNum];
        }
    
        return null;
    }

    伪数组转换为数组的方法

       Array.from(arr);
       Array.protype.slice(arr, 0);

    rem布局原理

    (function(){
        var html = document.querySelector('html');
        changeRem();
        window.addEventListener('resize', changeRem);
        function changeRem() {
            var width = html.getBoundingClientRect().width;
            html.style.fontSize = width / 10 + 'px';
        }
    })()

    new的原理是什么?通过new的方式创建对象和通过字面量创建有什么区别?

    1.创建一个新对象。
    2.这个新对象会被执行[[原型]]连接。
    3.将构造函数的作用域赋值给新对象,即this指向这个新对象.
    4.如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象

    ts和js的区别,为什么要选择使用ts?

    JavaScript 是轻量级的解释性脚本语言,可嵌入到 HTML 页面中,在浏览器端执行。而TypeScript 是JavaScript 的超集,即包含JavaScript 的所有元素,能运行JavaScript 的代码,并扩展了JavaScript 的语法。相比于JavaScript ,它还增加了静态类型、类、模块、接口和类型注解方面的功能,更易于大项目的开发。
    1.便于开发人员做注释。
    2.能帮助开发人员检测出错误并修改。
    3.TypeScript工具使重构更变的容易、快捷。
    4.TypeScript 引入了 JavaScript 中没有的“类”概念。
    5.TypeScript 中引入了模块的概念,可以把声明、数据、函数和类封装在模块中。
    6.类型安全功能能在编码期间检测错误,这为开发人员创建了一个更高效的编码和调试过程。

    HTTP1.0、HTTP1.1、HTTP2.0的关系和区别(重点)

    HTTP1.0:

    无状态、无连接

    HTTP1.1:

    持久连接
    请求管道化
    增加缓存处理(新的字段如cache-control)
    增加Host字段、支持断点传输等(把文件分成几部分)

    HTTP2.0:

    二进制分帧
    多路复用(或连接共享)
    头部压缩
    服务器推送

    对象的深拷贝与浅拷贝(重点)

    浅拷贝:仅仅复制对象的引用,而不是对象本身;
    Object.assign()
    深拷贝:把复制的对象所引用的全部对象都复制一遍。
    JSON.parse(JSON.stringify(initalObj)

    实现多个标签页之间通信的几种方法

    websocket
    localstorage: onstorage事件。

    vue-router 原理

    1. hash: 使用 URL hash 值来作路由。默认模式。
        window.addEventListener('hashchange', matchAndUpdate)
    2. history: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式。
        pushState replaceState window.onpopstate
    3. abstract模式
        abstract模式是使用一个不依赖于浏览器的浏览历史虚拟管理后端。根据平台差异可以看出,在 Weex 环境中只支持使用 abstract 模式。 不过,vue-router 自身会对环境做校验,如果发现没有浏览器的 API,vue-router 会自动强制进入 abstract 模式

    vue-router 钩子

    全局导航钩子
        router.beforeEach(to, from, next),
        router.beforeResolve(to, from, next),
        router.afterEach(to, from ,next)
    组件内钩子
        beforeRouteEnter: 在渲染该组件的对应路由被 confirm 前调用
        beforeRouteUpdate: 在当前路由改变,但是该组件被复用时调用
        beforeRouteLeave:  导航离开该组件的对应路由时调用
    单独路由独享组件
        beforeEnter

    Object.defineProperty缺点

    一: Object.defineProperty无法监听数组的变化,所以vue在监听数组的变化时有时不能做到双向绑定。

        // 遍历对象,对其属性值进行劫持
        Object.keys(data).forEach(function(key) {
          Object.defineProperty(data, key, {
            enumerable: true,//可枚举
            configurable: true,//可改变
            get: function() {
              console.log('get');
            },
            set: function(newVal) {
              // 当属性值发生变化时我们可以进行额外操作
              console.log(`大家好,我是${newVal}`);
              say(newVal);
            },
          });
        });
        data.name = '蔡康永'; //真正的贵族

    二: Object.defineProperty监听的是对象的属性,当监听的对象有很多层级构成,则需要递归对象直至基本类型,才能进行监听,比较麻烦。相比Proxy监听整个对象的方式,就方便很多。

    CSRF

    跨站请求伪造,击者盗用了你的身份,以你的名义发送恶意请求。
    预防CSRF攻击
    尽量使用POST,限制GET

    1. 尽量使用POST,限制GET当然POST并不是万无一失,攻击者只要构造一个form就可以,但需要在第三方页面做,这样就增加暴露的可能性。

    2.在拦截客户端拦截器加token添加 token 并验证,可以加到参数中,或者在 HTTP 头中自定义属性并验证。
    3.验证 HTTP Referer 字段,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。

    请详细说下你对 Vue 生命周期的理解?

    总共分为 8 个阶段创建前/后,载入前/后,更新前/后,销毁前/后。

    1. 创建前/后: 在 beforeCreate 阶段,vue 实例的挂载元素 el 还没有。
    2. 载入前/后:在 beforeMount 阶段,vue 实例的$el 和 data 都初始化了,但还是挂载之前为虚拟的 dom 节点,data.message 还未替换。在 mounted 阶段,vue 实例挂载完成,data.message 成功渲染。
    3. 更新前/后:当 data 变化时,会触发 beforeUpdate 和 updated 方法。
    4. 销毁前/后:在执行 destroy 方法后,对 data 的改变不会再触发周期函数,说明此时 vue 实例已经解除了事件监听以及和 dom 的绑定,但是 dom 结构依然存在

    四种常见的 POST 提交数据方式对应的content-type取值

    application/x-www-form-urlencoded —— 浏览器的原生 form 表单
    multipart/form-data —— 表单上传文件时,必须让 form 的 enctyped 等于这个值
    application/json —— 用来告诉服务端消息主体是序列化后的 JSON 字符串
    text/xml —— HTTP 作为传输协议,XML 作为编码方式的远程调用规范

    computed原理

    1. 当组件初始化的时候,computed 和 data 会分别建立各自的响应系统,Observer遍历 data 中每个属性设置 get/set 数据拦截
    2. 初始化 computed 会调用 initComputed 函数
      (1)注册一个 watcher 实例,并在内实例化一个 Dep 消息订阅器用作后续收集依赖(比如渲染函数的 watcher 或者其他观察该计算属性变化的 watcher)
      (2)调用计算属性时会触发其Object.defineProperty的get访问器函数
      (3)调用 watcher.depend() 方法向自身的消息订阅器 dep 的 subs 中添加其他属性的 watcher
      (4) 调用 watcher 的 evaluate 方法(进而调用 watcher 的 get 方法)让自身成为其他 watcher 的消息订阅器的订阅者,首先将 watcher 赋给 Dep.target,然后执行 getter 求值函数,当访问求值函数里面的属性(比如来自 data、props 或其他 computed)时,会同样触发它们的 get 访问器函数从而将该计算属性的 watcher 添加到求值函数中属性的 watcher 的消息订阅器 dep 中,当这些操作完成,最后关闭 Dep.target 赋为 null 并返回求值函数结果。
    3. 当某个属性发生变化,触发 set 拦截函数,然后调用自身消息订阅器 dep 的 notify 方法,遍历当前 dep 中保存着所有订阅者 wathcer 的 subs 数组,并逐个调用 watcher 的 update 方法,完成响应更新。

    computed 和 watch 的差异:

    1. computed 是计算一个新的属性,并将该属性挂载到 vm(Vue 实例)上,而 watch 是监听已经存在且已挂载到 vm上的数据,所以用 watch 同样可以监听 computed 计算属性的变化(其它还有 data、props)
    2. computed 本质是一个惰性求值的观察者,具有缓存性,只有当依赖变化后,第一次访问computed 属性,才会计算新的值,而 watch 则是当数据发生变化便会调用执行函数
    3. 从使用场景上说,computed适用一个数据被多个数据影响,而 watch 适用一个数据影响多个数据;

    CSS3

        @keyframes rotating{
            from{transform:rotate(0)}
            to{transform:rotate(360deg)}
        }
        animation:rotating 1.2s linear infinite;

    寄生组合继承

        function Person(name){
         this.name=name; //1
         this.className="person" 
        }
        Person.prototype.getName=function(){
         console.log(this.name)
        }
        function Man(name){
          Person.apply(this,arguments)
        }
        //注意此处
        Man.prototype = Object.create(Person.prototype);
        var man1=new Man("Davin");
        > man1.name
        >"Davin"
        > man1.getName()
        >"Davin"

    ES6中的class和ES5的类有什么区别?

    1.ES6 class 内部所有定义的方法都是不可枚举的;
    2.ES6 class 必须使用 new 调用;
    3.ES6 class 不存在变量提升;
    4.ES6 class 默认即是严格模式;
    5.ES6 class 子类必须在父类的构造函数中调用super(),这样才有this对象;ES5中类继承的关系是相反的,先有子类的this,然后用父类的方法应用在this上。

    let、const 以及 var 的区别是什么?

    1.let 和 const 定义的变量不会出现变量提升,而 var 定义的变量会提升。
    2.let 和 const 是JS中的块级作用域
    3.let 和 const 不允许重复声明(会抛出错误)
    4.let 和 const 定义的变量在定义语句之前,如果使用会抛出错误(形成了暂时性死区),而 var 不会。
    5.const 声明一个只读的常量。一旦声明,常量的值就不能改变(如果声明是一个对象,那么不能改变的是对象的引用地址)

    在JS中什么是变量提升?什么是暂时性死区?

    变量提升就是变量在声明之前就可以使用,值为undefined。在代码块内,使用 let/const 命令声明变量之前,该变量都是不可用的(会抛出错误)。这在语法上,称为“暂时性死区”。暂时性死区也意味着 typeof 不再是一个百分百安全的操作。

    typeof x;
    // ReferenceError(暂时性死区,抛错)
    let x;
    typeof y;
    // 值是undefined,不会报错

    HTML中的meta标签常用属性及其作用总结

    charset: 声明字符编码

    <meta charset="utf-8"> //HTML5

    http-equiv:

    模拟http标头字段
    http-equiv属性与content属性结合使用, http-equiv属性为指定所要模拟的标头字段的名称,content属性用来提供值。
    refresh 指定一个时间间隔(以秒为单位),在此时间过去之后从服务器重新载入当前页面,也可以另外指定一个页面.
    <meta http-equiv="refresh" content="2;URL=http://www.baidu.com">

    name 和 content:

    <meta name="参数" content="具体描述信息">
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />
    主要介绍一个当meta标签的name属性值为viewreport时的视口的大小
    name属性与content属性结合使用, name用来表示元数据的类型,表示当前<meta>标签的具体作用;content属性用来提供值
    

    什么是闭包?闭包的作用是什么?闭包有哪些使用场景?

    闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包最常用的方式就是在一个函数内部创建另一个函数。
    闭包的作用有:
    1.封装私有变量
    2.模仿块级作用域(ES5中没有块级作用域)
    3.实现JS的模块

    CSS中你知道的display的值有多少?

    none:隐藏对象。与visibility属性的hidden值不同,其不为被隐藏的对象保留其物理空间
    inline:指定对象为内联元素。
    block:指定对象为块元素。
    list-item:指定对象为列表项目。
    inline-block:指定对象为内联块元素。(CSS2)
    table:指定对象作为块元素级的表格。类同于html标签<table>(CSS2)
    inline-table:指定对象作为内联元素级的表格。类同于html标签<table>(CSS2)
    flex:将对象作为弹性伸缩盒显示。(伸缩盒最新版本)(CSS3)
    table-cell:指定对象作为表格单元格。类同于html标签<td>(CSS2)


    李帅醒
    113 声望5 粉丝

    内心还是残留一丝文艺情怀的小码农 新浪微博@李胖醒plus


    下一篇 »
    手写js或算法