Java 是一门不断发展的语言,这是一件好事。然而,其他语言的一些特性也是值得研究的。语言的结构是人们思考问题的方式,也是人们设计解决方案的方式。学习或至少熟悉其他语言是借鉴其设计的好方法。
Java 是我学习的第一门语言并且是我专业使用的语言。它是我大约十五年以来的主要谋生手段。然而,它并不是我多年来学习和使用的唯一语言:例如,很久以前,我必须开发 JavaScript 代码来实现动态用户界面。当时,它被称为 Dhtml ……几年前,我还自学了 Kotlin,并且从未停止过使用它。去年,在一家新公司工作时,我尝试了 Clojure,但没有成功。
在上述所有场景中,Java 仍然是我学习和评判其他语言的基准。以下是一些有趣的语言特性,我认为这些特性对于来自 Java 背景的人都颇具思想挑战性。
JavaScript 是我和 Java 一起使用的第一种语言。尽管 JavaScript 已经发展这么多年了,但它有一个实现起来非常奇怪的常见特性:新对象的实例化。
在 Java 中,首先创建要一个 类 :
publicclassPerson{
privatefinalStringname;
privatefinal LocalDate birthdate;
publicPerson(Stringname, LocalDate birthdate){
this.name = name;
this.birthdate = birthdate;
}
publicStringgetName(){
returnname;
}
publicLocalDategetBirthdate(){
returnbirthdate;
}
}
然后,就可以继续创建该类的 实例 了:
varperson1 =newPerson("John Doe", LocalDate.now());
varperson2 =newPerson("Jane Doe", LocalDate.now());
JavaScript 与 Java 的语法非常相似:
classPerson{
constructor(name, birthdate) {
this.name = name;
this.birthdate = birthdate;
}
}
letperson1 =newPerson("John Doe",Date.now());
letperson2 =newPerson("Jane Doe",Date.now());
相似之处到此为止。由于 JavaScript 具有动态特性,所以可以向现有实例中添加属性和函数。
person1.debug=function(){
console.debug(this);
}
person1.debug();
但是,这些只能添加到某个实例中。其他实例会缺少这些补充属性或函数:
person2.debug();// Throws TypeError: person2.debug is not a function
要将函数(或属性)添加到 所有实例 (无论是现在的还是将来的)中,都需要利用 原型 的概念:
Person.prototype.debug= function() {
console.debug(this);
}
person1.debug();
person2.debug();
let person3 = new Person("Nicolas", Date.now());
person3.debug();
几年前,我开始尝试着自学 Android。我发现这种体验对开发人员来说不太友好:当然,我了解它其中一个目标是尽可能减少内存占用,但这是以非常简洁的 api 为代价的。
我记得当时我必须调用带有很多参数的方法,其中大多数参数为 null 。在尝试寻找到一种方法来解决这个问题时,找到了 Kotlin 的扩展属性:带有默认参数。我后来停止了 Android 的学习,但仍继续使用 Kotlin。
我喜欢 Kotlin。很多人都称赞 Kotlin 的 null 安全性(null-safety)实现。但对我来说,我喜欢它,并不是因为它是 null 安全的,而是因为别的。
假设我们经常需要将字符串首字母改成大写。在 Java 中实现这一目的的方法是使用静态方法创建一个类:
publicclassStringUtils{
publicstatic String capitalize(Stringstring) {
var character =string.substring(0,1).toUpperCase();
var rest =string.substring(1,string.length() -1).toLowerCase();
returncharacter + rest;
}
}
在早期,每个项目几乎都具有 StringUtils 和 DateUtils 类。幸运的是,现有的库提供了最常用的功能,例如 Apache Commons Lang 和 Guava 。然而,它们仍遵循相同的设计原则,即遵循基于静态方法的设计原则。这很糟糕,因为 Java 被认为是一种面向对象语言。不幸的是,静态方法不是面向对象的。
在 扩展函数 和属性的帮助下,Kotlin 允许将行为、状态分别添加到现有的类中。语法非常简单,并且与面向对象的方法完全兼容:
funString.capitalize(): String {
valcharacter = substring(0,1).toUpperCase()
valrest = substring(1, length -1).toLowerCase()
returncharacter + rest
}
在编写 Kotlin 代码时,我经常使用这个。
在底层,Kotlin 编译器生成与 Java 代码类似的字节码。这仅仅是语法糖,但是从设计的角度来看,与 Java 代码相比,它是一个巨大的改进!
在大多数面向对象语言(Java、Scala、Kotlin 等)中,类可以实现一个 契约 (也称为 接口)。这样,客户端代码可以引用该接口,而无需关心任何特定的实现。
publicinterface Shape {
floatarea();
floatperimeter();
defaultvoiddisplay(){
System.out.println(this);
System.out.println(perimeter());
System.out.println(area());
}
}
publicclassRectangleimplementsShape{
publicfinalfloatwidth;
publicfinalfloatheight;
publicRectangle(floatwidth,floatheight){
this.width=width;
this.height=height;
}
@Override
publicfloatarea(){
returnwidth*height;//(1)
}
@Override
publicfloatperimeter(){
return2*width+2*height;//(1)
}
publicstaticvoidmain(String... args){
varrect=newRectangle(2.0f,3.0f);
rect.display();
}
}
(1)处为了精确起见,应该使用 BigDecimal ,但这不是重点
重点是:由于 Rectangle 实现了 Shape,所以可以在 Rectangle 的任何实例上调用在 Shape 上定义的 display() 方法。
Go 不是一种面向对象语言:它没有类的概念。它提供了结构体,并且函数可以与这种结构体相关联。它还提供了接口,该接口可以使用结构体来实现。
然而,Java 实现接口的方式是 显式的 :Rectangle 类声明它实现了 Shape。相反,Go 的方式是隐式的。实现接口所有函数的结构体隐式地实现了该接口。
这可以转换为如下代码:
packagemain
import(
"fmt"
)
typeshapeinterface{//(1)
area()float32
perimeter()float32
}
typerectanglestruct{//(2)
widthfloat32
heightfloat32
}
func(rect rectangle)area()float32{//(3)
returnrect.width * rect.height
}
func(rect rectangle)perimeter()float32{//(3)
return2* rect.width +2* rect.height
}
funcdisplay(shape shape){//(4)
fmt.Println(shape)
fmt.Println(shape.perimeter())
fmt.Println(shape.area())
}
funcmain(){
rect := rectangle{width:2, height:3}
display(rect)//(5)
}
(1)定义 shape 接口
(2)定义 rectangle 结构体
(3)将两个 shape 函数添加到 rectangle 中
(4)display() 方法只接收一个 shape 参数
(5)因为 rectangle 实现了 shape 的所有函数,并且由于是隐式实现的,所以 rect 也是一个 shape。因此,调用 display() 方法并将 rect 作为参数进行传递是完全合法的
我之前的公司对 Clojure 投入了大量的资金。正因为如此,我努力学习过这门语言,甚至还写了 几篇文章 来总结我对它的理解。
Clojure 深受 LISP 的启发。因此,表达式用圆括号括起来,首先执行位于圆括号内部的方法。此外,Clojure 是一种动态类型语言:它们虽然有类型,但没有声明。
另一方面,该语言提供了基于契约的编程。可以指定前置条件和后置条件:它们在运行时计算。这些条件可以进行类型检查, 例如,检查参数是字符串还是布尔值等?甚至可以进行更进一步地检查,类似于 _dependent 类型:
在计算机科学和逻辑学中,依赖类型是其定义依赖于某个值的类型。“整数对”是一种类型。由于对值的依赖,“第二个大于第一个的整数对”也是依赖类型。
— 维基百科:https://en.wikipedia.org/wiki/Dependent_type
它在运行时强制执行,因此它不能被真正称为依赖类型。然而,这是我所接触过的语言中最接近依赖类型的一种了。
之前,我曾详细写过一篇关于依赖类型和基于契约编程的 文章 。
一些语言吹嘘自己提供了模式匹配的特性。通常,模式匹配可用于计算变量,例如,在 Kotlin 中:
varstatusCode: Int
val errorMessage =when(statusCode) {
401->"Unauthorized"
403->"Forbidden"
500->"Internal Server Error"
else->"Unrecognized Status Code"
}
这个用法是类固醇上(steroids)的 switch 语句。然而,一般来说,模式匹配的应用要广泛得多。在下面的代码片段中,首先检查常规 HTTP 状态错误码,如果没有找到,则默认设成更通用的错误信息
val errorMessage =when{
statusCode== 401 ->"Unauthorized"
statusCode== 403 ->"Forbidden"
statusCode- 400 < 100 ->"Client Error"
statusCode== 500 ->"Internal Server Error"
statusCode- 500 < 100 ->"Server Error"
else->"Unrecognized Status Code"
}
不过,它是有限制的。
Elixir 是一种在 Erlang OTP 上运行的动态类型语言,它将模式匹配提升到了一个全新的水平。Elixir 的模式匹配可用于简单的变量析构
{a,b, c} = {:hello,"world",42}
a 将被赋值成 :hello,b 被赋值成 “world”,c 被赋值成 42。
它还可以对集合进行更高级的析构:
[head | tail] = [1,2,3]
head 被赋值成 1,tail 被赋值成 [2, 3]。
然而,对于函数重载来说,它甚至更是如此。作为一种函数式语言,Elixir 没有用于循环的关键字(for 或 while),循环需要使用递归来实现。
举个例子,我们使用递归来计算 List 的大小。在 Java 中,这是很容易的,因为有一个 size() 方法,但是 Elixir API 没有提供这样的功能。让我们用如下的伪代码来实现该功能,Elixir 也是采用这种递归的方法。
publicintlengthOf(List<?>item){
return lengthOf(0,items);
}
privateintlengthOf(intsize, List<?>items){
if(items.isEmpty()) {
return size;
}else{
return lengthOf(size+ 1,items.remove(0));
}
}
几乎可以将它逐行的转换成 Elixir:
def length_of(list),do: length_of(0,list)
defp length_of(size,list)do
if[]==listdo
size
else
[_|tail]=list//(1)
length_of(size+ 1,tail)
end
end
(1)变量析构的模式匹配。表头的值被赋值给 _ 变量,这意味着以后就无法引用它了,因为它没有用处了。
然而,如前所述,Elixir 模式匹配也适用于函数重载。因此,Elixir 的命名方式将是:
deflist_len(list),do:list_len(0,list)
defplist_len(size, []),do: size//(1)
defplist_len(size,list)do//(2)
[_|tail]=list
list_len(size+ 1,tail)
end
(1)如果列表为空,则调用此方法
(2)否则调用此函数
注意,模式是按照声明的顺序进行评估的:在上面的代码段中, Elixir 首先评估具有空列表的函数,如果不匹配,才评估第二个函数,即列表不为空。如果要以相反的顺序声明函数,则每次都会对非空列表进行匹配操作。
Python 是一种动态类型语言。与 Java 一样,Python 通过 for 关键字提供循环功能。下面的代码片段循环遍历集合中的所有项,并逐个打印它们。
fornin[1,2,3,4,5]:
print(n)
要在新集合中收集所有项,可以先创建一个空集合,然后在循环中添加每个项到空集合中:
numbers = []
fornin[1,2,3,4,5]:
numbers.append(n)
print(numbers)
然而,可以使用一个精美的 Python 特性: for 推导式(for comprehensions) 。虽然它与标准循环使用相同的 for 关键字,但是 for 推导式是一个能获得相同结果的函数式构造器。
numbers = [nfornin[1,2,3,4,5]]
print(numbers)
上面片段的输出是 [1, 2, 3, 4, 5] 。
也可以转换每个项。例如,下面的代码段将计算每个项的平方:
numbers = [n **2fornin[1,2,3,4,5]]
print(numbers)
输出是 [1, 4, 9, 16, 25]。
for 推导式的一个好处是能够使用条件语句。例如,下面的代码片段将只过滤偶数项,然后将其平方:
numbers = [n **2fornin[1,2,3,4,5]ifn %2==0]
print(numbers)
输出是 [4, 16]。
最后,for 推导式允许使用笛卡尔积。
numbers = [a:nfornin[1,2,3]forain['a','b']]
print(numbers)
它将会输出 [(‘a’, 1), (‘b’, 1), (‘a’, 2), (‘b’, 2), (‘a’, 3), (‘b’, 3)]。
以上的 for 推导式也被称为 列表推导式(list comprehensions) ,因为它们是为了创建新的列表而设计的。 Map 推导式(Map comprehension) 也是非常相似的,目的是为了创造 map。
原文链接:https://blog.frankel.ch/six-interesting-features-programming-languages/
contain 属性允许我们指定特定的 DOM 元素和它的子元素,让它们能够独立于整个 DOM 树结构之外。目的是能够让浏览器有能力只对部分元素进行重绘、重排,而不必每次都针对整个页面。
Html5的新特性语义化标签:有利于SEO,有助于爬虫抓取更多的有效信息,爬虫是依赖于标签来确定上下文和各个关键字的权重。表单新特性,多媒体视频(video)和音频(audio)
var不存在块级作用域,具有变量提升机制。 let和const存在块级作用域,不存在变量提升。在同一作用域内只能声明一次。const在声明时需要赋值且无法修改,但如果常量是对象,则对象的属性可以修改。
Optional Chaining(可选链式调用);Nullish coalescing(空值合并);Pipeline operator(管道运算符)通过三个函数对字符串进行处理;
在今天早些时候Angular团队发布了8.0.0稳定版。其实早在NgConf 2019大会上,演讲者就已经提及了从工具到差分加载的许多内容以及更多令人敬畏的功能。下面是我对8.0.0一些新功能的简单介绍,希望可以帮助大家快速了解新版本
与我使用的其他框架相比,我最喜欢 React 的原因之一就是它对 JavaScript 的暴露程度。没有模板DSL( JSX 编译为合理的 JavaScript),组件 API 只是通过添加 React Hooks 变得更简单,并且该框架为解决的核心 UI 问题提供非常少的抽象概念
最近 ECMAScript2019,最新提案完成:tc39 Finished Proposals,我这里也是按照官方介绍的顺序进行整理,如有疑问,可以查看官方介绍啦~另外之前也整理了 《ES6/ES7/ES8/ES9系列》,可以一起看哈。
JavaScript 最初的目的是为了“赋予网页生命”。这种编程语言我们称之为脚本。它们可以写在 HTML 中,在页面加载的时候会自动执行。脚本作为纯文本存在和执行。它们不需要特殊的准备或编译即可运行。
你可能刚上手 JavaScript,或者只是曾经偶尔用过。不管怎样,JavaScript 改变了很多,有些特性非常值得一用。 这篇文章介绍了一些特性,在我看来,一个严肃的 JavaScript 开发者每天都多多少少会用到这些特性
HTTP/2 相比于 HTTP/1.1,可以说是大幅度提高了网页的性能,只需要升级到该协议就可以减少很多之前需要做的性能优化工作,当然兼容问题以及如何优雅降级应该是国内还不普遍使用的原因之一。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!