早期IE下的事件模型与 DOM 标准提供的有些不同。如事件对象,在 IE8 之前并不是通过传递给事件处理器获取的,而是要通过 window.event 获取。373以下代码在 IE8 中,第一个 alert 将会返回 undefined,第二个才会是事件对象。部分事件属性也不同,如标准中的 target 属性,在早期 IE 下需要用 srcElement 替代。建议对兼容性相关内容做个了解即可,框架通常会处理好兼容性问题。
HTML中定义多媒体元素不是很复杂,但是难点在于各种浏览器的兼容性问题,例如:iPhone iPad 不支持 flash低版本 IE 不支持 embed非 IE 浏览器不完全支持 object音视频标签 audio video 仅支持 HTML5 标准多媒体文件格式在不同浏览器的支持程度不同多媒体文件格式在不同的硬件上的支持程序也不同所以说如果你需要大量使用多媒体技术,那么必须要非常熟悉多媒体技术在浏览器上的兼容性问题才行
由于 canvas 属于较新的技术,有些旧版本的浏览器是不支持的,所以我们在开发时就需要考虑到它们的使用场景。<canvas> 容器中可以包含任何 HTML 代码,包含的 HTML 代码即为备选方案。如果当前浏览器不支持 <canvas> 标签(尤其是IE9之前的IE浏览器),则可以使用备选兼容方案,如果当前浏览器支持 <canvas> 标签,则使用 canvas。兼容示例1408运行结果:上面的案例中,如果在支持 canvas 的浏览器中就不会看到 <canvas> 标签包含的内容,如果在不支持 canvas 的浏览器中则会显示提示文字。
通过上图可以看出,基本上兼容性最好的是 Firefox 和Safari ,其他浏览器基本上不兼容。
假设我们现在有一个 n 个活动的集合 S={a1, a2, a3, …an},这些活动需要使用相同一个资源,但是这个资源在某一个时刻只能被一个活动使用,并且每一个活动 ai 都有一个开始时间 si 和结束时间 fi ,其中 si < fi ,并且开始时间和结束时间都在可以选择的活动时间范围内。这里,如果某个活动 ai 被选中,则我们可以说活动 ai 发生在半开时间区间 [ si,fi ) 内,如果两个活动 ai 和 aj 满足 [ si, fi ) 和 [ sj, fj ) 不重叠,则称它们是兼容的。也就是说,若 si >= fj 或者 sj >= fi , 则称 ai 和 aj 是相互兼容的,在活动选择问题中,我们希望选出一个最大兼容活动集。我们考虑现实生活中的一个活动选择问题实例,比如学校里面有一个大的阶梯教室,可以用来上公开课。这里这个阶梯教室就相当于是一个资源,不同的公开课,比如数学课、语文课、英语课等等,这就是一个个活动,并且每节课都有一个开始时间和结束时间,活动选择问题就要求我们选择出所有的可以在阶梯教室中安排的课程,保证选出的课程集合是一个最大的兼容活动集。接下来,就让我们看看如何利用贪心算法解决活动选择问题。
即便 XMLHttpRequest 在绝大多数浏览器都得到支持,但仍有部分对象属性和方法不被少数浏览器所兼容。我们可以通过 Can I use 进行查阅。下面给出一个截至当前为止 XMLHttpRequest 在各浏览器上的兼容性表格进行参照。
前面小节中,介绍了 TypeScript 类型检查机制中的 类型推断 与 类型保护,本节来介绍 类型兼容性。我们学习类型兼容性,就是在学习 TypeScript 在一个类型能否赋值给其他类型的规则。本节将会详细介绍 TypeScript 在函数、枚举、类和泛型中的类型兼容性规则。
目前 Chrome 、 Firefox 、IE 9+ 基本上支持了大部分的 CSS3 特性,我们可以放心的使用了。对于支持性不好的也可以使用前缀。ChromeSafariFirefoxIEOperawebkit--webkit--moz--ms--0-
这个属性不怎么常用,因为它的兼容性不好,只兼容 IE 浏览器,它主要是给对齐属性text-align:justify做一个补充。
首先,在利用动态规划算法之前,我们需要清楚哪些问题适合用动态规划算法求解。一般而言,能够利用动态规划算法求解的问题都会具备以下两点性质:最优子结构: 利用动态规划算法求解问题的第一步就是需要刻画问题最优解的结构,并且如果一个问题的最优解包含其子问题的最优解,则此问题具备最优子结构的性质。因此,判断某个问题是否适合用动态规划算法,需要判断该问题是否具有最优子结构。Tips: 最优子结构的定义主要是在于当前问题的最优解可以从子问题的最优解得出,当子问题满足最优解之后,才可以通过子问题的最优解获得原问题的最优解。重叠子问题: 适合用动态规划算法去求解的最优化问题应该具备的第二个性质是问题的子问题空间必须足够” 小 “,也就是说原问题递归求解时会重复相同的子问题,而不是一直生成新的子问题。如果原问题的递归算法反复求解相同的子问题,我们就称该最优化问题具有重叠子问题。Tips: 在这里,我们需要注意是,与适用动态规划算法去求解的问题具备重叠子问题性质相反,前面我们介绍的分治算法递归解决问题时,问题的子问题都是互不影响,相互独立的,这个也是我们在选用动态规划算法还是分治法解决问题时的一个判断条件。
枚举与数字类型相互兼容:enum Status { Pending, Resolved, Rejected}let current = Status.Pendinglet num = 0current = numnum = current不同枚举类型之间是不兼容的:enum Status { Pending, Resolved, Rejected }enum Color { Red, Blue, Green }let current = Status.Pendingcurrent = Color.Red // Error
通过上述我们可以知道在 Kotlin 中拥有着与 Java 中完全不一样的类型系统。在 Java 中是不存在所谓的可空类型和非空类型。但是我们都知道 Kotlin 与 Java 的互操性很强,几乎是完全兼容 Java。那么Kotlin是如何兼容Java中的变量类型的呢?我们在 Kotlin 中肯定需要经常调用 Java 代码,有的人可能会回答说 Java 中使用@NotNull和@Nullable注解来标识。确实 Kotlin 可以识别多种不同风格的注解,包括 javax.annotation、android.support.annotation、org.jetbrains.annotation等。但是一些之前的第三方库并没有写的这么规范,显然无法通过这种方式完全解决这个问题。所以 Kotlin 引入一种新的概念叫做: 平台类型,平台类型本质上就是Kotlin不知道可空性信息的类型,既可以把它当做可空类型又可以把它当做非空类型。 这就意味要像 Java 代码中一样对在这个类型上做的操作负全部责任。所以对于 Java 中函数参数,Kotlin 去调用的时候系统默认会处理可空类型(为了安全性考虑),如果明确了不为空,可以直接把它修改为非空类型,系统也是不为报编译错误的,但是一旦这样处理了,必须保证不能为空。那么问题来了,很多人就疑问出于安全性考虑为什么不直接全部转化可空类型呢? 实际上这种方案看似可行,实际上有点不妥,对于一些明确不可能为空的变量还需要做大量额外的判空操作就显得冗余。否则非空类型就没有存在的意义了。
假设我们一共有 n 种物品,每种物品 i 的价值为 vi,重量为 wi,我们有一个背包,背包的容量为 c(最多可以放的物品重量不能超过 c),我们需要选择物品放入背包中,使得背包中选择的物品中总价值最大,在这里每个物品可以只选择部分。这便是我们常说的背包问题背包问题是一种常见的可以用贪心算法进行求解的问题,接下来,就让我们看看如何利用贪心算法解决背包问题。
article 用于定义网页中文档、章节、段落相关的文本结构,一个网页中可以定义多个 article 标签。与 span 不同的是,article 具有明确定义的语义。每个 article 中通常包含 header、H1-H6、address 等和文章标题、作者等相关信息的标签。1008注意: article 是 HTML5 新增的元素,可能不兼容低版本的 IE 浏览器。
大部分开发者都会合理、巧妙的运用 this 关键字。初学者容易在 this 指向上犯错,如下面这个 Vue 组件:<div id="app"></div><script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.min.js"></script><script> // 发送post请求 const post = (cb) => { // 假装发了请求并在200ms后返回了服务端响应的内容 setTimeout(function() { cb([ { id: 1, name: '小红', }, { id: 2, name: '小明', } ]); }); }; new Vue({ el: '#app', data: function() { return { list: [], }; }, mounted: function() { this.getList(); }, methods: { getList: function() { post(function(data) { this.list = data; console.log(this); this.log(); // 报错:this.log is not a function }); }, log: function() { console.log('输出一下 list:', this.list); }, }, });</script>这是初学 Vue 的同学经常碰到的问题,为什么这个 this.log() 会抛出异常,打印了 this.list 似乎也是正常的。这其实是因为传递给 post 方法的回调函数,拥有自己的 this,有关内容可以查阅 this章节。不光在这个场景下,其他类似的场景也要注意,在写回调函数的时候,如果在回调函数内要用到 this,就要特别注意一下这个 this 的指向。可以使用 ES6 的箭头函数 或者将需要的 this 赋值给一个变量,再通过作用域链的特性访问即可:<div id="app"></div><script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.min.js"></script><script> // 发送post请求 const post = (cb) => { // 假装发了请求并在200ms后返回了服务端响应的内容 setTimeout(function() { cb([ { id: 1, name: '小红', }, { id: 2, name: '小明', } ]); }); }; new Vue({ el: '#app', data: function() { return { list: [], }; }, mounted: function() { this.getList(); }, methods: { getList: function() { // 传递箭头函数 post((data) => { this.list = data; console.log(this); this.log(); // 报错:this.log is not a function }); // 使用保留 this 的做法 // var _this = this; // post(function(data) { // _this.list = data; // console.log(this); // _this.log(); // 报错:this.log is not a function // }); }, log: function() { console.log('输出一下 list:', this.list); }, }, });</script>这个问题通常初学者都会碰到,之后慢慢就会形成习惯,会非常自然的规避掉这个问题。
首先,这里我们考虑背包的容量为 30,并给出这个问题中我们考虑到的各类物品及对应的重量和价值,如下:物品 n (i)12345 重量 w (i)105151020 价值 v (i)2030152510回顾一下我们在贪心算法介绍中提到的,能够应用贪心算法求解的问题需要满足两个条件:最优子结构和贪心选择,接下来,我们就具体来看看在背包问题中的最优子结构和贪心选择分别是什么。首先,如果一个问题的最优解包含其子问题的最优解,则此问题具备最优子结构的性质。问题的最优子结构性质是该问题是否可以用贪心算法求解的关键所在。对于背包问题,很显然它是满足最优子结构性质的,因为一个容量为 c 的背包问题必然包含容量小于 c 的背包问题的最优解的。其次,我们需要考虑在背包问题中,我们应该按照什么样的贪心选择进行选取。很显然,如果要使得最终的价值最大,那么必定需要使得选择的单位重量的物品的价值最大。所以背包问题的贪心选择策略是:优先选择单位重量价值最大的物品,当这个物品选择完之后,继续选择其他价值最大的物品。这里的背包问题可以用贪心算法实现,是因为背包选择放入的物品可以进行拆分,即并不需要放入整个物品,可以选择放入部分物品,我们这样的背包选择问题为部分背包问题(可以只选择物品的部分),与之对应的另一种背包问题为 0-1 背包问题,这个时候整个物品不可以拆分,只可以选择放入或者不放入,0-1 背包问题用贪心算法并不能求得准确的解,需要用动态规划算法求解。背包问题求解详解:在这里,我们按照优先选择单位重量价值最大的物品,所以第一步需要计算单位每个物品的单位价值,如下:unitValue(1) = 20/10 = 2unitValue(2) = 30/5 = 6unitValue(3) = 15/15 = 1unitValue(4) = 25/10 = 2.5unitValue(5) = 10/20 = 0.5所以我们有:unitValue(2) > unitValue(4) > unitValue(1) > unitValue(3) > unitValue(5)当考虑背包的容量为 30 时, 我们发现可以按照物品的单位价值进行依次放入,直至背包容量放满或者物品放完为止,放入的过程如下:物品类型放入重量背包使用容量背包剩余容量 25525410151511025535300按照如上步骤我们简单分析了一下背包问题的求解过程,接下来,我们看一下如何用代码实现背包问题。
ABA 问题描述:假设有两个线程,线程 1 和线程 2,线程 1 工作时间需要 10 秒,线程 2 工作需要 2 秒;主内存值为 A,第一轮线程 1 和线程 2 都把 A 拿到自己的工作内存;第 2 秒,线程 2 开始执行,线程 2 工作完成把 A 改成了 B ;第 4 秒,线程 2 把 B 又改成了 A,然后就线程 2 进入休眠状态;第 10 秒,线程 1 工作完成,看到期望为 A 真实值也是 A 认为没有人动过,其实 A 已经经过了修改,只不过又改了回去,然后线程 1 进行 CAS 操作。ABA 问题解决:为了解决这个问题,在每次进行操作的时候加上一个版本号或者是时间戳即可。
要使用这个属性一定要先设置 text-align:justify;且仅仅兼容 IE 浏览器。
在我们之前的学习之中,我们或多或少都会遇到一些训练时间的问题。比如“训练时间越长是不是最后的结果就会越好?”等问题。答案当然是否定的,这是因为在训练的过程之中会遭遇到“过拟合”的问题,这是一种随着训练时间不断加长而产生的问题,那么这节课我们就来学习一下什么是过拟合,同时了解一下 TensorFlow 之中的避免过拟合的简单的方法。这节课之中,我们使用之前学习过的猫狗分类的例子进行示例演示。
在我们搭建好 RabbitMQ 集群之后,无论我们采用哪种搭建方式,我们本身都需要知道一些集群中容易出现的问题,这样我们在遇到集群出现问题的时候,我们才不会那么慌张。问题一:集群宕机集群宕机这一问题,是无论搭建什么样的集群,都是比较容易出现的一种问题,这种问题出现的频率高,同时,也是最容易修复的一种问题。那么什么是集群宕机呢?集群宕机其实指的就是我们的 RabbitMQ 集群,所在的服务器节点,由于种种原因导致我们的 RabbitMQ Server 服务非自然停止,或意外退出的一种现象。如果我们的 RabbitMQ Server 宕机了,就表明,我们的 RabbitMQ 集群中有一个或多个 RabbitMQ Server 服务节点不能够再继续提供服务了,那么所有有关 RabbitMQ Server 服务的请求都要在正常运行的服务节点上进行处理,可想而知,这种情况下,RabbitMQ 服务器所承受的压力有多大。上述是集群宕机之后所造成的一个直接结果,还有一种间接结果也是非常致命的,那就是由于集群节点服务的宕机,用户请求只能在可用的集群节点上进行处理,如果这个节点的服务器配置较低,那么很可能由于处理请求数量的激增,导致这个节点的服务器直接崩掉,也就是直接关机,或者服务器不再返回任何响应,这种情况也是比较严重的。问题二:集群间通信延迟集群间通信延迟也是比较容易出现的一种问题,这种问题我们从字面意思上来看,基本上可以知道个大概。集群间通信延迟问题,指的就是,RabbitMQ 集群中的一个节点向另一个节点传递数据,或者从另一个节点中获取数据时,目标节点不能及时将所需数据进行返回,导致的请求节点出现长时间等待的一种现象。那么集群间通信延迟问题造成的直接影响就是,用户的数据不能同步更新,导致用户看到的数据是不准确的,这极大影响了用户的使用体验。集群间通信延迟问题造成的间接影响就是,如果我们的目标节点迟迟不能返回响应数据,就会导致请求节点一直等待,那么位于请求节点上后续的用户请求就不能得到处理,这就是请求阻塞现象。问题三:集群数据文件丢失集群数据文件丢失问题相对而言,出现的频率较低,但是我们应该也要进行简单的了解。集群数据文件丢失问题,指的就是,位于服务器上的 RabbitMQ Server 节点突然出现的一种服务器磁盘数据丢失现象, 这种问题不出现还好,一旦出现,一般都是致命性的问题,往往恢复的时间也是最长的。集群数据文件丢失问题的出现,会直接影响用户无法打开我们的项目或应用,给用户造成非常严重的体验问题,同时,也会间接导致用户关键数据的丢失,造成我们的项目或者应用用户的一个流失。以上三种问题类型,是 RabbitMQ 集群中比较常见的三种问题类型,无论出现哪种问题,我们都应该知道一个大概的解决措施,或者恢复方案,下面让我们先来分析一下产生这三种问题的常见原因。
定位 MySQL 的问题 SQL,主要有两种方法,查看当前线程(show processlist)和慢日志。一般来说,当前发生的问题用到 show processlit,事后分析用到慢日志。
Hibernate 只是解决 JDBC 的问题,在项目中,不可避免的需要和其它框架进行整合。如 Spring 之流,这里面会有一个很大的坑,就是版本兼容问题。典型的表现就是明明添加了相应的包,但是,出现 class not found。这时就要考虑是否是版本之间的兼容问题。这个坑有的时候很让人迷惑,明明刚刚添加了相应的包,却告知不存在相应的类。Spring Boot 提供了 DATA JPA 实现,本质上是使用 Hibernate 的持久化功能。Spring Boot 开箱即用,很好解决了版本的兼容性问题。建议大家使用。
虽然 Web Components 还存在兼容性问题,但可以作为预备知识进行了解,或者应用于一些内部对兼容性无要求的项目。
谈到线程安全问题,我们先说说什么是共享资源。共享资源:所谓共享资源,就是说该资源被多个线程所持有或者说多个线程都可以去访问该资源。线程安全问题是指当多个线程同时读写一个共享资源并且没有任何同步措施时,导致出现脏数据或者其他不可预见的结果和问题。对于线程安全问题,在进行实际的开发操作过程中,我们要分析一下几点内容,确保多线程环境下的线程安全问题。确定是否是多线程环境:多线程环境下操作共享变量需要考虑线程的安全性;确定是否有增删改操作:多线程环境下,如果对共享数据有增加,删除或者修改的操作,需要谨慎。为了保证线程的同步性,必须对该共享数据进行加锁操作,保证多线程环境下,所有的线程能够获取到正确的数据。如生产者与消费者模型,售票模型;多线程下的读操作:如果是只读操作,对共享数据不需要进行锁操作,因为数据本身未发生增删改操作,不会影响获取数据的准确性。
泛型的类型兼容性根据其是否被成员使用而不同。先看一段代码示例:interface Empty<T> {}let x: Empty<number>let y: Empty<string>x = y! // OK上面代码里,x 和 y 是兼容的,因为它们的结构使用类型参数时并没有什么不同。但是当泛型被成员使用时:interface NotEmpty<T> { data: T}let x: NotEmpty<number>let y: NotEmpty<string>x = y! // Error代码解释: 因为第 4 行,泛型参数是 number 类型,第 5 行,泛型参数是 string 类型,所以最后一行赋值失败。如果没有指定泛型类型的泛型参数,会把所有泛型参数当成 any 类型比较:let identity = function<T>(x: T): void { // ...}let reverse = function<U>(y: U): void { // ...}identity = reverse // OK
兼容性问题是网站技术中老生常谈的问题,包括 HTML、JavaScript、CSS 都会出现兼容性问题。导致这个问题的根本原因是不同的浏览器厂商的内核不同,导致对 CSS 的解析效果不一致,继而显示效果千差万别。这里不去过分讨论内核不兼容的深层次原理,而是讨论一下大的解决思路,主要包括 4 个方面,浏览器 CSS 样式初始化、浏览器私有属性,CSS hack 语法和第三方插件。
本章节主要介绍了几个日常开发过程中经常碰到的问题,可能每个人碰到的不一样,这里仅供参考。如果大家有碰到其他问题可以上网查阅相关资料。
本小节我们主要学习了利用 ellipse 方法绘制一个椭圆。由于兼容性的问题,我们在后面的小节中还会学到兼容性更好的绘制椭圆的方法。
在工作中经常会遇到 var、let 及 const 以下几个问题:什么是变量提升?什么是暂时性死区?var、let 及 const 区别?这些问题在上面的讲解中都有提到过,这里我们总结一下:
类与对象字面量和接口的兼容性非常类似,但是类分实例部分和静态部分。比较两个类类型数据时,只有实例成员会被比较,静态成员和构造函数不会比较。class Animal { feet!: number constructor(name: string, numFeet: number) { }}class Size { feet!: number constructor(numFeet: number) { }}let a: Animallet s: Sizea = s! // OKs = a // OK代码解释: 类 Animal 和类 Size 有相同的实例成员 feat 属性,且类型相同,构造函数参数虽然不同,但构造函数不参与两个类类型比较,所以最后两行可以相互赋值。类的私有成员和受保护成员会影响兼容性。 允许子类赋值给父类,但是不能赋值给其它有同样类型的类。class Animal { protected feet!: number constructor(name: string, numFeet: number) { }}class Dog extends Animal {}let a: Animallet d: Doga = d! // OKd = a // OKclass Size { feet!: number constructor(numFeet: number) { }}let s: Sizea = s! // Error代码解释:第 13 行,子类可以赋值给父类。第 14 行,父类之所以能够给赋值给子类,是因为子类中没有成员。最后一行,因为类 Animal 中的成员 feet 是受保护的,所以不能赋值成功。