JDK15正式发布,新增功能预览!

thbcm阅读(209)

文章来源于公众号:Java中文社群 作者:磊哥

JDK 15 在 2020 年 9 月 15 号正式发布了,这次发布的主要功能有:

  • JEP 339:EdDSA 数字签名算法
  • JEP 360:密封类(预览)
  • JEP 371:隐藏类
  • JEP 372:删除 Nashorn JavaScript 引擎
  • JEP 373:重新实现 Legacy DatagramSocket API
  • JEP 374:重新实现 DatagramSocket API
  • JEP 375:实例模式匹配(第二次预览)
  • JEP 377:ZGC:一个可扩展的低延迟垃圾收集器
  • JEP 378:文本块
  • JEP 379:低暂停时间垃圾收集器
  • JEP 381:移除 Solaris 和 SPARC 端口
  • JEP 383:外部存储器访问 API(第二个内置程序)
  • JEP 384:Records(第二次预览)
  • JEP 385:不推荐的 RMI 激活去除

JEP:JDK Enhancement Proposals,JDK 增强建议,也就是 JDK 的特性新增和改进提案。

这些年发布的版本对应的 JEPs 数量如下图所示:

发布版本说明

根据发布的规划,这次发布的 JDK 15 将是一个短期的过度版,只会被 Oracle 支持(维护)6 个月,直到明年 3 月的 JDK 16 发布此版本将停止维护。而 Oracle 下一个长期支持版(LTS 版)会在明年的 9 月份候发布(Java 17),LTS 版每 3 年发布一个,上一次长期支持版是 18 年 9 月发布的 JDK 11。

JDK 15 新功能说明

JDK 15 为用户提供了十四项主要的增强/更改,包括一个孵化器模块,三个预览功能,两个不推荐使用的功能以及两个删除功能。

1、EdDSA 数字签名算法

新加入 Edwards-Curve 数字签名算法(EdDSA)实现加密签名。在许多其它加密库(如 OpenSSL 和 BoringSSL)中得到支持。与 JDK 中的现有签名方案相比,EdDSA 具有更高的安全性和性能。这是一个新的功能。

2、隐藏类

此功能可帮助需要在运行时生成类的框架。框架生成类需要动态扩展其行为,但是又希望限制对这些类的访问。隐藏类很有用,因为它们只能通过反射访问,而不能从普通字节码访问。此外,隐藏类可以独立于其他类加载,这可以减少框架的内存占用。这是一个新的功能。

3、重新实现 DatagramSocket API

重新实现旧版 DatagramSocket API,更简单、更现代的实现来代替java.net.DatagramSocketjava.net.MulticastSocketAPI 的基础实现,提高了 JDK 的可维护性和稳定性。

4、ZGC 功能转正

ZGC 已由JEP 333集成到JDK 11 中,其目标是通过减少 GC 停顿时间来提高性能。借助 JEP 377,ZGC 从预览功能转变为生产功能。

5、文本块功能转正

文本块由JEP 355在 2019 年提出,文本块是一种多行字符串文字,它避免了大多数转义序列的需要,以一种可预测的方式自动设置字符串的格式,并在需要时使开发人员可以控制格式。借助 JEP 378,文本块已成为 Java 语言的永久功能。

6、Shenandoah 垃圾回收算法转正

Shenandoah 垃圾回收从实验特性变为产品特性。这是一个从 JDK 12 引入的回收算法,该算法通过与正在运行的 Java 线程同时进行疏散工作来减少 GC 暂停时间。Shenandoah 的暂停时间与堆大小无关,无论堆栈是 200 MB 还是 200 GB,都具有相同的一致暂停时间。

7、密封类(预览)

通过密封的类和接口来增强 Java 编程语言,用于限制超类的使用,密封的类和接口限制其它可能继承或实现它们的其它类或接口。

8、instanceof 自动匹配模式(预览)

旧写法:

// 先判断类型
if (obj instanceof String) {
    // 然后转换
    String s = (String) obj;
    // 然后才能使用
}

新写法:

if (obj instanceof String s) {
    // 如果类型匹配 直接使用
} else {
    // 如果类型不匹配则不能直接使用
}

这是第二次预览该功能,我们已经在 Java 14 中首次预览过该特性。

9、Records Class(预览)

Records Class 也是第二次出现的预览功能,它在 JDK 14 中也出现过一次了,使用 Record 可以更方便的创建一个常量类,使用的前后代码对比如下。

旧写法:

class Point {
    private final int x;
    private final int y;


    Point(int x, int y) { 
        this.x = x;
        this.y = y;
    }


    int x() { return x; }
    int y() { return y; }


    public boolean equals(Object o) { 
        if (!(o instanceof Point)) return false;
        Point other = (Point) o;
        return other.x == x && other.y = y;
    }


    public int hashCode() {
        return Objects.hash(x, y);
    }


    public String toString() { 
        return String.format("Point[x=%d, y=%d]", x, y);
    }
}

新写法:

record Point(int x, int y) { }

也就是说在使用了 record 之后,就可以用一行代码编写出一个常量类,并且这个常量类还包含了构造方法、toString()、equals() 和 hashCode() 等方法。

10、外部存储器访问 API(预览)

目的是引入一个 API,以允许 Java 程序安全有效地访问 Java 堆之外的外部内存。这同样是 Java 14 的一个预览特性。

11、其它功能

其它功能里面还有一些弃用和不建议使用的功能,比如移除了 Nashorn JavaScript 引擎,同时也移除了删除 Solaris 和 SPARC 端口,并标记了一些弃用功能。

以上就是W3Cschool编程狮关于JDK15正式发布,新增功能预览!的相关介绍了,希望对大家有所帮助。

查询ElasticSearch:用SQL代替DSL

thbcm阅读(206)

文章来源于公众号:码农知识点 ,作者Monica2333

ES7.x 版本的 x-pack 自带 ElasticSearch SQL,我们可以直接通过 SQL REST API、SQL CLI 等方式使用 SQL 查询。

SQL REST API

Kibana Console中输入:

POST /_sql?format=txt
{
  "query": "SELECT * FROM library ORDER BY page_count DESC LIMIT 5"
}

将上述 SQL 替换为你自己的 SQL 语句,即可。返回格式如下:

    author      |        name        |  page_count   | release_date
-----------------+--------------------+---------------+------------------------
Peter F. Hamilton|Pandora's Star      |768            |2004-03-02T00:00:00.000Z
Vernor Vinge     |A Fire Upon the Deep|613            |1992-06-01T00:00:00.000Z
Frank Herbert    |Dune                |604            |1965-06-01T00:00:00.000Z

SQL CLI

elasticsearch-sql-cli 是安装 ES 时 bin 目录的一个脚本文件,也可单独下载。我们在 ES 目录运行

./bin/elasticsearch-sql-cli https://some.server:9200

输入 SQL 即可查询

sql> SELECT * FROM library WHERE page_count > 500 ORDER BY page_count DESC;
     author      |        name        |  page_count   | release_date
-----------------+--------------------+---------------+---------------
Peter F. Hamilton|Pandora's Star      |768            |1078185600000
Vernor Vinge     |A Fire Upon the Deep|613            |707356800000
Frank Herbert    |Dune                |604            |-144720000000

SQL To DSL

Kibana输入:

POST /_sql/translate
{
  "query": "SELECT * FROM library ORDER BY page_count DESC",
  "fetch_size": 10
}

即可得到转化后的 DSL query:

{
  "size": 10,
  "docvalue_fields": [
    {
      "field": "release_date",
      "format": "epoch_millis"
    }
  ],
  "_source": {
    "includes": [
      "author",
      "name",
      "page_count"
    ],
    "excludes": []
  },
  "sort": [
    {
      "page_count": {
        "order": "desc",
        "missing": "_first",
        "unmapped_type": "short"
      }
    }
  ]
}

因为查询相关的语句已经生成,我们只需要在这个基础上适当修改或不修改就可以愉快使用 DSL 了。

下面我们详细介绍下 ES SQL 支持的SQL语句如何避免错误使用

首先需要了解下 ES SQL 支持的 SQL 语句中,SQL 术语和ES术语的对应关系:

ES SQL 的语法支持大多遵循 ANSI SQL 标准,支持的 SQL 语句有 DML 查询和部分 DDL 查询。 DDL 查询如:DESCRIBE table,SHOW COLUMNS IN table略显鸡肋,我们主要看下对SELECT,Function的DML查询支持。

SELECT

语法结构如下:

SELECT [TOP [ count ] ] select_expr [, ...]
[ FROM table_name ]
[ WHERE condition ]
[ GROUP BY grouping_element [, ...] ]
[ HAVING condition]
[ ORDER BY expression [ ASC | DESC ] [, ...] ]
[ LIMIT [ count ] ]
[ PIVOT ( aggregation_expr FOR column IN ( value [ [ AS ] alias ] [, ...] ) ) ]

表示从0-N个表中获取行数据。SQL 的执行顺序为:

  1. 获取所有 FROM中的关键词,确定表名。
  2. 如果有WHERE条件,过滤掉所有不符合的行。
  3. 如果有GROUP BY条件,则分组聚合;如果有HAVING条件,则过滤聚合的结果。
  4. 上一步得到的结果经过select_expr运算,确定具体返回的数据。
  5. 如果有 ORDER BY条件,会对返回的数据排序。
  6. 如果有 LIMIT or TOP条件,会返回上一步结果的子集。

与常用的SQL有两点不同,ES SQL 支持TOP [ count ]PIVOT ( aggregation_expr FOR column IN ( value [ [ AS ] alias ] [, ...] ) )子句。
TOP [ count ] :如SELECT TOP 2 first_name FROM emp表示最多返回两条数据,不可与LIMIT条件共用。
PIVOT子句会对其聚合条件得到的结果进行行转列,进一步运算。这个我是没用过,不做介绍。

FUNCTION

基于上面的 SQL 我们其实已经能有过滤,聚合,排序,分页功能的 SQL 了。但是我们需要进一步了解 ES SQL 中 FUNCTION 的支持,才能写出丰富的具有全文搜索,聚合,分组功能的 SQL。 使用SHOW FUNCTIONS 可列举出支持的函数名称和所属类型。

SHOW FUNCTIONS;


      name       |     type
-----------------+---------------
AVG              |AGGREGATE
COUNT            |AGGREGATE
FIRST            |AGGREGATE
FIRST_VALUE      |AGGREGATE
LAST             |AGGREGATE
LAST_VALUE       |AGGREGATE
MAX              |AGGREGATE
MIN              |AGGREGATE
SUM              |AGGREGATE
........

我们主要看下聚合,分组,全文搜索相关的常用函数。

全文匹配函数

MATCH:相当于 DSL 中的match and multi_match查询。

MATCH(
    field_exp,       --字段名称
    constant_exp,       --字段的匹配值
    [, options])       --可选项

使用举例:

SELECT author, name FROM library WHERE MATCH(author, 'frank');


    author     |       name
---------------+-------------------
Frank Herbert  |Dune
Frank Herbert  |Dune Messiah
SELECT author, name, SCORE() FROM library WHERE MATCH('author^2,name^5', 'frank dune');


    author     |       name        |    SCORE()
---------------+-------------------+---------------
Frank Herbert  |Dune               |11.443176
Frank Herbert  |Dune Messiah       |9.446629

QUERY:相当于 DSL 中的 query_string 查询。

QUERY(
    constant_exp      --匹配值表达式
    [, options])       --可选项

使用举例:

SELECT author, name, page_count, SCORE() FROM library WHERE QUERY('_exists_:"author" AND page_count:>200 AND (name:/star.*/ OR name:duna~)');


      author      |       name        |  page_count   |    SCORE()
------------------+-------------------+---------------+---------------
Frank Herbert     |Dune               |604            |3.7164764
Frank Herbert     |Dune Messiah       |331            |3.4169943

SCORE():返回输入数据和返回数据的相关度relevance. 使用举例:

SELECT SCORE(), * FROM library WHERE MATCH(name, 'dune') ORDER BY SCORE() DESC;


    SCORE()    |    author     |       name        |  page_count   |    release_date
---------------+---------------+-------------------+---------------+--------------------
2.2886353      |Frank Herbert  |Dune               |604            |1965-06-01T00:00:00Z
1.8893257      |Frank Herbert  |Dune Messiah       |331            |1969-10-15T00:00:00Z

聚合函数

AVG(numeric_field) :计算数字类型的字段的平均值。

SELECT AVG(salary) AS avg FROM emp;

COUNT(expression):返回输入数据的总数,包括COUNT()时field_name对应的值为null的数据。 COUNT(ALL field_name):返回输入数据的总数,不包括field_name对应的值为null的数据。 COUNT(DISTINCT field_name):返回输入数据中field_name对应的值不为null的总数。 SUM(field_name):返回输入数据中数字字段field_name对应的值的总和。 MIN(field_name):返回输入数据中数字字段field_name对应的值的最小值。 MAX(field_name):返回输入数据中数字字段field_name对应的值的最大值。

分组函数

这里的分组函数是对应 DSL 中的bucket分组。

HISTOGRAM:语法如下:

HISTOGRAM(
           numeric_exp,    --数字表达式,通常是一个field_name
           numeric_interval    --数字的区间值
)


HISTOGRAM(
           date_exp,      --date/time表达式,通常是一个field_name
           date_time_interval      --date/time的区间值
)

如下返回每年1月1号凌晨出生的数据:

ELECT HISTOGRAM(birth_date, INTERVAL 1 YEAR) AS h, COUNT(*) AS c FROM emp GROUP BY h;




           h            |       c
------------------------+---------------
null                    |10
1952-01-01T00:00:00.000Z|8
1953-01-01T00:00:00.000Z|11
1954-01-01T00:00:00.000Z|8
1955-01-01T00:00:00.000Z|4
1956-01-01T00:00:00.000Z|5
1957-01-01T00:00:00.000Z|4
1958-01-01T00:00:00.000Z|7
1959-01-01T00:00:00.000Z|9
1960-01-01T00:00:00.000Z|8
1961-01-01T00:00:00.000Z|8
1962-01-01T00:00:00.000Z|6
1963-01-01T00:00:00.000Z|7
1964-01-01T00:00:00.000Z|4
1965-01-01T00:00:00.000Z|1

ES SQL局限性

因为ES SQLES DSL在功能上并非完全匹配,官方文档提到的 SQL 局限性有:

大的查询可能抛ParsingException

在解析阶段,极大的查询会占用过多的内存,在这种情况下,Elasticsearch SQL引擎将中止解析并抛出错误。

nested类型字段的表示方法

SQL 中不支持nested类型的字段,只能使用

[nested_field_name].[sub_field_name]

这种形式来引用内嵌子字段。 使用举例:

SELECT dep.dep_name.keyword FROM test_emp GROUP BY languages;

nested类型字段不能用在where 和 order by 的Scalar函数上

如以下 SQL 都是错误

SELECT * FROM test_emp WHERE LENGTH(dep.dep_name.keyword) > 5;


SELECT * FROM test_emp ORDER BY YEAR(dep.start_date);

不支持多个nested字段的同时查询

如嵌套字段nested_Anested_B无法同时使用。

nested内层字段分页限制

当分页查询有nested字段时,分页结果可能不正确。这是因为:ES 中的分页查询发生在Root nested document上,而不是它的内层字段上。

keyword类型的字段不支持normalizer

不支持数组类型的字段

这是因为在 SQL 中一个field只对应一个值,这种情况下我们可以使用上面介绍的 SQL To DSL 的 API 转化为 DSL 语句,用 DSL 查询就好了。

聚合排序的限制

  • 排序字段必须是聚合桶中的字段,ES SQL CLI突破了这种限制,但上限不能超过512行,否则在sorting阶段会抛异常。推荐搭配Limit子句使用,如:
SELECT * FROM test GROUP BY age ORDER BY COUNT(*) LIMIT 100;

  • 聚合排序的排序条件不支持Scalar函数或者简单的操作符运算。聚合后的复杂字段(比如包含聚合函数)也是不能用在排序条件上的。

以下是错误例子:

SELECT age, ROUND(AVG(salary)) AS avg FROM test GROUP BY age ORDER BY avg;


SELECT age, MAX(salary) - MIN(salary) AS diff FROM test GROUP BY age ORDER BY diff;

子查询的限制

子查询中包含GROUP BY or HAVING 或者比SELECT X FROM (SELECT ...) WHERE [simple_condition]这种结构复杂,都是可能执行不成功的。

TIME 数据类型的字段不支持GROUP BY条件和HISTOGRAM函数

如以下查询是错误的:

SELECT count(*) FROM test GROUP BY CAST(date_created AS TIME);


SELECT HISTOGRAM(CAST(birth_date AS TIME), INTERVAL '10' MINUTES) as h, COUNT(*) FROM t GROUP BY h

但是将 TIME 类型的字段包装为Scalar函数返回是支持 GROUP BY 的,如:

SELECT count(*) FROM test GROUP BY MINUTE((CAST(date_created AS TIME));

返回字段的限制 如果一个字段不在 source 中存储,是无法查询到的。keyword, date, scaled_float, geo_point, geo_shape这些类型的字段不受这种限制,因为他们不是从_source中返回,而是从docvalue_fields中返回。

以上就是W3Cschool编程狮关于查询ElasticSearch:用SQL代替DSL的相关介绍了,希望对大家有所帮助。

(ES5版)深入理解 JavaScript 执行上下文和执行栈

thbcm阅读(201)

文章来源于公众号:前端桃园 ,作者桃翁

最近在研究 JavaScript 基础性的东西,但是看到对于执行上下文的解释我发现有两种,一种是执行上下文包含:scope(作用域)、variable object(变量对象)、this value(this 值),另外一个种是包含:lexical environment(词法环境)、variable environment(变量环境)、this value(this 值)。

后面我查阅了不少博客以及 ES3 和 ES5 的规范才了解到,第一种是 ES3 的规范,经典书籍《JavaScript 高级程序设计》第三版就是这样解释的,也是网上广为流传的一种,另一种是 ES5 的规范。

然后我接着又去翻了 ES2018 中的,发现又有变化了,已经增加了更多的内容了,考虑到这部分内容颇为复杂,准备后面再进行总结分享,查资料的时候看到这篇讲执行上下文(ES5 )的还不错,所以就翻译出来先分享给大家。

以后看到变量对象、活动对象知道是 ES3 里面的内容,而如果是词法环境、变量环境这种词就是 ES5 以后的内容。

以下是正文:

什么是执行上下文?

简而言之,执行上下文是计算和执行 JavaScript 代码的环境的抽象概念。每当 JavaScript 代码在运行的时候,它都是在执行上下文中运行。

执行上下文的类型

JavaScript 中有三种执行上下文类型。

  • 全局执行上下文 — 这是默认或者说基础的上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:创建一个全局的 window 对象(浏览器的情况下),并且设置 this 的值等于这个全局对象。一个程序中只会有一个全局执行上下文。
  • 函数执行上下文 — 每当一个函数被调用时, 都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创建的。函数上下文可以有任意多个。每当一个新的执行上下文被创建,它会按定义的顺序(将在后文讨论)执行一系列步骤。
  • Eval 函数执行上下文 — 执行在 eval 函数内部的代码也会有它属于自己的执行上下文,但由于 JavaScript 开发者并不经常使用 eval,所以在这里我不会讨论它。

执行栈

执行栈,也就是在其它编程语言中所说的“调用栈”,是一种拥有 LIFO(后进先出)的数据结构,被用来存储代码运行时创建的所有执行上下文。

JavaScript 引擎第一次遇到你的脚本时,它会创建一个全局的执行上下文并且压入当前执行栈。每当引擎遇到一个函数调用,它会为该函数创建一个新的执行上下文并压入栈的顶部。

引擎会执行处于栈顶的执行上下文的函数。当该函数执行结束时,执行上下文从栈中弹出,控制流程到达当前栈中的下一个上下文。

让我们通过下面的代码示例来理解:

let a = 'Hello World!';


functionfirst() {
  console.log('Inside first function');
  second();
  console.log('Again inside first function');
}


functionsecond() {
  console.log('Inside second function');
}


first();
console.log('Inside Global Execution Context');

当上述代码在浏览器加载时,JavaScript 引擎创建了一个全局执行上下文并把它压入当前执行栈。当遇到 first() 函数调用时,JavaScript 引擎为该函数创建一个新的执行上下文并把它压入当前执行栈的顶部。

当从 first() 函数内部调用 second() 函数时,JavaScript 引擎为 second() 函数创建了一个新的执行上下文并把它压入当前执行栈的顶部。当 second() 函数执行完毕,它的执行上下文会从当前栈弹出,并且控制流程到达下一个执行上下文,即 first() 函数的执行上下文。

first() 执行完毕,它的执行上下文从栈弹出,控制流程到达全局执行上下文。一旦所有代码执行完毕,JavaScript 引擎从当前栈中移除全局执行上下文。

怎么创建执行上下文?

到现在,我们已经看过 JavaScript 怎样管理执行上下文了,现在让我们了解 JavaScript 引擎是怎样创建执行上下文的。

创建执行上下文有两个阶段:1) 创建阶段2) 执行阶段

创建阶段

JavaScript 代码执行前,执行上下文将经历创建阶段。在创建阶段会发生三件事:

  1. this 值的决定,即我们所熟知的 this 绑定
  2. 创建词法环境组件。
  3. 创建变量环境组件。

所以执行上下文在概念上表示如下:

ExecutionContext = {
  ThisBinding = <this value>,
  LexicalEnvironment = { ... },
  VariableEnvironment = { ... },
}

this 绑定:**

在全局执行上下文中,this 的值指向全局对象。(在浏览器中,this引用 Window 对象)。

在函数执行上下文中,this 的值取决于该函数是如何被调用的。如果它被一个引用对象调用,那么 this 会被设置成那个对象,否则 this 的值被设置为全局对象或者 undefined(在严格模式下)。例如:

let foo = {
  baz: function() {
  console.log(this);
  }
}


foo.baz();   // 'this' 引用 'foo', 因为 'baz' 被
             // 对象 'foo' 调用


let bar = foo.baz;


bar();       // 'this' 指向全局 window 对象,因为
             // 没有指定引用对象

词法环境

官方的 ES6 文档把词法环境定义为

词法环境是一种规范类型,基于 ECMAScript 代码的词法嵌套结构来定义标识符和具体变量和函数的关联。一个词法环境由环境记录器和一个可能的引用outer词法环境的空值组成。

简单来说词法环境是一种持有标识符—变量映射的结构。(这里的标识符指的是变量/函数的名字,而变量是对实际对象[包含函数类型对象]或原始数据的引用)。

现在,在词法环境的内部有两个组件:(1) 环境记录器和 (2) 一个外部环境的引用

  1. 环境记录器是存储变量和函数声明的实际位置。
  2. 外部环境的引用意味着它可以访问其父级词法环境(作用域)。

译者注:外部环境已经跟 ES3 规定的作用域的作用类似

词法环境有两种类型:

  • 全局环境(在全局执行上下文中)是没有外部环境引用的词法环境。全局环境的外部环境引用是null。它拥有内建的 Object/Array/等、在环境记录器内的原型函数(关联全局对象,比如 window 对象)还有任何用户定义的全局变量,并且 this的值指向全局对象。
  • 函数环境中,函数内部用户定义的变量存储在环境记录器中。并且引用的外部环境可能是全局环境,或者任何包含此内部函数的外部函数。

环境记录器也有两种类型(如上!):

  1. 声明式环境记录器存储变量、函数和参数。
  2. 对象环境记录器用来定义出现在全局上下文中的变量和函数的关系。

简而言之,

  • 全局环境中,环境记录器是对象环境记录器。
  • 函数环境中,环境记录器是声明式环境记录器。

注意 — 对于函数环境声明式环境记录器还包含了一个传递给函数的 arguments 对象(此对象存储索引和参数的映射)和传递给函数的参数的 length

抽象地讲,词法环境在伪代码中看起来像这样:

GlobalExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // 在这里绑定标识符
    }
    outer: <null>
  }
}


FunctionExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // 在这里绑定标识符
    }
    outer: <Global or outer function environment reference>
  }
}

变量环境:

它同样是一个词法环境,其环境记录器持有变量声明语句在执行上下文中创建的绑定关系。

如上所述,变量环境也是一个词法环境,所以它有着上面定义的词法环境的所有属性。

在 ES6 中,词法环境组件和变量环境的一个不同就是前者被用来存储函数声明和变量(letconst)绑定,而后者只用来存储 var 变量绑定。

我们看点样例代码来理解上面的概念:

let a = 20;
const b = 30;
var c;


function multiply(e, f) {
 var g = 20;
 return e * f * g;
}


c = multiply(20, 30);

执行上下文看起来像这样:

GlobalExectionContext = {


  ThisBinding: <Global Object>,


  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // 在这里绑定标识符
      a: < uninitialized >,
      b: < uninitialized >,
      multiply: < func >
    }
    outer: <null>
  },


  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // 在这里绑定标识符
      c: undefined,
    }
    outer: <null>
  }
}


FunctionExectionContext = {
  ThisBinding: <Global Object>,


  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // 在这里绑定标识符
      Arguments: {0: 20, 1: 30, length: 2},
    },
    outer: <GlobalLexicalEnvironment>
  },


VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // 在这里绑定标识符
      g: undefined
    },
    outer: <GlobalLexicalEnvironment>
  }
}

注意 — 只有遇到调用函数 multiply 时,函数执行上下文才会被创建。

可能你已经注意到 letconst 定义的变量并没有关联任何值,但 var 定义的变量被设成了undefined

这是因为在创建阶段时,引擎检查代码找出变量和函数声明,虽然函数声明完全存储在环境中,但是变量最初设置为 undefinedvar 情况下),或者未初始化(letconst 情况下)。

这就是为什么你可以在声明之前访问 var 定义的变量(虽然是 undefined),但是在声明之前访问 letconst 的变量会得到一个引用错误。

这就是我们说的变量声明提升。

执行阶段

这是整篇文章中最简单的部分。在此阶段,完成对所有这些变量的分配,最后执行代码。

注意 — 在执行阶段,如果 JavaScript 引擎不能在源码中声明的实际位置找到 let 变量的值,它会被赋值为 undefined

结论

我们已经讨论过 JavaScript 程序内部是如何执行的。虽然要成为一名卓越的 JavaScript 开发者并不需要学会全部这些概念,但是如果对上面概念能有不错的理解将有助于你更轻松,更深入地理解其他概念,如变量声明提升,作用域和闭包。

以上就是W3Cschool编程狮关于(ES5版)深入理解 JavaScript 执行上下文和执行栈的相关介绍了,希望对大家有所帮助。

这10个有用的HTML5功能,你肯定没用过!

thbcm阅读(212)

文章来源于公众号:前端人

HTML5 不是新事物。自从最初发布(2008年1月)以来,我们一直在使用它的一些功能。今天给大家分享的是一些让你意想不到的效果,和特性!到目前为止,我还没有真正使用过它!

在本文中,我列出了十个 HTML5 我过去没用过但现在发现有用的功能。

1.输出标签

<output>标签表示的运算的结果。通常,此元素定义一个区域,该区域将用于显示某些计算得出的文本。

代码如下:

<form oninput="x.value=parseInt(a.value) * parseInt(b.value)">
   <input type="number" id="a" value="0">
          * <input type="number" id="b" value="0">
                = <output name="x" for="a b"></output>
</form>

效果如下:

小小提示

如果您要在客户端JavaScript中执行任何计算,并且希望结果反映在页面上,请使用<output>标记。您不必走动使用即可获取元素的额外步骤getElementById()。

2.详细信息标签

<details>标签提供随需应变的细节给用户。如果需要按需向用户显示内容,请使用此标记。默认情况下,小部件是关闭的。打开后,它将展开并显示其中的内容。

<summary>标签使用<details>来为它指定一个可见的标题。

代码如下:

<details>
  <summary>Click Here to get the user details</summary>
   <table>
      <tr>
          <th>#</th>
          <th>Name</th>
          <th>Location</th>
          <th>Job</th>
      </tr>
      <tr>
          <td>1</td>
          <td>Adam</td>
          <td>Huston</td>
          <td>UI/UX</td>
      </tr>
    </table>
</details>

效果如下:

3.内容可编辑

contenteditable属性是可以在元素上设置以使内容可编辑的属性。它可与DIVPUL等元素一起使用。使用方法如下:

<element contenteditable="true|false"/>

注意,如果contenteditable未在元素上设置,则会从其父级继承该属性。

代码如下:

<h2> Shoppping List(Content Editable) </h2>
 <ul class="content-editable" contenteditable="true">
     <li> 1. Milk </li>
     <li> 2. Bread </li>
     <li> 3. Honey </li>
</ul>

效果如下:

小小提示

可以使spandiv元素可编辑,并且可以使用css样式向其添加任何丰富的内容。这将比使用输入字段处理它更好。试试看!

4.地图

<map>标签可以帮助定义图像映射。图像映射是其中具有一个或多个可单击区域的任何图像。map标签与<area>标签一起确定可点击区域。可点击区域可以是矩形,圆形或多边形区域中的任意一种。如果未指定任何形状,它将考虑整个图像。

代码如下:

<div>
    <img src="circus.jpg" width="500" height="500" alt="Circus" usemap="#circusmap">
    <map name="circusmap">
        <area shape="rect" coords="67,114,207,254" href="elephant.htm">
        <area shape="rect" coords="222,141,318, 256" href="lion.htm">
        <area shape="rect" coords="343,111,455, 267" href="horse.htm">
        <area shape="rect" coords="35,328,143,500" href="clown.htm">
        <area shape="circle" coords="426,409,100" href="clown.htm">
    </map>
 </div>

效果如下:

小小提示

图像贴图有其自身的缺点,但是您可以将其用于视觉演示。如何用全家福照片尝试一下并深入研究个人照片(可能是我们一直以来都怀有的旧照片!)。

5.标记内容

使用<mark>标记突出显示任何文本内容。

代码如下:

 <p> 我为何这么帅? <mark>"这该死的魅力"</mark> 是吗? </p>

效果如下:

小小提示

您还可以使用CSS更改突出显示颜色,标记功能确实能够做出很多有意思的东西!

mark {
  background-color: green;
  color: #FFFFFF;
}

6.data- *属性

这些data-*属性用于存储页面或应用程序专用的自定义数据。可以在JavaScript代码中使用存储的数据来创建更多的用户体验。

data- *属性由两部分组成:

  • 属性名称不得包含任何大写字母,并且前缀“ data-”后必须至少长一个字符
  • 属性值可以是任何字符串

代码如下:

<h2> 你准备好了吗 </h2>
 <div class="data-attribute" 
      id="data-attr" 
      data-custom-attr="You are just Awesome!"> 
      我有个秘密!
  </div>
 <button onclick="reveal()">点击看我的咪咪</button>
function reveal() {
   let dataDiv = document.getElementById('data-attr');
    let value = dataDiv.dataset['customAttr'];
   document.getElementById('msg').innerHTML = `<mark>${value}</mark>`;
}

效果如下:

小小提示

要在JavaScript中读取这些属性的值,可以使用getAttribute()它们的完整HTML名称(即data-custom-attr),但是该标准定义了一种更简单的方法:使用dataset属性。

7.数据清单

<datalist>标签指定的预先定义的选项列表,并允许用户添加更多了。它提供了一项autocomplete功能,使您可以提前输入所需的选项

代码如下:

<form action="" method="get">
    <label for="fruit">从列表中选择你的水果:</label>
    <input list="fruits" name="fruit" id="fruit">
        <datalist id="fruits">
           <option value="Apple">
           <option value="Orange">
           <option value="Banana">
           <option value="Mango">
           <option value="Avacado">
        </datalist>
     <input type="submit">
 </form>  

效果如下:

小小提示

与传统<select>-<option>标签有何不同?选择标记用于从选项中选择一个或多个项目,您需要浏览列表以进行选择。Datalist是具有自动完成功能的高级功能。

8.范围(滑块)

range是给定的一个样滑块范围选择的输入类型。

代码如下:

<form method="post">
    <input type="range" 
           name="range" 
           min="0" 
           max="100" 
           step="1" 
           value=""
           onchange="changeValue(event)"/>
 </form>
 <div class="range">
      <output id="output" name="result">  </output>
 </div> 

小小提示

sliderHTML5中没有任何毛病

9.测量标签

使用<meter>标签测量给定范围内的数据。

代码如下:

<label for="home">/home/atapas</label>
<meter id="home" value="4" min="0" max="10">2 out of 10</meter><br>


<label for="root">/root</label>
<meter id="root" value="0.6">60%</meter><br>

效果如下:

小小提示

不要将<meter>标签用于进度指示器,以实现用户体验。我们有<Progress>HTML5 的标记。

<label for="file">Downloading progress:</label>
<progress id="file" value="32" max="100"> 32% </progress>

10.输入(这个没什么新鲜的,凑数吧!)

我们最了解这部分的用法,例如文本,密码等输入类型。输入类型的特殊用法很少,

必填

将输入字段标记为必填

<input type="text" id="username1" name="username" required>

效果如下:

自动对焦

通过将光标放在输入元素上来自动将焦点放在输入元素上。

<input type="text" id="username2" name="username" required autofocus>

正则表达式验证

您可以使用正则表达式指定模式以验证输入。

<input type="password" 
       name="password" 
       id="password" 
       placeholder="6-20 chars, at least 1 digit, 1 uppercase and one lowercase letter" 
       pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,20}$" autofocus required>

选色器

<input type="color" onchange="showColor(event)">
<p id="colorMe">Color Me!</p>

以上就是W3Cschool编程狮关于这10个有用的HTML5功能,你肯定没用过!的相关介绍了,希望对大家有所帮助。

OkHttp透明压缩,收获性能10倍,外加故障一枚

thbcm阅读(193)

文章来源于公众号:小姐姐味道 ,作者小姐姐养的狗

要使用OkHttp,一定要知道它的透明压缩,否则死都不知道怎么死的;或者活也不知道为什么活的不舒坦。

反正不是好事。

什么叫透明压缩呢?OkHttps 在发送请求的时候,会自动加入 gzip 请求头Accept-Encoding:gzip。所以,当返回的数据带有 gzip 响应头时Content-Encoding=gzipOkHttps 会自动帮我们解压数据。(Accept-EncodingContent-Encoding是一对请求头,分别对应着请求和返回)

为什么要进行压缩呢?因为它能大幅减少传输的容量。像一些 CPU 资源占用不高的服务,比如 Kafka ,我们就可以开启 gzip 压缩,加快信息的流转。

这个压缩比有多高呢?可以看下下面实实在在的截图,对于普通的xml或者json,数据可以由9MB压缩到350KB左右,压缩比足足达到了26

它让系统性能飞起来

SpringCloud微服务体系,现在有非常多的公司在用。即使是一些传统企业,一些大数据量的toB企业,也想尝一尝螃蟹。

对于一个简单的 SpringBoot 服务,我们只需要在 yml 文件中配置上相应的压缩就可以了。这样,我们就打通了浏览器到 Web 服务的这一环。这种压缩方式,对于大数据量的服务来说,是救命式的!

具体配置如下。

server:
  port: 8082
  compression:
    enabled: true
    min-response-size: 1024
    mime-types: ["text/html","text/xml","application/xml","application/json","application/octet-stream"]

它所对应的 Spring 配置类是org.springframework.boot.web.server.Compression

但是不要高兴太早。由于是分布式环境,这里面调用链就会长一些。即使是在内网,动辄十几MB的网络传输,也会耗费可观的时间。

如上图,一个请求从浏览器到达真正的服务节点,可能要经过很多环节。

  • nginx转发请求到微服务网关zuul
  • zuul转发到具体的微服务A
  • 微服务A通过Feign接口调用微服务B

如果我们的数据,大多数是由微服务B提供的,那么上面的任何一个环节传输效率慢,都会影响请求的性能。

所以,我们需要开启 Feign 接口的 gzip 压缩。使用 OkHttps 的透明代理是最简单的方式。

首先,在项目中引入 feign 的 jar 包。

<dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
</dependency>

其次,在 yml 文件中启用 OkHttps 作为 feign 的客户端请求工具包。稳妥起见,我们同时屏蔽了 httpclient ,这个东西太重太老了。

feign:
  httpclient:
    enabled: false
  okhttp:
    enabled: true

到此为止,我们就可以享受 OkHttps 的透明代理带来的便捷性了。

假如你的应用数据包大,调用链长,这种方式甚至会给你的服务带来数秒的性能提升。 xjjdog 就曾经靠调整几个参数,就让一个蜗牛系统飞了起来。大家惊呼:原来B端也可以C一下。

OkHttp是如何实现透明压缩的?

OkHttps 对于透明压缩的处理,是通过拦截器来做的。具体的类,就是okhttp3.internal.http.BridgeInterceptor

具体代码如下,当判断没有Accept-Encoding头的时候,就自行加入一个。

// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
  transparentGzip = true;
  requestBuilder.header("Accept-Encoding", "gzip");
}

最关键的代码在下面。

if (transparentGzip
    && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
    && HttpHeaders.hasBody(networkResponse)) {
  GzipSource responseBody = new GzipSource(networkResponse.body().source());
  Headers strippedHeaders = networkResponse.headers().newBuilder()
      .removeAll("Content-Encoding")
      .removeAll("Content-Length")
      .build();
  responseBuilder.headers(strippedHeaders);
  String contentType = networkResponse.header("Content-Type");
  responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}

可以看到if语句里,有三个条件。

  • 程序没有设置Accept-Encoding,启用了透明压缩
  • 服务端有Content-Encoding头,并启用了gzip压缩
  • 有数据包

只有同时满足这三个条件,OkHttps 的透明压缩才会起作用,帮我们自动解压。

它挖的坑有点深

可惜的是,上面的关键代码,只有if,没有else,也就是当其中的任何一个条件不满足,后端的数据包将原封不动的返回。

2、3两个条件是没有什么问题的,原样返回后端数据并没有什么损害,问题就出在第一个条件里。

如果你在代码中,使用了下面的代码:

Request.Builder builder = chain.request()
                .newBuilder()
                .addHeader("Accept", "application/json")
                .addHeader("Accept-Encoding", "gzip");

也就是手动设置了Accept-Encoding头信息。这很常见,因为这体现了程序员思维的严谨。

正是这种严谨,造成了问题。

假如你的后端应用刚开始是没有开启gzip压缩的,这时候两者相安无事;但如果你的后端应用突然有一天开启了gzip压缩,你的这段代码将全部over。

原因就是,服务端gzip数据包会原样返回,你需要手动处理gzip数据包。

所以,不加是好事,加了反而会坏事,除非你想自己处理gzip数据。

由于OkHttpAndroid上应用也非常广泛,如果你不知道这个细节,造成的后果就是灾难性的。客户端更新慢,只能老老实实回退服务端了。

智能的背后,总有些肉眼不可见的细节。就像是xjjdog纯情的背后,总有一份羞涩。只有深入了解,你才会知道它的美。

以上就是W3Cschool编程狮关于OkHttp透明压缩,收获性能10倍,外加故障一枚的相关介绍了,希望对大家有所帮助。

API 接口,统一格式返回!你学到了么

thbcm阅读(198)

文章来源于公众号:Java旅途

前言

在移动互联网,分布式、微服务盛行的今天,现在项目绝大部分都采用的微服务框架,前后端分离方式,(题外话:前后端的工作职责越来越明确,现在的前端都称之为大前端,技术栈以及生态圈都已经非常成熟;以前后端人员瞧不起前端人员,那现在后端人员要重新认识一下前端,前端已经很成体系了)。

一般系统的大致整体架构图如下:

需要说明的是,有些小伙伴会回复说,这个架构太简单了吧,太low了,什么网关啊,缓存啊,消息中间件啊,都没有。因为老顾这篇主要介绍的是API接口,所以我们聚焦点,其他的模块小伙伴们自行去补充。

接口交互

前端和后端进行交互,前端按照约定请求URL路径,并传入相关参数,后端服务器接收请求,进行业务处理,返回数据给前端。

针对URL路径的restful风格,以及传入参数的公共请求头的要求(如:app_version,api_version,device等),老顾这里就不介绍了,小伙伴们可以自行去了解,也比较简单。

后端服务器如何实现把数据返回给前端?

返回格式

后端返回给前端我们一般用 JSON 体方式,定义如下:

{
  #返回状态码
  code:integer,
  #返回信息描述
  message:string,
  #返回值
  data:object
}

CODE状态码

code 返回状态码,一般小伙伴们是在开发的时候需要什么,就添加什么。

如接口要返回用户权限异常,我们加一个状态码为101吧,下一次又要加一个数据参数异常,就加一个102的状态码。这样虽然能够照常满足业务,但状态码太凌乱了

我们应该可以参考 HTTP 请求返回的状态码

:下面是常见的HTTP状态码:
200 - 请求成功
301 - 资源(网页等)被永久转移到其它URL
404 - 请求的资源(网页等)不存在
500 - 内部服务器错误

我们可以参考这样的设计,这样的好处就把错误类型归类到某个区间内,如果区间不够,可以设计成4位数。

#1000~1999 区间表示参数错误
#2000~2999 区间表示用户错误
#3000~3999 区间表示接口异常

这样前端开发人员在得到返回值后,根据状态码就可以知道,大概什么错误,再根据 message 相关的信息描述,可以快速定位。

Message

这个字段相对理解比较简单,就是发生错误时,如何友好的进行提示。一般的设计是和code状态码一起设计,如

再在枚举中定义,状态码

状态码和信息就会一一对应,比较好维护。

Data

返回数据体,JSON 格式,根据不同的业务又不同的 JSON 体。

我们要设计一个返回体类 Result

控制层Controller

我们会在controller层处理业务请求,并返回给前端,以order订单为例

我们看到在获得 order 对象之后,我们是用的 Result 构造方法进行包装赋值,然后进行返回。小伙伴们有没有发现,构造方法这样的包装是不是很麻烦,我们可以优化一下。

美观美化

我们可以在 Result 类中,加入静态方法,一看就懂

那我们来改造一下 Controller

代码是不是比较简洁了,也美观了。

优雅优化

上面我们看到在 Result 类中增加了静态方法,使得业务处理代码简洁了。但小伙伴们有没有发现这样有几个问题:

1、每个方法的返回都是Result封装对象,没有业务含义

2、在业务代码中,成功的时候我们调用Result.success,异常错误调用Result.failure。是不是很多余

3、上面的代码,判断id是否为null,其实我们可以使用hibernate validate做校验,没有必要在方法体中做判断。

我们最好的方式直接返回真实业务对象,最好不要改变之前的业务方式,如下图

这个和我们平时的代码是一样的,非常直观,直接返回 order 对象,这样是不是很完美。那实现方案是什么呢?

实现方案

小伙伴们怎么去实现是不是有点思路,在这个过程中,我们需要做几个事情

1、定义一个注解@ResponseResult,表示这个接口返回的值需要包装一下

2、拦截请求,判断此请求是否需要被@ResponseResult注解

3、核心步骤就是实现接口ResponseBodyAdvice和@ControllerAdvice,判断是否需要包装返回值,如果需要,就把Controller接口的返回值进行重写。

注解类

用来标记方法的返回值,是否需要包装

拦截器

拦截请求,是否此请求返回的值需要包装,其实就是运行的时候,解析 @ResponseResult 注解

此代码核心思想,就是获取此请求,是否需要返回值包装,设置一个属性标记。

重写返回体

上面代码就是判断是否需要返回值包装,如果需要就直接包装。这里我们只处理了正常成功的包装,如果方法体报异常怎么办?处理异常也比较简单,只要判断 body 是否为异常类。

怎么做全局的异常处理,篇幅原因,老顾这里就不做介绍了,只要思路理清楚了,自行改造就行。

重写Controller

在控制器类上或者方法体上加上 @ResponseResult 注解,这样就 ok 了,简单吧。到此返回的设计思路完成,是不是又简洁,又优雅。

这个方案还有没有别的优化空间,当然是有的。如:每次请求都要反射一下,获取请求的方法是否需要包装,其实可以做个缓存,不需要每次都需要解析。当然整体思路了解,小伙伴们就可以在此基础上面自行扩展。

以上就是W3Cschool编程狮关于API 接口,统一格式返回!你学到了么的相关介绍了,希望对大家有所帮助。

Vue3.0版本发布,让我们看看有什么更新

thbcm阅读(211)

文章来源于公众号:前端森林

Vue 官方团队终于发布了Vue3.0版本,就在 2020 年 9 月 18 日晚 11 点半左右,代号为One Piece

其实Vue3.0版本发布的消息,我是昨天晚上刷朋友圈看到的(已经差不多凌晨 1 点了),然后我就立刻起来,打开电脑,看了一下github,把官方发布文档过了一遍。其实我感觉这次版本更新,最主要的还是Composition API以及对于TypeScript的支持,而且早在 4 月 21 日,尤大在 B 站关于Vue.js 3.0 Beta最新进展的分享上就已经提到了这些改动。

此次Vue3.0版本更新提供了改进的性能,更小的捆绑包大小,更好的TypeScript集成,以及用于处理大规模用例的新API,为框架的长期未来迭代奠定了坚实的基础。

这里我就参考官方releases给大家大概说一下主要更新(更详细的直接查看 releases docs):

进一步推进“渐进框架”概念

Vue一开始就秉承这样的原则:成为任何人都能快速学习且平易近人的框架。(时至今日,作为Vue的资深用户,我觉得他做到这一点了)。当然Vue3.0将这种灵活性进一步提升。

分层内部模块

Vue 3.0内核仍然可以通过一个简单的<script>标签使用,但其内部结构已被彻底重写为一组解耦的模块。新的体系结构提供了更好的可维护性,并允许最终用户通过tree-shaking来减少运行时体积大小的一半。

解决规模问题的新 API

Vue 3.0引入了Composition API一套全新的API,旨在解决大型应用程序中Vue使用的难点。Composition API建立在响应式API之上,与2.x基于对象的API方式相比,可实现类似于React Hook的逻辑组成和复用,拥有更灵活的代码组织模式以及更可靠的类型推断能力。

性能改进

Vue 2相比,Vue 3bundle包大小方面通过tree-shaking减轻了多达41%的体积),初始渲染速度加快了55%,更新速度提升了133%,内存使用率方面表现出了显著的性能改进最多可减少54%

改进与TypeScript的兼容

Vue 3.0的代码库是用TypeScript编写的,具有自动生成、测试并构建类型声明。同时,Vue 3已全面支持TSX

未来计划

紧接着,Vue官方团队计划要做的事情如下:

  • 迁移版本
  • IE11支持
  • devtools中的RouterVuex集成
  • Vetur中模板类型推断的进一步改进

快速上手

如果你已经迫不及待的想去学习Vue3了呢,这里放上对应的链接:

参考

Vue 官方发布文档

以上就是W3Cschool编程狮关于Vue3.0版本发布,让我们看看有什么更新的相关介绍了,希望对大家有所帮助。

Vue3.0正式发布,本次发布所有总结,一起看看!

thbcm阅读(179)

文章来源于公众号:前端人 作者:鬼哥

2020年09月18日,Vue.js 3.0 正式发布。本次发布框架本身带来了几个大点的改进,以及功能和历程的一些总结!

1.Vue3.0历程

  • 3.0版本代表了2年多的开发工作
  • 具有30多个RFC
  • 2600多个提交
  • 来自99个贡献者
  • 628个拉取请求
  • 大量开发和文档工作

2.Vue的使命

成为任何人都可以快速学习的易于接近的框架

3.性能方面的优化

  • 路由懒加载
  • keep-alive缓存页面
  • 使用v-show复用DOM
  • v-for 遍历避免同时使用 v-if
  • 长列表性能优化
  • 事件的销毁
  • 图片懒加载
  • 第三方插件按需引入
  • 无状态的组件标记为函数式组件
  • 子组件分割
  • 变量本地化
  • SSR

4.更好的TypeScript集成

  • 改进的TypeScript支持,编辑器能提供强有力的类型检查和错误及警告
  • 更好的调试支持

5.用于处理大规模用例的新API

  • reactive
  • ref
  • computed
  • readonly
  • watchEffect
  • watch
  • unref
  • toRef
  • toRefs
  • isRef
  • isProxy
  • isReactive
  • isReadonly
  • customRef
  • markRaw
  • shallowReactive
  • shallowReadonly
  • shallowRef
  • toRaw

6.分层内部模块

Vue 3.0内核仍然可以通过一个简单的“标签使用,但其内部结构已被彻底重写为一组解耦的模块。新的体系结构提供了更好的可维护性,并允许最终用户通过摇树来减少运行时大小的一半。

模块如下:

模块提供的功能点:

1.编译器支持用于构建时自定义的自定义AST转换(例如,构建时i18n)

2.核心运行时提供API用于创建针对不同渲染目标(例如本机移动设备WebGL终端)的自定义渲染器。默认DOM渲染器使用相同的API构建。

3.该@vue/reactivity模块导出的功能可以直接访问Vue的反应系统,并且可以用作独立程序包。它可以与其他模板解决方案(例如lit-html)配对,甚至可以用于非UI方案。

7.composition-api设计目的

旨在解决大型应用程序中Vue使用的难点。Composition API建立在反应性API之上,与2.x基于对象的API相比,可实现类似于React挂钩的逻辑组成和重用,更灵活的代码组织模式以及更可靠的类型推断。

8.性能改进(相对Vue2)

  • 通过摇树(减轻了多达41%的资源大小)
  • 初始渲染(加快了多达55%的速度)
  • 更新速度:(加快了133%的速度)
  • 内存占用:(最多减少54%)

9.提供的两个实验性功能

用于在SFC中使用Composition API的语法糖

SFC中状态驱动的CSS变量

迁移3.0指南

10.兼容IE11

计划在2020年第四季度重点关注它们。因此,计划迁移现有v2应用程序的用户或者需要IE11支持人员此时应意识到这些限制。

11.后续计划

  • 迁移版本
  • IE11支持
  • 新devtools中的Router和Vuex集成
  • Vetur中模板类型推断的进一步改进

新文档链接:

  • 新官网: v3.vuejs.org/
  • 官方介绍: github.com/vuejs/vue-next/releases/tag/v3.0.0
  • 迁移指南 : v3.vuejs.org/guide/migration/introduction.html
  • Composition API : v3.vuejs.org/guide/composition-api-introduction.html

以上就是W3Cschool编程狮关于Vue3.0正式发布,本次发布所有总结,一起看看!的相关介绍了,希望对大家有所帮助。

有个程序媛女朋友是什么体验?

thbcm阅读(194)

文章来源于公众号:Java极客技术 作者:鸭血粉丝

有人问阿粉,有个程序媛女朋友是一种什么体验。阿粉虽然没有,但是身边有案例,这不为了满足大家的好奇心,去问了一圈,结果问下来之后,阿粉酸了…

程序员 A :你问我有个程序媛女朋友是啥体验?第一体验就是脱单了!还有什么体验比这更好么?

阿粉摸了摸自己的头发,默默的低下了头

程序员 B :记得有次跟我女朋友表白,告诉她我爱她,在我心里她永远都是第一位的,结果遭到她的质问,在我心里谁是第零?!!!

程序员 C :我女朋友就是程序媛,然后有次她在家也加班来着,让我帮忙看一下代码

“你为啥要用要敲空格键呢?用 tab 键不香么?”“用空格键不管怎么看,格式都不会变”“怎么会有人用空格?空格键除了制造噪音外哪里比 tab 强了,而且我跟你讲,用 tab 键可以让减少文件的占用空间”“我就用空格键怎么了,我就爱用怎么了”“你是老大,代码自己看吧”

程序员 D :什么体验么?和程序媛在一起,周末的时候就不是逛街了,是对着撸代码,时不时再吐吐槽对方的代码写的都是啥

阿粉想到周末都是自己在看技术视频,我的眼泪才没有流下来,我明明在笑…

程序员 E :我调试喜欢打 log ,女朋友调试喜欢打断点,每次都是她求我让我帮忙看看她的程序,然后调着调着我俩就开始抢笔记本了,“你给我,你这么写不行,给我我来写”“不要,我觉得是这里的问题,让我再试试,肯定是这里的问题”“不对不对,不是那里的问题,我有思路,把笔记本给我,我给你写”……到最后我俩差点儿打起来,好委屈,刚开始不是她来求我的么,而且还要嘲笑我一番喜欢打 log ,调试明明打 log 无敌好吧……

程序员 F :乖,咱们左括号换个行成不

对方:不要!!!

遇上这么一个女朋友,我能怎样?

程序员 G :最大的体验就是,平时的饭就不要想着在家吃了,特别是晚饭,在家吃是不可能的,因为 50% 的概率大家都加班, 50% 的概率一个人等另一个人加班

以上就是W3Cschool编程狮关于有个程序媛女朋友是什么体验?的相关介绍了,希望对大家有所帮助。

10 个坏习惯 程序员应该避免

thbcm阅读(259)

每个程序员在工作生涯、学习经历中,都会难免会养成一些坏习惯。在本文中,小编向读者展示一些有了而且很难改的坏习惯。希望如果你刚接触程序员这个职业,能够尽量避免这些坏习惯;如果你已经有了这些坏习惯,那希望你能够意识到它们,并可以开始改变

一、休息不够

我敢肯定你们很多人或者说几乎所有人都对这个坏习惯感到十分罪恶。我也是,对没有休息或休息不够依然有罪恶感。曾有一段时间,我凌晨六点入睡,中午一点左右起来吃午饭,一直工作到第二天凌晨六七点。这简直太常见了,几乎每天都是如此。在工作任务紧急的时候,我做过许多可笑的事情。我想我们每个人面对DDL的时候都有过这样的情况,我要说的不是这种,而是你的日常习惯。

我建议你每天尽量多休息。我不能说那种具体而微的计划,因为每个人都不一样,但在一般意义上,你应该每小时都应该起身伸展四肢,活动一下,喝杯咖啡,吃点东西。很多时候,如果你思路停滞,那你需要休息一下,大脑得到休息有助于更容易地解决问题。所以找到适合你的休息方式。即使你认为自己不需要休息,但也试一下,你会发现自己的效率提高了不少。

二、拒绝寻求帮助

这一条在学习和工作场景中都很常见。我们很多人都不寻求帮助。原因很多,但我认为最主要的原因无非是自尊以及害怕让人认为自己啥也不会。我们很多人都有冒名顶替综合征,觉得自己不完全胜任自己的工作。我在公司里、在和客户打交道时、甚至在上课和辅导中都有这种感觉。我们认为寻求帮助会强化这种感觉。但事实上,它浪费了大量时间,阻碍了你的成长。

  • 其他真正的开发人员们都是视频或书籍一般的资源,甚至要比这些好太多。他们可以直接回答你的问题,帮助你真正的理解它。
  • 只有混蛋才会因为你问问题而批评你,这时候我会尽力避开这些人。
  • 如果你想要自己找到答案,不想寻求帮助,那也没问题,给自己一个时间限制。当你的前辈就在你旁边,他可能知道或至少可以帮到你的时候,千万不要浪费几天时间来寻找答案。

三、停止学习

我不管你是否是从业20余年的高级开发人员,你应该一直把自己当成一个学生。这比大多数职业的要求更高,因为这一行总在变化。没有一个开发人员可以知道所有事情的全部内容,他们这一分钟这样做,下一分钟就有变化发生,他们就必须学习更多。如果你骄傲自满,停止阅读、学习,你必然会落后。即便你有一份不需要学习任何新东西的工作,一旦你失去了那份工作(这并非不可能发生),你会远远落后。所以即使有这样的工作,我也建议你边工作边学习。

不管你喜欢那种语言、框架、库,你都要及时更新你的知识。如果你的职位需要的只是旧知识,这也是可理解的,因为很多领导倾向于认为如果公司还没没落,那就没必要修复。所以你仍然会发现有些团队用着过时的、不受支持的技术,只因为该技术还能运行。如果你边工作边学习这方面的新东西,那你就可以向团队展示更快、更高效、更简单的新技术是可能的。你或许能够说服他们更新技术、改善公司。

四、混乱的代码

这一条更多地是一种技术习惯,但也可能意味着很多事情。你应当以一种直观、高效、安全的方式写代码。这在自学的时候简直太难了,因为在相关的课程中,你学到的不是写某些代码的最佳方法,而是导师认为这么写最容易理解一些核心概念。所以,你必须进行一些额外的研究,找出清理你代码的最佳方法。

  • 我绝对建议你使用“避免重复代码原则”(DRY principle,don’t repeat yourself)。如果你遇到常见的代码块,那就创建某种“类”(class)或“函数”(function)来合并这部分功能,而不是仅仅重复它。这让代码更加简洁,节省了大量代码行,并且便于他人使用。
  • 你也要注意性能,比如压缩图片、缩小JavaScript和CSS。你可以使用task runner(如gulp)或其他工具自动完成压缩,面对小型任务,你甚至可以使用minifier.org等手动完成。
  • 另外,不要进行不必要的API接口调用,要使你的全栈应用程序以尽可能少的请求完成你需要的全部功能。以及测试……我在这上面犯了很多错,没有做足够的测试。就我所知,单元测试有助于搭建更健全的应用程序,避免潜在问题,节约时间和开销。但我就是很讨厌它,这可能是我最坏的习惯之一,也是我成为更好的开发人员需要解决的大问题。有时候我们为了节约时间减少测试,但在现实中,这样做会降低应用程序的性能、效率和可读性,而且与一开始就采用正确的方法相比,它可能在将来引起更大的麻烦。所以请记住这一点。

五、工作和生活的不平衡

工作和生活的平衡真的很重要,尤其是当我们成家以后。不管什么类型的程序员都要花大量的时间在工作上,这有很多原因:事情总在发生变化;我们遇到的问题让我们寸步难行;我们需要研究这些问题等等……这就导致了很多时候,我们不得不工作到很晚、很早就开始工作、周末也加班。这会让你远离生活中的一切,比如你爱的人们、你的个人爱好。你可能喜欢运动、远足或是在外吃大餐,但假如你一直要加班,你就没法做这些让你快乐的事情。

我在这个领域有很多经验。我和妻子有两个孩子,其中一个患有自闭症,但我没法像我想的那样花大量的时间陪他们。我面临着双重困境,因为我既要处理编码问题,又要处理内容创作者的问题,要不断地提出新想法,有质量地记录下来等等。

如果你是自由职业者,为自己工作,所有的生计都取决于完成工作,你只有把所有的事情做完才能拿到报酬。有报酬,也就必然得承受持续不断的担忧:自己是否能跟上进度、自己能否完成任务。压力真的很大,让我们不得不把其他事情从我们生活中挤走。并不是说在公司工作的人就不会经历这些,而是说当你需要考虑所有的事情的时候,压力等级完全不同。所以我很同情那些有自己生意的人。

但即便如此,你也不能让它控制你的生活,你必须为你的家人和朋友留出时间,或者更坦率地说,为你自己留出时间。生活可远比写几行代码丰富多了。做你自己喜欢的事情,给你的生活带来平衡。

六、糟糕的办公室政治

这一条是给那些在公司工作的人们的。你和其他人一起工作,但这反过来又会引起冲突、分歧、争论等等。许多开发人员十分傲慢,总认为自己是正确的,即使他们知道自己犯了错,他们中的一些人也永远不会承认。我不是说这是大多数开发人员,但我认为我们至少会遇到一位这样的人。我听很多人说,他们的团队非常棒,成员之间相处得非常融洽,这很好,但不是所有团队都是这样子。

  • 很多时候你们会在想法和解决方案上发生冲突,试着表现得老练和对对方的尊重,但同时,也不要太容易就被说服,尤其是当你对自己的提案非常有信心的时候。
  • 不要大喊大叫,不要骂人,千万别做这些事情,这什么用也没有。
  • 如果他们开始这样对待你,你就离开,做一个更强大的人。
  • 如果你不幸遇到了一个十足混蛋的团队成员,他完全不听理由,你除了想办法避开他之外,别无他法。
  • 在某些情况下,你可能不得不和你们的领导交谈,不过我还是建议你先和那个人谈谈。

七、不能从错误中吸取教训

作为一位开发人员,你会犯很多错误,这不可避免也没什么大不了的。但如果你总是犯同样的错误,不能从中吸取教训,那就有问题了。

当你犯错后,我建议你遵从这样一个流程:

  • 找出错误的根本原因是什么;
  • 找到可以防止错误再次出现的流程;
  • 弄清楚如果早先发现了错误,能否避免后果。

当你犯下大错,请你仔细思考一下这三件事,很有可能就不会再犯这个错误了,或者你至少下次会尽早发现错误,避免情况恶化。同时,不要因为犯错而对自己太苛刻,谁都会犯错。

八、太早放弃

挫折是编程的重要环节。我制作了一些关于挫折及其应对的视频。我也见过很多人因为沮丧而在特定的项目和编程上过早言弃。一些项目确实特别难,似乎一旦你修复了一些bug,它就会导致另一个bug,bug复bug无穷尽也。你可能开始觉得自己处理不了,自己本可以去做别的,做这个就是浪费时间浪费生命浪费金钱……你开始产生很多消极的想法。如果你很快就放弃某个项目,或是很快辞职,那么拟投入到这个项目或工作中的一切都将化为泡影。

我并不是说你啥项目都不能放弃,只是我看过太多次人们在即将成功的前夜放弃了。所以在放弃之前,确保你试尽了所有可能:已经上下求索、寻求帮助、用遍方法、换个技术、休息很长一段时间让自己的思绪恢复正常、如果可能的话已经搁置了一段时间……你在放弃之前做了所有能做的事情,如果还是失败,那么也许是时候放弃它、继续前进了。成功可能就在眼前,如果你这时候放弃,真的太耻辱了。

九、做一个无所不知的人

我之前提到过,我认为一些开发人员十分傲慢的原因是,他们认为自己无所不知。他们从不听其他人的意见,他们为啥要听呢?他们知道所有的答案。如果你周围全是这样的人,那真是糟透了,这对你也没好处,因为你也会认为自己无所不知,就会不积极学习、不改善自己。我敢打赌,总有一天你会被自己犯下的错误叫醒,这源于你不听取任何人的意见,也不自己进行调研。

这些人大多数都是Stack Overflow网站(一个IT技术问答网站)的魔鬼,他们取笑新开发人员的问题,嘲笑他人的回答,一有机会就点击“反对”。我讨厌死这些人了。我认为他们中的许多人在学校里就总是被人找茬,他们利用自己的知识欺负其他可能有问题或是没有所得的开发人员,看上去像是忘掉了被欺负的感受,只想要复仇。我这个理论可能有问题,只是一家之言。

无论他们出于什么原因这么做,我认为如果他们思想开放、欢迎不同的想法、尊重他人,那他们就会比总是想要“正确”的人快乐多了。他们可能是团队中最聪明的人,也是最糟糕的人,因为没人愿意和他们合作,无法进行良好的沟通。对于一个想要成功的团队来说,沟通和团结是必需的,而无所不知的人会破坏这些。所以,如果你是这样的人,试着别那么自负,开放一点,尊重一点,你会走得更远。

十、不接受建设性批评

最后一条和上一条有点联系,不能接受建设性批评。无所不知的魔鬼和真心想要帮助你的人之间还是有很大区别的。有时你很难看清这个区别,因为别人指出你的错误或者不完善的地方,你可能感觉不是那么好,感觉自己受到他人的攻击。

但很多情况下不是这样的,这只是某个人想教给你一个更好的方法,或是分享自己的意见。作为内容生产者,我花了很久才学会分辨挑事者和好心人。一开始,有人谈论我如何做某事的时候,我都很有戒心。但我意识到,他们中的许多人只是在试图提供合理的帮助。如果他们并非不尊重,或者只是对一些无关紧要的事情挑三拣四,那我就需要把它当做对我和对我的知识有益的东西。

建设性的批评是学习的绝佳资源,因为建设性的批评很有针对性,是你现有的问题,是他人给你的具体解决方案,这是无价的。事实上,代码审查简直太棒了,你可以得到其他人的建议,改进你的代码甚至是你自己。

所以不要把这类事情都看成人身攻击,除非你真的受到了攻击,或是故意的取笑和不尊重。听进别人的批评或改进建议总是很难,但最终,这会让你成为一个更棒的开发者。

无论是新开发人员,还是经验丰富的老手,我都希望这些建议能够帮助到你们。

以上就是W3Cschool编程狮关于10 个坏习惯程序员应该避免的相关介绍了,希望对大家有所帮助。

联系我们