面试题目整理

vue相关

1.VNode是什么?什么是虚拟dom?

在vue.js中存在一个VNode类,使用它可以实例化不同类型的vnode实例,这些实例可以被渲染成真实的dom对象。
虚拟dom是通过 js 对象来描述真实 DOM 结构和属性。例如:

//下面是一个a标签
<a class="demo" style="color: red" href="#">
    文本内容
</a>
// 用VNode的实例对象来描述
{
  tag: 'a',
  data: {
    calss: 'demo',
    attrs: {
      href: '#'
    },
    style: {
      color: 'red'
    }
  },
  children: ['文本内容']
}

由于真实dom频繁排版与重绘的效率低,对虚拟DOM进行频繁修改,然后一次性对比并修改真实DOM中需要改的部分,最后在真实DOM中进行排版与重绘,减少过多DOM节点排版与重绘损耗。

举个例子:下面有一段html代码

<ul id='list'>
    <li class='item'>Item 1</li>
    <li class='item'>Item 2</li>
</ul>

用js对象模拟如下

{
    tag:'ul',
    attrs:{
        id:'list'
    },
    children:[
        {
            tag:'li',
            attrs:{  className:'item'  },
            children:['Item 1']
        },
        {
            tag:'li',
            attrs:{  className:'item'  },
            children:['Item 2']
         }
    ]
}

如果我们要把Item 2改成Item B,我们会先生成类似上面的js DOM结构如下

{
    tag:'ul',
    attrs:{
        id:'list'
    },
    children:[
        {
            tag:'li',
            attrs:{  className:'item'  },
            children:['Item 1']
        },
        {
            tag:'li',
            attrs:{  className:'item'  },
            children:['Item B'] //注意此处Item 2改编成了Item B
         }
    ]
}

然后通过对比发现改变的只有Item B;现在提供两种改变的思路:

  • 1.直接删掉所有Item,再把Item1 和 Item B加进来。
  • 2.先生成最终js的DOM结构,然后对比之前的js DOM结构,找出差异部分再改变 。
    只从思路上分析,第一种更加简单快捷;然而实际是,浏览器最最最耗费性能的就是DOM操作,dom操作是最昂贵的,现在的浏览器执行js是非常快的,所以虚拟DOM有其存在的价值。
vdom如何应用,核心API是什么?(这个可以先不看)

要我们自己去实现一个虚拟dom,大概过程应该有以下三步:

  • compile:如何把真实DOM编译成vnode。
  • diff:我们要如何知道oldVnode和newVnode之间有什么变化。
  • patch:如果把这些变化用打补丁的方式更新到真实dom上去。
    vue的虚拟dom实现参考了snabbdom.js的实现。

snabbdom.js核心API是两个函数,一个h函数(用来创建vnode),一个patch函数(用来对比变化并替换)。


image.png

image.png
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div id="container"></div>
    <button id="btn-change">change</button>


    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-class.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-props.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-style.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-eventlisteners.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/h.js"></script>
    <script>
        var snabbdom = window.snabbdom;

        //定义path
        var path = snabbdom.init([
            snabbdom_class,
            snabbdom_props,
            snabbdom_style,
            snabbdom_eventlisteners
        ])

        //定义h
        var h = snabbdom.h;

        var container = document.getElementById('container');

        //生成vnode
        var vnode = h('ul#list',{},[
            h('li.item',{},'Item 1'),
            h('li.item',{},'Item 2')
        ]);
        path(container,vnode);

        document.getElementById('btn-change').addEventListener('click',function () {
           //生成newVnode
            var newVnode = h('ul#list',{},[
                h('li.item',{},'Item 1'),
                h('li.item',{},'Item B'),
                h('li.item',{},'Item 3')
            ]);
            path(vnode,newVnode);
        });
    </script>
</body>
</html>

diff算法大概结构

      const vDom = {
            tag:'ul',
            attrs:{id:'list'},
            children:[
                {
                    tag:'li',
                    attrs: {className:'item'},
                    children:['item1']
                },{
                    tag:'li',
                    attrs:{className:'item'},
                    children:['item2']
                }
            ]
        };
        function createElement(vnode) {
            let tag = vnode.tag,
                attrs = vnode.attrs || {},
                children = vnode.children || [];
            if(!tag){return null;}
            //创建真实的DOM元素
            let elem = document.createElement(tag);
            //属性
            let attrName;
            for(attrName in attrs){
                if(attrs.hasOwnProperty(attrName)){
                    //给elem添加属性
                    elem.setAttribute(attrName,attrs[attrName]);
                }
            }
            //子元素
            children.forEach(function (childVNode) {
                //给elem添加子元素
               elem.appendChild(createElement(childVNode))
            });

            // 返回真实的dom元素
            return elem;
        }
        const newVNode = {
            tag:'ul',
            attrs:{id:'list'},
            children:[
                {
                    tag:'li',
                    attrs: {className:'item'},
                    children:['item1']
                },{
                    tag:'li',
                    attrs:{className:'item'},
                    children:['item222']
                },{
                    tag:'li',
                    attrs:{className:'item'},
                    children:['item3']
                }
            ]
        };
        function updateChildren(vNode,newVNode) {
            let children = vNode.children || [],
                newChildren = newVNode.children || [];

            children.forEach(function (childVNode,index) {
                let newChildVNode = newChildren[index];
                if(childVNode.tag===newChildVNode.tag){
                    updateChildren(childVNode,newChildVNode);
                }else{
                    replaceNode(childVNode,newChildVNode);
                }

            });
        }
        function replaceNode(vNode,newVNode) {
            let elem = vNode.elem,
                newElem = createElement(newVNode);

            //替换
            //...
        }
        //节点新增和删除
        //节点重新排序
        //节点属性、样式、事件绑定
        //如何极致压榨性能
2.v-show 与 v-if 区别?
  • v-show是css切换,v-if是完整的销毁和重新创建;
  • 使用频繁切换时用v-show,运行时较少改变时用v-if;
  • v-if="false" v-if是条件渲染,当false的时候不会渲染。
3.计算属性和 watch 的区别?

计算属性是自动监听依赖值的变化,从而动态返回内容,监听是一个过程,在监听的值变化时,可以触发一个回调,并做一些事情。
所以区别来源于用法,只是需要动态值,那就用计算属性;需要知道值的改变后执行业务逻辑,才用 watch。

4.computed 和 methods 有什么区别?

methods是一个方法,它可以接受参数,而computed不能,computed是可以缓存的,methods不会。

5.vue的生命周期
  • beforeCreate
    vue实例的挂载元素el和数据对象data都为undefined,还未初始化。
  • created
    vue实例的数据对象有了,el还没有。
  • beforeMount:
    挂载开始之前被调用,相关的render函数首次被调用(虚拟DOM),实例已完成以下的配置: 编译模板,把data里面的数据和模板生成html,完成了el和data 初始化,注意此时还没有挂在html到页面上。
  • mounted:
    挂载完成,也就是模板中的HTML渲染到HTML页面中,此时一般可以做一些ajax操作,mounted只会执行一次。
  • 更新前后 beforeUpdate/updated
    当data变化时,会触发beforeUpdate和updated方法。
  • beforeDestory
    在实例销毁之前调用,实例仍然完全可用,这一步还可以用this来获取实例,一般在这一步做一些重置的操作,比如清除掉组件中的定时器 和 监听的dom事件。
  • destoryed
    在实例销毁之后调用,调用后,所以的事件监听器会被移出,所有的子实例也会被销毁,该钩子在服务器端渲染期间不被调用。
路由的跳转方式
  • <router-link to='home'> router-link标签会渲染为<a>标签。
  • 编程式导航 也就是通过js跳转 比如 router.push('/home')
6.nextTick()

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后,立即使用这个回调函数,获取更新后的 DOM。

// 修改数据
vm.msg = 'Hello'
// DOM 还未更新
Vue.nextTick(function () {
  // DOM 更新
})
7.slot插槽
  • 单个插槽
    当子组件模板只有一个没有属性的插槽时,父组件传入的整个内容片段将插入到插槽所在的 DOM 位置,并替换掉插槽标签本身。
  • 命名插槽
    solt元素可以用一个特殊的特性name来进一步配置如何分发内容。多个插槽可以有不同的名字。这样可以将父组件模板中 slot 位置,和子组件 slot 元素产生关联,便于插槽内容对应传递

js相关

1.继承的方法有哪些?并解释各个继承详情。
/**1.构造函数继承**/
function parent(){
  this.colors = ['red','blue'];
}
function children(){
  parent.call( this );
}
var c1 = new children();
console.log( c1.colors );   // ['red','blue']
/**2.实例继承**/
function parent2(){
  this.colors = ['red','blue'];
}
function children2(){}
children2.prototype = new parent2();
var c2_1 = new children2();
var c2_2 = new children2();
console.log( c2_1.colors ); // ['red','blue']
console.log( c2_2.colors ); // ['red','blue']
c2_1.colors.push('white');  
console.log( c2_2.colors ); // ['red','blue','white']
/**3.组合继承**/
function parent3(){
  this.colors = ['red','blue'];
}
function children3(){
  parent3.call(this);
}
children3.protopype = new parent3();
var c3_1 = new children3();
var c3_2 = new children3();
console.log( c3_1.colors ); // ['red','blue']
console.log( c3_2.colors ); // ['red','blue']
c3_1.colors.push('white');
console.log( c3_2.colors ); // ['red','blue']

/**4.组合寄生继承(最优 记住这个就可以)**/
function parent4(){
  this.colors = ['red','blue'];
}
function children4(){
  parent4.call(this);
}
children4.prototype = Object.create(parent4);
children4.prototype.constructor = children4;
var c4_1 = new children4();
var c4_2 = new parent4();
console.log( c4_1 instanceof parent4 );  //false
console.log( c4_2 instanceof parent4 );  //true
2.什么是闭包?

简单讲,闭包就是指有权访问另一个函数作用域中的变量的函数。
外部函数调用之后在其内部声明的变量本应该被销毁,但闭包的存在使我们仍然可以访问外部函数的变量对象,这就是闭包的重要概念。

 function outer(){
     var a = 1;
     return function(){
         console.log(a++);
     }
 }
var b = outer();
b();//1
b();//由于a的值不会被释放,再次调用结果为2

应用场景,比如单例模式,防抖函数,节流函数等。

/**单例模式  得到的始终是同一个对象*/
(function (global) {
    function SimpleObj() {
        var instance = null;
        return function () {
            if (instance == null) {
                instance = {
                    name: 'simapleobj'
                }
            }
            return instance;
        }
    }
    global.getInstance = SimpleObj();
})(window)
getInstance(); //{name: "simapleobj"}
var a = getInstance();
console.log(a); //{name: "simapleobj"}
var b = getInstance();
console.log(b); //{name: "simapleobj"}
a.name = 'ACE';
console.log(a); //{name: "ACE"}
console.log(b); //{name: "ACE"}
var c = getInstance();
console.log(c); //{name: "ACE"}
getInstance(); //{name: "ACE"}

/*防抖函数*/
<input type="text">
<script>
    var input = document.querySelector("input");
    function debounce(func, delay) {
        let time;
        return function () {
            //保存this指向
            const context = this;
            //保存传入的参数
            const args = arguments;
            //清除上次的延时器
            clearTimeout(time);
            //重新设置一个延时器,延迟为delay毫秒
            time = setTimeout(function () {
                func.apply(context, args)
            }, delay)
        }
    }
    var inputEvent = debounce(function () {
        console.log("向后端发送请求");
    }, 1500);
    input.addEventListener("input", inputEvent);
</script>
3.原型链

每个函数有个prototype属性,指向其原型对象,原型对象有一个constructor属性指向构造函数;通过函数实例化(new)出来的对象有一个proto属性,也指向函数的原型对象;原型对象也有__proto__属性,指向原型对象的原型对象,通过一层层往上指,直到Object.prototype。

function foo(){}
let f = new foo();
console.log(f.__proto__===foo.prototype);//true
console.log(foo.prototype.constructor===foo);//true

4.如何解决跨域问题 详解转载

  • 1.通过jsonp跨域(只支持get方式)
    通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过script标签,再请求一个带参网址实现跨域通信。
    html代码
<body>
    <script>
        function test(data) {
            alert(data.name);
        }
    </script>
    <script src="http://127.0.0.1:3000/jsonp?callback=test"></script>
</body>

服务端代码(用的express)

router.get('/jsonp', function (req, res, next) {
  res.set({
    'Content-Type': 'json',
  });
  var data = {
    "name": "Monkey"
  };
  data = JSON.stringify(data);
  res.end(`${req.query.callback}(${data})`);
});
  • 2.跨域资源共享(CORS)
    普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置;
    若要带cookie请求:前后端都需要设置。
    前端设置
    1)原生ajax
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容

// 前端设置是否带cookie
xhr.withCredentials = true;

xhr.open('post', 'http://www.domain2.com:8080/login', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('user=admin');

xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status == 200) {
        alert(xhr.responseText);
    }
};

2)jQuery ajax

$.ajax({
    ...
   xhrFields: {
       withCredentials: true    // 前端设置是否带cookie
   },
   crossDomain: true,   // 会让请求头中包含跨域的额外信息,但不会含cookie
    ...
});

3)vue框架

//axios设置:
axios.defaults.withCredentials = true;

// vue-resource设置:
Vue.http.options.credentials = true;

服务端设置

var http = require('http');
var server = http.createServer();
var qs = require('querystring');

server.on('request', function(req, res) {
    var postData = '';

    // 数据块接收中
    req.addListener('data', function(chunk) {
        postData += chunk;
    });

    // 数据接收完毕
    req.addListener('end', function() {
        postData = qs.parse(postData);

        // 跨域后台设置
        res.writeHead(200, {
            'Access-Control-Allow-Credentials': 'true',     // 后端允许发送Cookie
            'Access-Control-Allow-Origin': 'http://www.domain1.com',    // 允许访问的域(协议+域名+端口)
            /* 
             * 此处设置的cookie还是domain2的而非domain1,因为后端也不能跨域写cookie(nginx反向代理可以实现),
             * 但只要domain2中写入一次cookie认证,后面的跨域接口都能从domain2中获取cookie,从而实现所有的接口都能跨域访问
             */
            'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'  // HttpOnly的作用是让js无法读取cookie
        });

        res.write(JSON.stringify(postData));
        res.end();
    });
});

server.listen('8080');
console.log('Server is running at port 8080...');
  • WebSocket协议跨域
    WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯。
实现求和sum,支持sum(1), sum(1,2,3,4), sum(1)(2)(3), console.log(sum(1)(2,3)(4)) = 10
function sum(...rest) {
    let rst = 0;
    rest.forEach(item => rst += item);
    let temp = function (...innerRest) {
        innerRest.forEach(item => rst += item)
        return temp;
    }
    temp.toString = function () {
        return rst;
    }
    return temp;
}
console.log(sum(1))
console.log(sum(1)(2))
console.log(sum(1, 2)(3))
console.log(sum(1, 2)(3)(4, 5))
chain = new Chain, chain.eat().sleep(5).eat().sleep(6).work()
class Chain {
    constructor(name) {
        console.log(name)
        this.stack = [];
        this.time = null;
    }
    async invoke() {
        console.log("invoke")
        if (this.time) {
            clearTimeout(this.time)
        }
        this.time = setTimeout(async () => {
            while (this.stack.length) {
                await this.stack.shift()();
            }
        }, 3000)
    }
    eat() {
        console.log("push eat")
        this.stack.push(
            function () {
                return new Promise(function (reslove) {
                    console.log("eat...")
                    reslove()
                })
            }
        )
        this.invoke()
        return this;
    }
    work() {
        console.log("push work")
        this.stack.push(
            function () {
                return new Promise(function (reslove) {
                    console.log("work...")
                    reslove()
                })
            }
        )
        this.invoke()
        return this;
    }
    sleep(delay) {
        console.log("push sleep")
        this.stack.push(
            function () {
                return new Promise(function (reslove) {
                    setTimeout(function () {
                        console.log("sleep " + delay + " 秒")
                        reslove()
                    }, delay * 1000)
                })
            }
        )
        this.invoke()
        return this;
    }
}
const c = new Chain("zhangsan");
const d = c.eat().sleep(1).work().sleep(2).eat();
d.sleep(2).work()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容