前端面试题集每日一练Day4

2021-05-19  本文已影响0人  一颗脑袋

问题先导

知识梳理

meta标签有哪些常见用法?

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

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

关键字:html

隐藏元素有哪些方法?

最常见的当然是display: none;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)两种算法。

方法一:广度优先搜索

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

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

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

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

参考代码:

/**
 * 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);
};

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

上一篇 下一篇

猜你喜欢

热点阅读