问题先导
-
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
用来表示文档根,以及link
、script
、style
等特殊信息标签。
除了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);
};
关键字:广度优先搜索、深度优先搜索
网友评论