1、Algorithm 一道算法题
给你两棵二叉树
root和subRoot。检验root中是否包含和subRoot具有相同结构和节点值的子树。如果存在,返回true;否则,返回false。 二叉树tree的一棵子树包括tree的某个节点和这个节点的所有后代节点。tree也可以看做它自身的一棵子树。
这道题是树的递归。遍历树时采用深度优先遍历,即先访问根节点,再访问左节点,最后访问右节点。 由于子树可以是 tree 的某个节点和这个节点的所有后代节点,因此如果 root 树的根节点和 subRoot 树的根节点不同时,可以进而判断 root 树的左节点和 subRoot 树的根节点,仍然不相同,则判断 root 树的右节点和 subRoot 树的右节点。
/**
* 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 {TreeNode} subRoot
* @return {boolean}
*/
var isSubtree = function(root, subRoot) {
if(!root){
return false;
}
const result= isTreeNodeEqual(root,subRoot);
if(result){
return true;
}
return isSubtree(root.left,subRoot) || isSubtree(root.right,subRoot)
}
function isTreeNodeEqual(root,subRoot){
// 两者皆为空,则相等
if(!root && !subRoot){
return true;
}
// 两者之一不为空,则不相等
if(!root || !subRoot){
return false;
}
return isTreeNodeEqual(root.left,subRoot.left) && isTreeNodeEqual(root.right,subRoot.right);
}
2、Review 读一篇英文文章
Understanding Classes in JavaScript:理解 JavaScript 中的类
JavaScript is a prototype-based language, and every object in JavaScript has a hidden internal property called [[Prototype]] that can be used to extend object properties and methods. You can read more about prototypes in our Understanding Prototypes and Inheritance in JavaScript tutorial. Until recently, industrious developers used constructor functions to mimic an object-oriented design pattern in JavaScript. The language specification ECMAScript 2015, often referred to as ES6, introduced classes to the JavaScript language. Classes in JavaScript do not actually offer additional functionality, and are often described as providing “syntactical sugar” over prototypes and inheritance in that they offer a cleaner and more elegant syntax. Because other programming languages use classes, the class syntax in JavaScript makes it more straightforward for developers to move between languages.
JavaScript 是一种基于原型的语言,JavaScript 中的每个对象都有一个隐藏的内部属性称为 [[Prototype]],它可以用于扩展对象的属性和方法。你可以在我们的《理解 JavaScript 中的原型和继承》教程中了解更多关于原型的内容。
直到最近,勤奋的开发者使用构造函数来模拟 JavaScript 中的面向对象设计模式。ECMAScript 2015(通常称为 ES6)的语言规范引入了类到 JavaScript 语言中。JavaScript 中的类实际上并没有提供额外的功能,通常被描述为在原型和继承上提供了更干净和更优雅的语法糖。因为其他编程语言使用类,JavaScript 中的类语法使开发者在不同语言之间更容易切换。
A JavaScript class is a type of function. Classes are declared with the
classkeyword. We will use function expression syntax to initialize a function and class expression syntax to initialize a class.
JavaScript 类是一种函数类型。类通过使用 class 关键字声明。我们将使用函数表达式语法来初始化一个函数,并使用类表达式语法来初始化一个类。
// Initializing a function with a function expression
const x = function() {}
// Initializing a class with a class expression
const y = class {}
We can access the
[[Prototype]]of an object using theObject.getPrototypeOf()method. Let’s use that to test the empty function we created.
我们可以使用 Object.getPrototypeOf() 方法来访问对象的 [[Prototype]]。让我们使用它来测试我们创建的空函数。
Object.getPrototypeOf(x);
Output
ƒ () { [native code] }
We can also use that method on the class we just created.
我们也可以在刚刚创建的类上使用该方法。
Object.getPrototypeOf(y);
Output
ƒ () { [native code] }
The code declared with
functionandclassboth return a function[[Prototype]]. With prototypes, any function can become a constructor instance using thenewkeyword.
使用函数和类声明的代码都返回一个函数的 [[Prototype]]。通过原型,任何函数都可以使用 new 关键字成为一个构造函数的实例。
const x = function() {}
// Initialize a constructor from a function
const constructorFromFunction = new x();
console.log(constructorFromFunction);
Output
x {}
constructor: ƒ ()
This applies to classes as well.
这同样适用于类。
const y = class {}
// Initialize a constructor from a class
const constructorFromClass = new y();
console.log(constructorFromClass);
Output
y {}
constructor: class
These prototype constructor examples are otherwise empty, but we can see how underneath the syntax, both methods are achieving the same end result.
这些原型构造函数的示例本身是空的,但我们可以看到在语法下,这两种方法都实现了相同的最终结果。
In the prototypes and inheritance tutorial, we created an example based around character creation in a text-based role-playing game. Let’s continue with that example here to update the syntax from functions to classes.
在原型和继承教程中,我们创建了一个基于文本角色扮演游戏中角色创建的示例。让我们继续使用该示例,将语法从函数更新为类。
A constructor function is initialized with a number of parameters, which would be assigned as properties of
this, referring to the function itself. The first letter of the identifier would be capitalized by convention.
构造函数通过一些参数进行初始化,这些参数将被分配为 this 的属性,指的是函数本身。按照惯例,标识符的首字母应大写。
constructor.js
// Initializing a constructor function
function Hero(name, level) {
this.name = name;
this.level = level;
}
When we translate this to the class syntax, shown below, we see that it is structured very similarly.
当我们将其转换为下面所示的类语法时,我们可以看到它的结构非常相似。
// Initializing a class definition
class Hero {
constructor(name, level) {
this.name = name;
this.level = level;
}
}
We know a constructor function is meant to be an object blueprint by the capitalization of the first letter of the initializer (which is optional) and through familiarity with the syntax. The
classkeyword communicates in a more straightforward fashion the objective of our function.
通过初始化器的首字母大写(这是可选的)和对语法的熟悉,我们知道构造函数是用来创建对象的蓝图。而 class 关键字以更直接的方式传达了我们函数的目标。
The only difference in the syntax of the initialization is using the
classkeyword instead offunction, and assigning the properties inside aconstructor()method.
在初始化语法中,唯一的区别是使用 class 关键字而不是 function,并在 constructor() 方法内部分配属性。
The common practice with constructor functions is to assign methods directly to the
prototypeinstead of in the initialization, as seen in thegreet()method below.
在构造函数中,常见的做法是将方法直接分配给原型,而不是在初始化中进行,就像下面的 greet() 方法一样。
function Hero(name, level) {
this.name = name;
this.level = level;
}
// Adding a method to the constructor
Hero.prototype.greet = function() {
return `${this.name} says hello.`;
}
With classes this syntax is simplified, and the method can be added directly to the class. Using the method definition shorthand introduced in ES6, defining a method is an even more concise process.
使用类,这种语法被简化了,方法可以直接添加到类中。使用 ES6 引入的方法定义简写,定义一个方法变得更加简洁。
class Hero {
constructor(name, level) {
this.name = name;
this.level = level;
}
// Adding a method to the constructor
greet() {
return `${this.name} says hello.`;
}
}
Let’s take a look at these properties and methods in action. We will create a new instance of
Herousing thenewkeyword, and assign some values.
让我们看看这些属性和方法的实际应用。我们将使用 new 关键字创建一个 Hero 的新实例,并分配一些值。
const hero1 = new Hero('Varg', 1);
If we print out more information about our new object with
console.log(hero1), we can see more details about what is happening with the class initialization.
如果我们使用 console.log(hero1) 打印出关于我们的新对象的更多信息,我们可以看到有关类初始化的更多细节。
Output
Hero {name: "Varg", level: 1}
__proto__:
▶ constructor: class Hero
▶ greet: ƒ greet()
We can see in the output that the
constructor()andgreet()functions were applied to the__proto__, or[[Prototype]]ofhero1, and not directly as a method on thehero1object. While this is clear when making constructor functions, it is not obvious while creating classes. Classes allow for a more simple and succinct syntax, but sacrifice some clarity in the process.
我们可以在输出中看到,constructor() 和 greet() 函数被应用于 hero1 的 proto,或者说 [[Prototype]],而不是直接作为 hero1 对象的方法。虽然在创建构造函数时这一点很明显,但在创建类时并不明显。类允许使用更简单和简洁的语法,但在这个过程中会牺牲一些清晰度。
An advantageous feature of constructor functions and classes is that they can be extended into new object blueprints based off of the parent. This prevents repetition of code for objects that are similar but need some additional or more specific features.
构造函数和类的一个有利特性是它们可以基于父类扩展为新的对象蓝图。这样可以避免为相似但需要一些额外或更具体功能的对象重复编写代码。
New constructor functions can be created from the parent using the
call()method. In the example below, we will create a more specific character class calledMage, and assign the properties ofHeroto it usingcall(), as well as adding an additional property.
可以使用 call() 方法从父类创建新的构造函数。在下面的示例中,我们将创建一个更具体的角色类 Mage,并使用 call() 将 Hero 的属性分配给它,同时添加一个额外的属性。
// Creating a new constructor from the parent
function Mage(name, level, spell) {
// Chain constructor with call
Hero.call(this, name, level);
this.spell = spell;
}
At this point, we can create a new instance of
Mageusing the same properties asHeroas well as a new one we added.
此时,我们可以使用与 Hero 相同的属性以及我们添加的新属性来创建 Mage 的新实例。
const hero2 = new Mage('Lejon', 2, 'Magic Missile');
Sending
hero2to the console, we can see we have created a newMagebased off the constructor.
将 hero2 发送到控制台,我们可以看到我们已经基于构造函数创建了一个新的 Mage。
Output
Mage {name: "Lejon", level: 2, spell: "Magic Missile"}
__proto__:
▶ constructor: ƒ Mage(name, level, spell)
With ES6 classes, the
superkeyword is used in place ofcallto access the parent functions. We will useextendsto refer to the parent class.
在 ES6 类中,super 关键字用于替代 call 来访问父类函数。我们将使用 extends 来引用父类。
// Creating a new class from the parent
class Mage extends Hero {
constructor(name, level, spell) {
// Chain constructor with super
super(name, level);
// Add a new property
this.spell = spell;
}
}
Now we can create a new
Mageinstance in the same manner.
现在我们可以以相同的方式创建一个新的 Mage 实例。
const hero2 = new Mage('Lejon', 2, 'Magic Missile');
We will print
hero2to the console and view the output.
我们将把 hero2 打印到控制台并查看输出。
Output
Mage {name: "Lejon", level: 2, spell: "Magic Missile"}
__proto__: Hero
▶ constructor: class Mage
The output is nearly exactly the same, except that in the class construction the
[[Prototype]]is linked to the parent, in this caseHero.
输出几乎完全相同,只是在类构造中,[[Prototype]] 与父类 Hero 相关联。
Below is a side-by-side comparison of the entire process of initialization, adding methods, and inheritance of a constructor function and a class.
下面是构造函数和类的整个初始化、添加方法和继承过程的并排比较。
function Hero(name, level) {
this.name = name;
this.level = level;
}
// Adding a method to the constructor
Hero.prototype.greet = function() {
return `${this.name} says hello.`;
}
// Creating a new constructor from the parent
function Mage(name, level, spell) {
// Chain constructor with call
Hero.call(this, name, level);
this.spell = spell;
}
// Initializing a class
class Hero {
constructor(name, level) {
this.name = name;
this.level = level;
}
// Adding a method to the constructor
greet() {
return `${this.name} says hello.`;
}
}
// Creating a new class from the parent
class Mage extends Hero {
constructor(name, level, spell) {
// Chain constructor with super
super(name, level);
// Add a new property
this.spell = spell;
}
}
Although the syntax is quite different, the underlying result is nearly the same between both methods. Classes give us a more concise way of creating object blueprints, and constructor functions describe more accurately what is happening under the hood.
尽管语法有很大的不同,但两种方法的基本结果几乎相同。类提供了一种更简洁的方式来创建对象的蓝图,而构造函数则更准确地描述了底层发生的情况。
In this tutorial, we learned about the similarities and differences between JavaScript constructor functions and ES6 classes. Both classes and constructors imitate an object-oriented inheritance model to JavaScript, which is a prototype-based inheritance language.
在本教程中,我们学习了 JavaScript 构造函数和 ES6 类之间的相似性和差异。类和构造函数都模拟了 JavaScript 中的面向对象继承模型,这是一种基于原型的继承语言。
Understanding prototypical inheritance is paramount to being an effective JavaScript developer. Being familiar with classes is extremely helpful, as popular JavaScript libraries such as React make frequent use of the
classsyntax.
理解原型继承对于成为一名高效的 JavaScript 开发人员至关重要。熟悉类的使用非常有帮助,因为流行的 JavaScript 库(如 React)经常使用类语法。
3、Technique/Tips 分享一个小技术
利用替换元素的::before和::after伪元素是失效的,可以实现“基于伪元素的图片内容生成技术”,在图片还没加载时把 alt 信息呈现出来。 由于图片加载较慢,在图片未加载时,不为 img 设置 src 属性,而是设置一个 data-src 属性,并给 img 元素设置伪元素 ::after 将 alt 属性值通过 content 属性呈现出来。待图片加载完成后,将 img 的 src 属性替换为 data-src 的值,img 元素由普通元素变成替换元素,此时的 ::after 伪元素失效,不再展示 alt 信息。 关键 CSS 代码如下:
img::after {
/* 黑色alt信息条 */
content: attr(alt);
position: absolute;
left: 0; bottom: 0;
width: 100%;
line-height: 30px;
background-color: rgba(0,0,0,.5);
color: white;
font-size: 14px;
transform: translateY(100%);
/* 来点过渡动画效果 */
transition: transform .2s;
visibility: visible;
}
img:hover::after {
transform: translateY(0);
}
-- from:张鑫旭
4、Share 分享一个观点
在这个世界上,只有一个人有能力把你打倒 —— 这个人就是你自己,无论其他人对你怎么进行消极的评价,最终都是你自己的想法打倒了你自己。如果你并不在乎其他人的想法,那么没有人可以对你造成影响。
当然了,我们不能做一个偏执的人,别人的批评和评价有可能是对我们有益的,那我们需要去倾听这些声音。只不过,我们要学会一套甄别方法,把不正确的评价和批评剔除,以免造成对我们自身的伤害和负面影响。
首先,我们需要了解十种认知扭曲形式,我们必须非常熟悉这些扭曲的思想,因为熟悉它们可以让我们快速判断不正确的思想路径:
1、要么一切要么全无思想。
以黑白分明的范畴来看待事物,如果你的表现不够完美,你就会认为自己彻底失败。
2、过于概括。
你把一个孤立的消极事件看做是一个永远会持续下去的失败模式。
3、心灵过滤。
你选择一段消极细节,反复思考这段细节。结果,在你眼里,整个现实都变得黑暗起来,就像一滴墨水染黑了整杯水一样。
4、贬损积极的东西。
你拒绝承认积极的经验,你会找这样那样的理由仍为它们“不算数”。这样你就可以坚持和你日常经理相矛盾的消息信念了。
5、跳跃式结论。
即便没有确定的事实令人信服地支持你的结论,你也会对事情作出一个消息的解释。
5a、测心术。
你武断地认为别人对你作出消极的反映,你甚至不愿花工夫去检验一下。
5b、先知错误。
你预期事情会变糟,而且你坚信这个预言是一个已经成立的事实。
6、夸大和缩小。
你夸大了事情的重要性(比如你弄糟了的事情或者别人的成绩),或者不合适地缩小了事情,直到它们显地很小(你个人的优良品质或者别人的不足)。这种扭曲又被称作“双目镜把戏”。
7、情绪推理。
你假定自己的消极情绪必然反映了事情的真实状况:“我这么感觉,所以它肯定是真的。”
8、应该陈述。
你试图用应该或不应该来激发自己,就好像在期望你做什么事之前应该先鞭笞你或惩罚你一样。“必须”和“本该”同样也是罪魁祸首。这种情绪的结果是一种负罪感。当你用应该陈述来要求别人时,你会体会到愤怒、灰心和怨恨。
9、贴标签与标签不当。
贴标签时过于概括的一种极端形式。你不再描述你的错误,而是为你自己贴上一个消极的标签:“我是一个失败者”。当别人的行为以一种不当的方式与你发生关系时,你也会给他贴上一个标签:“他是一个该死的讨厌鬼”。标签不当是指使用高度主观的语言或高度情绪化的语言来描述一件事情。
10、归己化。
你会把自己看作是许多外界消极事件的原因,事实上你并不应该为这些事负主要责任。