美文网首页
前端面试题集每日一练Day4

前端面试题集每日一练Day4

作者: 一颗脑袋 | 来源:发表于2021-05-19 01:13 被阅读0次

    问题先导

    • meta标签的常见用法有哪些?

    • 隐藏元素有哪些方法?【css

    • 请编写一个和运算符instanceof相同功能的函数

    • Proxy代理对象相较于Object.defineProperty有哪些优势?

    • 路径总和

      给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和 targetSum 。
      输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
      输出:true
      

    知识梳理

    meta标签有哪些常见用法?

    meta的含义为"自身的,本身的",在HTML中用于表示文档级的元数据信息。我们可以使用meta存储文档的任何元数据信息,当然,有些文档信息有专门的标签,比如title用来表示文档标题,base用来表示文档根,以及linkscriptstyle等特殊信息标签。

    除了HTTP提供的了一些name作为使用的共识,开发者还可以自定义name。常见的meta用法有:

    • charset,设定文档编码类型

      <meta chartset="UTF-8" >
      
    • keyweods,页面关键字

      <meta name="keywords" content="news" >
      
    • description,页面描述信息

      <meta name="description" content="这是一篇关于meta的文章"
      
    • http-equiv,提供了 content 属性的信息/值的 HTTP 头。http-equiv 属性可用于模拟一个 HTTP 响应头。

      描述
      content-type 规定文档的字符编码。实例:<meta http-equiv="content-type" content="text/html; charset=UTF-8">
      default-style 规定要使用的预定义的样式表。实例:<meta http-equiv="default-style" content="the document's preferred stylesheet">
      refresh 定义文档自动刷新的时间间隔。实例:<meta http-equiv="refresh" content="300">
    • viewport,移动端适配,控制视口大小和比例

      <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" >
      

      其中,content参数有以下几种:

      • width :宽度(数值/device-width)
      • height:高度(数值/device-height)
      • initial-scale :初始缩放比例
      • maximum-scale :最大缩放比例
      • minimum-scale :最小缩放比例
      • user-scalable :是否允许用户缩放(yes/no)

    关键字:html

    隐藏元素有哪些方法?

    最常见的当然是display: none;visibility: hidden;,此外,还有一些“骚操作”让元素“看不见”,也就达到了隐藏的效果,总结如下:

    • display: none:将元素移出文档流,真正意义上的隐藏元素。
    • visibility: hidden:也是真实的隐藏,但元素仍占据在文档流中,但不会响应事件。
    • opacity: 0:将元素透明度设置为0,所以事件还是会响应。
    • position: absolution:绝对定位让元素脱离文档流,然后让其定位到视窗外来达到隐藏的效果。
    • z-index:设置元素z轴将元素置于相对底部,通过上层元素的遮挡来实现隐藏效果。
    • transform: scale(0, 0):将元素缩放为0,效果和visibility: hidden一样,元素仍占据在文档流中,但不会响应事件。
    • clip-path:使用元素裁剪的方法来实现元素的隐藏,这种方法下,效果和visibility: hidden一样,元素仍占据在文档流中,被裁剪出去的隐藏部分不会响应事件。

    关键字:css基础

    请编写一个和运算符instanceof相同功能的函数

    首先我们要知道,instanceof 运算符的功能描述:用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。也就是我们常说的判断对象是否是某个函数的实例。

    所以写出instanceof功能的关键点在于正确遍历原型链。原型链是js语言的基础设计,js的函数Function有一个固定的属性prototype,我们一般称之为显式原型,因为一般来说,函数是开发自定义的,也是可以编辑的,因此对开发而言是“显式”的。当我们new Function之后就可以得到一个实例对象(object),实例对象的相关属性就会来自我们定义好的Function,这样,实例的父函数的显式原型prototype也会被继承下去,但在实例中,我们需要换一个名字:__proto__来表示,由于是继承而来的,所以我们称之为隐式原型,总结来说就是:

    子实例的隐式原型 = 父类的显示原型,这样,父类和子类就通过显示原型和隐式原型互相连接起来,我们也称之为原型链。

    比如:mikky是一只小猫,它的父类继承关系是这样的: mikky -> Cat -> Animal -> Object。那么,在js中,mikky的原型链就是这样的:

    mikky.__proto__ = Cat.prototype;
    Cat.prototype.__proto__ = Animal.prototype;
    Animal.prototype.__proto__ = Object.prototype;
    

    结合子实例的隐式原型 = 父类的显示原型这句话,我们知道(new Animal()).__proto__ = Animation.prototype,再根据第二句代码,就得到Cat.prototype = New Animal(),所以可以把子类理解为父类的一个实例,这也是继承实现的一种方法:让子类的显示原型等于父类的一个实例即可。

    这样,通过作用链的层层查找,我们就能判断某个函数是否出现在某个实例的原型链上。在js中Object对象为上帝类,也是作用域链的结束标记。

    当调用实例的属性或方法时,也是在原型链层层往上查找,找到了就用作用域链上的属性或方法,找不到才会报错或返回undefined,这也是js继承的本质。

    值得注意的是,隐式原型链__proto__是非标准的,不推荐直接这么调用,而是通过Object.getPrototypeOf(obj)来获取。

    根据上面的分析,我们就可以写出instanceof的判断功能函数:

    /**
     * 判断实例obj是否为构造器constructor的实例
     * (判断构造器的原型是否在实例obj的原型链上)
     */
    function instanceOf(obj, constructor) {
        if(!obj || !constructor) {
            return false;
        }
        const basePrototype = constructor.prototype; // 构造函数原型
        let prototype_link_next = Object.getPrototypeOf(obj); // 同obj.__proto__
        while(prototype_link_next) {
            if(B_proto === prototype_link_next) {
                return true;
            }
            prototype_link_next = Object.getPrototypeOf(prototype_link_next);
        }
        return false;
    }
    

    更多原型链细节请参考:继承与原型链 - MDN

    关键字:js数据类型、原型链

    Proxy代理对象相较于Object.defineProperty有哪些优势?

    我们知道Vue3.0已经使用Proxy代理对象进行数据劫持,说明其存在一定的优势。

    Objec.defineProperty虽然可以劫持属性的操作,但对方法的调用却无法劫持,比如数组的push、下标取值、下标赋值等操作,Vue只能通过重写原型链的这些方法才能实现数据响应效果,而且无法捕获到属性的删除和新增变化。

    Proxy解决了上述缺陷,除了对属性操作进行捕获,很多方法的调用都实现了捕获器,目前看来唯一的缺点可能就是兼容性问题了。

    更多细节请参考:

    关键字:Vue基础

    路径总和

    给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。

    输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
    输出:true
    

    对于在二叉树数据结构搜索,一般都可以使用广度优先搜索(Breadth-First Search,BFS)和深度优先搜索(Depth-First Search,DFS)两种算法。

    方法一:广度优先搜索

    题目的要求是寻找路径总和是否存在目标值,从广度优先的角度,我们进行逐层累加并统计各个路径的和,直到找到符合的值并且为叶子节点时结束遍历。

    广度优先的基本逻辑就是一层一层遍历,我们要搜索的对象是路径总和,所以我们需要在层级遍历时记录根节点到每个结点的和,遇到叶子节点时进行路径和与目标值进行比较。

    至于如何保存结点与根结点到该结点的路径和,可以使用两个队列,一个队列存储结点,另一个队列存储路径总和,两个队里要保证一一对应(结点与总和一一对应),也就是入队和出队要保持同步进行:

    • 入队:队列1记录结点a;队列2记录结点a的值加上当前路径的总和
    • 出队:队列1取出结点a;队列2取出结点a对应的路径和
    • 入队 + 出队循环:a的孩子结点入队1,孩子结点的值加上出队的路径和再入队,然后再出队。

    如此往复,当结点为叶子节点是进行搜索判断,否则继续同步入队、出队操作,直到队列为空。

    参考代码:

    /**
     * Definition for a binary tree node.
     * function TreeNode(val, left, right) {
     *     this.val = (val===undefined ? 0 : val)
     *     this.left = (left===undefined ? null : left)
     *     this.right = (right===undefined ? null : right)
     * }
     */
    /**
     * @param {TreeNode} root
     * @param {number} targetSum
     * @return {boolean}
     */
    var hasPathSum = function(root, targetSum) {
        // 初始化两个队列,分别用于存储结点和结点路径和
        if(!root){
            return false;
        }
        // 根结点入队
        const arr_node = [root];
        const arr_sum = [root.val];
        while(arr_node.length > 0) {
            // 出队
            const node = arr_node.shift();
            const sum = arr_sum.shift();
            // 入队
            const left = node.left;
            const right = node.right;
            if(!left && !right && sum === targetSum) {
                return true;
            }
            if(left) {
                arr_node.push(left);
                arr_sum.push(sum + left.val);
            }
            if(right) {
                arr_node.push(right);
                arr_sum.push(sum + right.val);
            }
        }
        return false;
    };
    

    方法二:深度优先搜索

    深度优先搜索是一条路径走到底,如果符合条件就结束。深度优先一般可以通过迭代来实现,刚好本题也支持这种实现:

    var hasPathSum = function(root, targetSum) {
        if(!root) {
            return false;
        }
        if(!root.left && !root.right && targetSum == root.val) {
            return true;
        }
        return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);
    };
    

    关键字:广度优先搜索、深度优先搜索

    相关文章

      网友评论

          本文标题:前端面试题集每日一练Day4

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