美文网首页
中级前端面试题,不仅仅只要会使用,更要懂原理

中级前端面试题,不仅仅只要会使用,更要懂原理

作者: 祝家庄打烊 | 来源:发表于2019-09-29 18:14 被阅读0次

    什么事单线程?

    主线程只有一个线程,同一时刻只能执行单个任务

    为什么选择单线程?

    JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。如:用户在同一个时刻,既要要变样式又要改变其宽度,这会程序不知所措

    单线程意味着什么?

    单线程就意味着,所有任务都需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就需要一直等着。造成资源的浪费

    如何解决单线程带来的性能问题?

    异步处理可以解决单线程带来的性能问题,异步会重新开辟一个线程(辅助线程),加载执行异步任务,异步任务有了运行结果,会把任务扔到任务队列里,等待主线程所有的同步任务都执行完成后,才会执行任务队列里面的任务。

    什么是事件循环机制?

    所有同步任务都在主线程上的栈中执行 ——所有的异步任务有单独线程处理——处理结果存放到任务队列中(标记成事件)——栈中的同步任务都执行完,开始执行任务队列中的事件,循环往复的过程,就叫做事件循环机制。


    Event Loop

    this的原理?为什么会产生this?

    简单点说,函数是存放在堆内存中,栈内存只存放着函数的引用地址,根据引用地址在堆内存中寻找执行的函数。堆内存的每个函数都是独立的,尽管对象中会有函数存在,也仅仅只是函数的引用。因为每个函数都是独立的,所以需要一个属性来指向当前的执行环境,this就因此产生。不懂得可以去看下阮一峰老师的文章,http://www.ruanyifeng.com/blog/2018/06/javascript-this.html

    浏览器的执行时间线?

    1.创建Document对象,标志着浏览器执行的开始,开始解析页面document.readyState="loading"
    2.遇到link,创建线程,进行异步加载样式,并继续解析页面
    3.遇到script外部加载js,两种情况,第一种没有设置async,defer属性,同步加载堵塞后面程序执行,等待脚本加载完成,才开始解析页面。第二种设置async,defer,重新创建线程加载,立马解析,不会等待。对于async属性的脚本,脚本加载完成后立即执行;对于defer属性的脚本,文档解析完成后,开始执行
    4.遇到img等,先正常解析dom结构,然后浏览器异步加载src,并继续解析文档
    5.当文档解析完成(domTree建立完毕,不是加载完毕),document.readyState=‘interative’
    6.文档解析完成后,所有设置defer的脚本会按顺序执行(现实并不按顺序,js高程中有说明);
    7.document对象触发DOMContentLoaded事件(jQuery入口函数其实就是触发的这个事件),这也标志着程序从同步脚本执行阶段,转化为事件驱动阶段。(也就标志着事件可以开始使用)
    8.当所有异步的脚本加载完并执行后(img等页面所有的都加载并执行完后),document.readyState=‘complete’,window触发load事件;

    new Function的实现原理?

    先了解构造函数的内部实现,第一步:隐式的创建var this = {};第二步:隐式的return this。注意:new不是用来创建对象的,是用来继承的。

    /*
        *new是用来继承的。怎么实现?
        *1.继承构造函数的属性
        *2.继承构成函数原型的属性
    */
    function Zhuy(){
        this.name="朱家庄";
        this.content="努力成就未来";
        this.say=function(){
            console.log(this.name)
        }
        return {name:"zjz",z:"z"};   //注意:构造函数如果返回基本类型,默认返回this.如果返回的是对象,默认返回对象
    }
    Zhuy.prototype.order="钱多多"
           
    function newObj(){
        var obj = {};
        constructor = Array.prototype.shift.call(arguments); //获取构造函数
        obj.__proto__=constructor.prototype; //返回的对象原型指向构造函数的原型
        var result = constructor.call(obj)    //执行构造函数,改变构造函数内的this指向
        return typeof(result)=="object"?result||obj:obj;
    }
    var person = newObj(Zhuy,"姓名","内容");
    console.log("person",person);
    

    Array.prototype.slice实现原理?

    第一步:新创建数组对象。第二步:对this进行过滤,满足条件的存放到数组。第三步:返回数组对象。

    /*
    Array.prototype.slice.call(arguments)带有length属性的对象包括伪数组,转换成数组
    Array.prototype.slice原理,对内部的this进行过滤,返回截取后的数组。
    */
    var newArray = [1,2,3,4,5,6,7,8];
    Array.prototype.sliceNew = function(){
        var arr = [];
        for(var i=0;i<this.length;i++){
            switch(arguments.length){
                case 0:
                arr.push(this[i]);
                break;
                case 1:
                i>=arguments[0]&&arr.push(this[i]);
                break;
                default:
                if(i>=arguments[0]&&i<arguments[1]){
                    arr.push(this[i]);
                }
                break;
            }
        }
        return arr;
    }
    console.log(newArray.sliceNew(2,5));
    

    原型的最终指向?

    首先了解包装类的概念:原始值没有属性和方法,不能给原始值赋属性值,当给一个原始值赋予属性的时候,不会报错,会隐式的转化成对象(new Number或new String)。赋完值后,删除当前对象,以后程序也找到此属性和对象。


    image

    设计模式(23种),说出4种即可?

    单例模式:一个类里面只能实例化一个对象,实例化构造函数,先判断当前实例是否存在,不存在的话,进行实例化并将值存储起来。
    优势:防止多次实例化导致内存泄漏(每次实例化都会在堆中产生一个新的对象)

    var mode1 = {
        Example:function(name,content){
            this.name = name;
            this.content = content;
        },
        single:function(name,content){
            if(!this.store){
                this.store=new this.Example(name,content);
            }
            console.log("this.store",this.store)
        }
    }
    mode1.single("祝家庄打样","666");
    

    工厂模式:实例化一个对象,对象可以生产出各种属性和共有的方法,通过传递参数的形式来获取独特的方法。
    优势:解耦.提供共有方法

    function Factory(color,model){
        this.color=color;
        this.model=model;
        this.size=200;
        this.wheel=function(){
            console.log("制造轮子")
        };
        this.seat=function(){
            console.log("制造后背椅")
        };
        this.assemble=function(){
            console.log("进行组装")
        }
     }
    var product = new Factory("黑色","BM");
    product.wheel();
    product.seat();
    //所有工序都完成,可以组装车子了
    product.assemble();
    

    发布订阅模式:订阅者消息都存储起来,发布者遍历执行储存的消息
    优势:发布者与订阅者耦合性降低,发布者不用去管订阅者如何去接收消息,只需要发送消息即可

    var mode3={
        store:[],
        //订阅者,存储消息
        order:function(res,type){
            this.store.push({type:type,inform:res})
        },
        //发布者,遍历消息
        publish:function(){
            if(this.store.length==0) return false;
            var discount = null;
            for(var i=0;i<this.store.length;i++){
                switch (this.store[i].type){
                    case "88":
                    this.store[i].inform("发布一条消息:楼盘打折咯,"+this.store[i].type+"平方米九折优惠");
                    break;
                    case "100":
                    this.store[i].inform("发布一条消息:楼盘打折咯,"+this.store[i].type+"平方米八折优惠");
                    break;
                    default:
                    this.store[i].inform("发布一条消息:楼盘打折咯,"+this.store[i].type+"平方米七折优惠");
                            
                }
            }
        }
    };
    mode3.order(function(res){console.log(res)},"88")
    mode3.order(function(res){console.log(res)},"100")
    mode3.publish();
    

    代理模式:类似一个中介,在客户端对象和提供服务者对象建立的一个桥梁
    优势:解耦,客户端只需要通知代理对象即可

    var Shop = function(name){
        this.name=name;
    }
    //商场买鞋店铺,8~18点店铺打样
    Shop.prototype.buyShoes=function(){
        var hour = new Date().getHours();
        return (hour>8&&hour<18)?this.name:null
    }
    //助理去商场买鞋,并把消息返回给明星
    var assistant = {
        buyShoes:function(res){
            start.buyShoes(res.buyShoes())
        }
    }
    //明星对象
    var start={
        buyShoes:function(name){
            console.log("买了一双:"+name)
        }
    }
    //明星通知助理要买高跟鞋
    assistant.buyShoes(new Shop("高跟鞋"))
    

    策略模式:策略模式指的是定义一系列的算法,把它们一个个封装起来。
    优势:解耦,每个算法都是独立的存在,不会相互影响
    奖金算法(普通)

    var calculateBouns = function(type,money){
        var defaultValue=money;
        switch(type){
            case "S":
            defaultValue=money*4;
            break;
            case "A":
            defaultValue=money*3;
            break;
            case "B":
            defaultValue=money*2;
            break;
          };
          return defaultValue;
    }
    console.log(calculateBouns("A",4000))
    

    奖金算法(策略模式)

    //设置每个策略
    var PerformanceS = function(){};
    PerformanceS.prototype.cal = function(salary){
        return salary*4;
    }
    var PerformanceA = function(){};
    PerformanceA.prototype.cal = function(salary){
        return salary*3;
    }
    var PerformanceB = function(){};
    PerformanceB.prototype.cal = function(salary){
        return salary*2;
    }
    //设置奖金类
    var Bouns = function(){
        this.salary = null;
        this.strategy = null;
    };
    Bouns.prototype.setSalary = function(salary){
        this.salary=salary;
    }
    Bouns.prototype.setStrategy = function(strategy){
        console.log("strategy",strategy)
        this.strategy = strategy;
    }
    Bouns.prototype.getBouns = function(){
        return this.strategy.cal(this.salary);
    }
    //调用方法
    var bounsway = new Bouns();
    bounsway.setSalary(5000);
    bounsway.setStrategy(new PerformanceS());
    console.log(bounsway.getBouns());
    

    如何防止抖动

    函数防抖(debounce):当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。比如,取输入框的值(每次输入都需要取值)或下拉加载(每次下拉都需要获取底部距离),这样十分的耗费性能。解决方法也是很简单的,每次加载函数的时候,定义一个延长时间。第二次清空重新加载。

    var int = jitter(function(ev){
        console.log(ev.target.value);
    },2000);
    function inputChange(ev){
        int(ev);
    }
    function jitter(fn,delay){
        var timer = null;
        return function(ev){
            if(timer){
                clearTimeout(timer);
            }
            timer = setTimeout(function(){
                fn(ev);
            }, delay);
        }
    }
    

    节流

    函数节流(throttle):当持续触发事件时,保证一定时间段内只调用一次事件处理函数。

    function throttle(fn,delay){
        var timer = null;
        return function(){
            if(!timer){
                timer = setTimeout(function(){
                    fn();
                    timer=null;
                 }, delay);
              }
          }
    }
    function handle() {            
        console.log(Math.random());        
    }        
    window.addEventListener('scroll', throttle(handle, 1000));
    

    相关文章

      网友评论

          本文标题:中级前端面试题,不仅仅只要会使用,更要懂原理

          本文链接:https://www.haomeiwen.com/subject/krdmuctx.html