我们来浅谈代码语言的魅力

2023-10-27

01 浅谈 V8 Hidden Classes 和 Inline Caches

Javascript 是动态的、基于属性链的语言,V8 是流行的 JavaScript 运行引擎。我们知道在运行时可以改变对象的属性和类型。为了定位对象的属性和类型,V8 引入隐藏类(Hidden Classes)概念,用于优化属性访问速度。看如下代码:

function Person(name, age) {
  this.name = name;
  this.age  = age;
}

const zhangsan = new Person('Zhangsan', 20);
const xiaofang = new Person('Xiaofang', 21);
xiaofang.gender = 'female';

初始化 Person 类时会给对象 zhangsan 创建隐藏类 C0,C0 还没有包含任何属性。当初始化属性 zhangsan.name 时会基于 C0 创建出新隐藏类 C1。由此类推,属性 age 初始化后,生成 C2。当初始化对象 xiaofang 时,由于属性结构相同其会复用 C0、C1、C2。但是给对象 xiaofang 增加属性 gender 时,其会基于 C2 生成新隐藏类 C3。

但如果你不小心执行了 delete 操作,那么情况就比较糟糕,如下:

function Person(name, age) {
    this.name = name;
    this.age  = age;
}
for (let i=0; i<1000000; i++) {
    const xiaofang = new Person('Xiaofang', 21);
    delete xiaofang.name;
}

上述代码加上 delete 语句,执行耗时 230ms。而去掉 delete 语句,执行耗时 4ms。差距如此大的原因就是在于隐藏类的破坏。我们可能一不小心在某些场景使用了 delete,特别是大循环体内时,性能退化就明显了。我们可以使用赋值 null 替换 delete。

V8 更进一步做了 Inline Caches 策略,可进一步提高属性访问速度。当多次访问属性时,如果能保持一个类的方法的参数类型不变,上下文不变,执行过程点不变,那么根据该缓存策略,函数内对象属性查找过程就能避免,提高函数执行速度。我们经常可能喜欢在函数体内定义函数,这些应该尽量避免。

02 浅析 Java 逻辑运算与位运算

逻辑运算与位运算

算数、赋值、逻辑、关系、自增自减、条件以及位运算等丰富的运算和相应的运算符是 Java 语言的主要特点之一,也是我们学习一门语言时的基础。本文我们主要简单了解一下 Java 中的逻辑运算和位运算,以及我们实际开发过程中如何更有效的应用它们的特性。

首先了解什么是逻辑运算和位运算:

逻辑运算:逻辑运算又称布尔运算。逻辑运算符要求操作数的数据类型为逻辑型,其运算结果也是逻辑型(boolean)值。逻辑运算符把各个运算的关系表达式连接起来组成一个复杂的逻辑表达式,以判断程序中的表达式是否成立,判断的结果是 true 或 false。

位运算:位运算是以二进制位为单位进行的运算,其操作数和运算结果都是整型值。这些整数类型包括 long,int,short,char 和 byte。

运算符

Java 中是如何执行这些运算的呢?那就是通过运算符。

逻辑运算符:短路与 "&&"、短路或 "||"、逻辑非 "!"、逻辑或 "|"、逻辑与 "&"。

  • &&(短路与)与 &(逻辑与)区别:看含义,区别就在于 "短路",看说明 "如果 a 为 false 则结果为 false,且不会计算 b"(因为不论 b 为何值,结果都为 false)。

  • ||(短路或)与 |(逻辑或)区别:同上区别同样在 "短路"。a | b : 如果 a 为 true 则结果为 true,但会继续计算 b(不具备短路能力时,会计算所有逻辑项)。

实践一

基于以上的学习,下面这个问题,在实际开发过程中你会如何决策呢?假设你有一个需求需要同时判断多个条件,这些条件你会有意识的做前后排序还是简单的无序罗列呢?如下你会选择方案 1 还是方案 2 呢?

public class Test {
    public static void main(String[] args) {
        // 方案1、a() && b()
        System.out.println(a() && b());
        // 方案2、b() && a()
        System.out.println(b() && a());
    }

    /**
     * 一个耗时的逻辑判断
     */
    public static boolean a() {
        System.out.println("do a!");
        boolean a = false;
        // 耗时耗性能的逻辑
        for (int i = 0; i < 1000000; i++) {
            // 耗时操作
            a = true;
        }
        return a;
    }

    /**
     * 一个简单的逻辑判断
     */
    public static boolean b() {
        System.out.println("do b!");
        return false;
    }
}

想必大家都能做出正确的选择,这其实是一个非常小的知识点,但平时开发过程很多人都会忽视。我们在日常开发过程中要注重细节,越是基础的逻辑越能积蓄更大的能量,养成良好的编程习惯以及性能意识。

短路与(&&)和短路或(||)由于短路机制的存在,能够优化逻辑运算的计算,从而提高效率。在实际开发过程中,应该有意识的使用短路与和短路或的短路能力,来优化我们的逻辑运算。

位运算符:包含位逻辑运算符(位与 '&',位或 '|',位非 '~',位异或 '^'),位移运算符(右移 '>>',左移 '<<',右移补零 '>>>')

逻辑比较简单,我们不再详述,我们主要看看位运算如何应用到我们的日常实际开发中。也许我们会发现,我们日常开发中好像并不经常用到位运算,但我们在看 Android 源码的时候确能经常看到相应的使用。比如 View 中的 mPrivateFlags、mPrivateFlags1、mPrivateFlags2、mPrivateFlags3 等等,它们一个变量甚至可以保存几十个不同的状态,这也就是通过位运算能够达到的一个优势,可以让 “多状态” 的管理像单状态管理一样简单高效。

实践二

假设一共有七种颜色,一个物体同时能拥有多种颜色,我们该如何简单高效的维护该物体的颜色属性呢?

1、维护一个颜色属性集合,每拥有一个颜色就向集合中添加,移除则删除?

2、维护 7 个布尔值指向不同的色值,每拥有一个就设置为 true,否则为 false?

以上只是简单的举几个例子,比如用布尔值来维护状态,这也是我们最常用的方法,这种对单状态的维护会很简单和直观,但对多状态的维护就会显得不那么适合。

这时我们就可以尝试考虑采用位运算的方式。回顾位运算的按位与和按位或,按位或在对应位不同时则为 1,是不是等同于我们添加了一个状态?同理,按位与在对应位都是 1 时则为 1,是不是就可以判断是否具有某个状态?我们则可用不同位的 1 来表示不同的状态,最终结果具有哪个位置的 1 就说明具有哪个状态,这样我们就能以一个属性来同时管理多个状态了。

public class Test {
    /** 单属性维护多状态 */
    private static int mColors = 0B00000000;
    private static final int RED = 0B00000001;
    private static final int ORANGE = 0B00000010;
    private static final int YELLOW = 0B00000100;
    private static final int GREEN = 0B00001000;
    private static final int BLUE = 0B00010000;
    private static final int PURPLE = 0B00100000;
    private static final int PINK = 0B01000000;

    public static void main(String[] args) {
        // 同时具有红绿蓝三色,mColors = 0B00011001
        mColors = mColors | RED | GREEN | BLUE;
        System.out.println(mColors);
        // 判断是否具有红色,true
        System.out.println((mColors & RED) != 0);
        // 判断是否具有黄色,false
        System.out.println((mColors & YELLOW) != 0);
        // 添加黄色,mColors = 0B00011101
        mColors |= YELLOW;
        // 判断是否具有黄色,true
        System.out.println((mColors & YELLOW) != 0);
        // 移除黄色,mColors = 0B00011001
        mColors &= ~YELLOW;
        // 判断是否具有黄色,false
        System.out.println((mColors & YELLOW) != 0);
    }
}

总结


基础语法中存在容易让人忽视的特性,有时却会有很大的妙用,掌握这些特性,能够为我们解决实际问题提供更多的可能性,以及拓宽我们的视野。

03 理解 Golang 的 type func ()


在 Go 语言中,可以用 type 关键字自定义类型,比如常用的:

type Myint int // 定义了MyInt类型,其基础类型是int,与int有相同的底层数据结构,但是是完全不通的两种类型
type Book struct {  // 定义了一个Book结构体,包含Title和PageNum两个字段
    Title    string
    PageNum  int
}

同样地,Go 语言也支持自定义函数类型,具有相同的参数和返回值列表(不包括参数名与函数名,需要参数和返回值列表的类型与顺序一致)的函数被视为同一种类型。既然函数被当做一种类型可以被定义,那同样地,再 Go 语言里,函数可以像其他类型一样,被当做普通的值,再其他函数之间传递、作变量赋值、做类型判断和类型转换,举个例子:

type PowerCanculator func(num int) int   // 幂次计算
 
func PowerBase2(num int) int {    // 以2为底的幂次计算
    return 1 << num
}
 
func main() {
    var pc PowerCanculator
    var result int
    pc = PowerBase2
    result = pc(2)
    fmt.Println(result) // 结果为4
}

在以上例子里,PowerBase2 作为 PowerCanculator 类型的一种实现,被当做参数赋值给一个 PowerCanculator 类型的函数变量并调用。

那么函数被作为参数传递有什么好处或者妙用呢,我们可以考虑这样一个场景:我们希望有个计算器,能够进行任意数字计算,且所有参数和具体的操作都由调用方给出,这样我们的计算器仅做统一调度即可,该如何实现?

type PowerCanculator func(num int) int   // 幂次计算
  
func PowerBase2(num int) int {    // 以2为底的幂次计算
    return 1 << num
}
 
func PowerBase4(num int) int {    // 以4为底的幂次计算
    return 1 << (num+1)
}
 
func Canculator(num int, pc PowerCanculator) (int, error) {
    if pc == nil {
        return 0,errors.New("param error")
    }
    return pc(num),nil
}
 
func main() {
   result1, err := Canculator(2, PowerBase2)
   if err != nil {
      fmt.Println(err)
   }
   fmt.Println(result1) //结果为4
   result2, err := Canculator(2, PowerBase4)
   if err != nil {
      fmt.Println(err)
   }
   fmt.Println(result2) //结果为16
}

以上是一个函数被当做另外一个函数的参数传入的例子,那么函数既然被当做了一个普通参数,那么它是否也可以被当做另外一个函数的返回值返回呢,答案是可以:

fun PowerCanculatorGenerator(base int)  PowerCanculator {
    switch base{
    case 2:
        return PowerBase2
    case 4:
        return PowerBase4
    default:
        return nil
    }
}

func main() {
    var base int
    var powerBase2 PowerCanculator
    var powerBase4 PowerCanculator
    
    powerBase2 = PowerCanculatorGenerator(2)    
    powerBase4 = PowerCanculatorGenerator(4)
    fmt.Println(powerBase2(2)) //结果为4
    fmt.Println(powerBase4(2)) //结果为16
}

以上是一个简单的例子,其实函数类型最典型的应用场景,是进行 http 服务路由注册的时候,比如说现在有两个 http 请求 uri 分别是 /sayhi 和 /saybye,在进行 http 请求实现时,可以这么写:

func mHttp() {
    http.HandleFunc("/sayhi", hi)   
    http.HandleFunc("/sayBye", bye)
    http.ListenAndServe("0.0.0.0:8888",nil)
}
 
func hi(w http.ResponseWriter, r *http.Request) {
 
}
 
func bye(w http.ResponseWriter, r *http.Request) {
 
}

其中在 http 包里就存在一个函数类型:

type HandlerFunc func(ResponseWriter, *Request)

不难看出,HandlerFunc 是一个函数类型,有两个参数,而 hi 和 bye 是对 HandlerFunc 的实现,通过指定 HandleFunc 里函数变量的值,实现不同服务的路由注册。

以上内容,其实总得来说是在说明函数的两个关键特性:

1. 可以被当做参数传入函数;

2. 可以作为返回值从函数中返回。

其实,函数在 Go 语言中被称作『一等公民』,除了拥有函数的基本特性和上述两个特性之外,还具备更多特殊运用,比如对函数进行显式类型转换等,这里就不多做赘述,感兴趣的同学可以自行查阅相关文章和书籍~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我们来浅谈代码语言的魅力 的相关文章

  • 从直方图计算平均值和百分位数?

    我编写了一个计时器 可以测量任何多线程应用程序中特定代码的性能 在下面的计时器中 它还会在地图中填充花费了 x 毫秒的调用次数 我将使用这张图作为我的直方图的一部分来进行进一步的分析 例如调用花费了这么多毫秒的百分比等等 public st
  • 使用 JavaScript 防止网页导航离开

    如何使用 JavaScript 防止网页导航离开 Using onunload允许您显示消息 但不会中断导航 因为为时已晚 然而 使用onbeforeunload将中断导航 window onbeforeunload function re
  • 如何绕过Access-Control-Allow-Origin?

    我正在一个平台上对我自己的服务器进行ajax调用 他们设置了阻止这些ajax调用的平台 但我需要它从我的服务器获取数据以显示从我的服务器数据库检索到的数据 我的 ajax 脚本正在运行 它可以将数据发送到我的服务器的 php 脚本以允许其处
  • JQuery $.ajax() 在 java servlet 中发布数据

    我想将数据发送到 java servlet 进行处理 数据将具有可变长度并采用键 值对 A1984 1 A9873 5 A1674 2 A8724 1 A3574 3 A1165 5 数据不需要这样格式化 这就是我现在的方式 var sav
  • Java、Spring:使用 Mockito 测试 DAO 的 DataAccessException

    我正在尝试增加测试覆盖率 所以我想知道 您将如何测试 DAO 中抛出的 DataAccessExceptions 例如在一个简单的 findAll 方法中 该方法仅返回数据源中的所有数据 就我而言 我使用 Spring JdbcTempla
  • Spring Data JPA:查询如何返回非实体对象或对象列表?

    我在我的项目中使用 Spring Data JPA 我正在演奏数百万张唱片 我有一个要求 我必须获取各种表的数据并构建一个对象 然后将其绘制在 UI 上 现在如何实现我的 Spring 数据存储库 我读到它可以通过命名本机查询来实现 如果指
  • 如何使用 Javascript 在 html 文件中搜索字符串?

    我有 5 个 html 文件 并且有一个搜索表单 我想用它来搜索这些 html 文件中的文本
  • IntelliJ 组织导入

    IntelliJ 是否具有类似于 Eclipse 中的组织导入功能 我拥有的是一个 Java 文件 其中多个类缺少导入 例子 package com test public class Foo public Map map public J
  • Lombok @Builder 不创建不可变对象?

    在很多网站上 我看到 lombok Builder 可以用来创建不可变的对象 https www baeldung com lombok builder singular https www baeldung com lombok buil
  • Javascript等待/异步执行顺序

    所以我试图把我的头脑集中在 Promise await async 上 我不明白为什么当 go 执行时 带有 finished 的警报会紧随 console log coffee 之后 当所有函数都使用等待 承诺时 为什么它只等待 getC
  • 无需登录即可直接从 Alfresco 访问文件/内容

    我的场景是这样的 我有一个使用 ALFRESCO CMS 来显示文件或图像的 Web 应用程序 我正在做的是在 Java servlet 中使用用户名和密码登录 alfresco 并且我可以获得该登录的票证 但我无法使用该票证直接从浏览器访
  • 我可以限制分布式应用程序发出的请求吗?

    我的应用程序发出 Web 服务请求 提供商处理的请求有最大速率 因此我需要限制它们 当应用程序在单个服务器上运行时 我曾经在应用程序级别执行此操作 一个对象跟踪到目前为止已发出的请求数量 并在当前请求超出允许的最大负载时等待 现在 我们正在
  • Hadoop NoSuchMethodError apache.commons.cli

    我在用着hadoop 2 7 2我用 IntelliJ 做了一个 MapReduce 工作 在我的工作中 我正在使用apache commons cli 1 3 1我把库放在罐子里 当我在 Hadoop 集群上使用 MapReduceJob
  • 如何将数据推送到嵌套对象

    如何将另一个元素推入variables来自以下对象的属性 var request name Name id 3 rules name Rule name tags tagId 1 variables variable var1 matchT
  • 记录类名、方法名和行号的性能影响

    我正在我的 java 应用程序中实现日志记录 以便我可以调试应用程序投入生产后可能出现的潜在问题 考虑到在这种情况下 人们不会奢侈地使用 IDE 开发工具 以调试模式运行事物或单步执行完整代码 因此在每条消息中记录类名 方法名和行号将非常有
  • 将对象从手机共享到 Android Wear

    我创建了一个应用程序 在此应用程序中 您拥有包含 2 个字符串 姓名和年龄 和一个位图 头像 的对象 所有内容都保存到 sqlite 数据库中 现在我希望可以在我的智能手表上访问这些对象 所以我想实现的是你可以去启动 启动应用程序并向左和向
  • 如何从 Cloud Functions for Firebase 文件夹读取证书文件

    我正在尝试读取 certs 文件夹下的文件 如下所示 functions certs idp public cert perm 这是我用来读取文件的代码 fs readFileSync path join dirname certs idp
  • 如何使用通配符模拟泛型方法的行为

    我正在使用 EasyMock 3 2 我想基于 Spring Security 为我的部分安全系统编写一个测试 我想嘲笑Authentication http docs spring io autorepo docs spring secu
  • Safari 扩展将消息发送到特定选项卡

    有没有办法从全局页面发送消息到特定选项卡 我目前正在做的是 在创建选项卡时 注入的脚本会创建一个唯一的 ID 并将包含该编号的消息发送到全局页面 并且全局页面会保存该编号 如果全局页面需要发送一些数据到一个tab 即 tab 3 然后全局页
  • 即使调整大小,如何获得屏幕的精确中间位置

    好的 这个问题有两部分 当我做一个JFrame 并在其上画一些东西 即使我将宽度设置为 400 并使其在一个项目击中它时 当然 允许项目宽度 它会反弹回来 但由于某种原因 它总是偏离屏幕约 10 个像素 有没有办法解决这个问题 或者我只需要

随机推荐

  • python-获取当前目录/上级目录/上上级目录...

    获取文件 在其他人使用你的代码时 常常因为目录层级的问题为无法运行程序 所以在引起其他模块文件时 尽量采取绝对路径的方式导 python官方建议尽量的使用绝对目录 而不是相对目录 获取当前文件的绝对路径 os path abspath fi
  • 公式微分后,为什么是偏导的相加

    二郎在研究一个公式中 会涉及分析变量对最终结果产生影响时 注 最终结果 这里确实是最后需要获得的结果 数学公式一定要和物理对应 输入放在一边 输出放在一边 否则都放在一边 就全成自变量了 变量对最终结果影响 既然涉及了分析 那么我们就先说为
  • c++中分文件编写

    c 中分文件编写 本文内容来自某视频教程 分文件格式编写格式 h中写类的成员函数声明 cpp中写成员函数实现 代码案例 所有代码写在一个文件中 如下是一段没有分文件编写的代码 即将所有代码写在一个源文件中 如图所示 以下代码实现了利用全局函
  • nginx转发wss

    做了一个tp6 swoole的项目 没找到swoole开启ssl的资料 想起以前做过转发可以实现 就新建了一个站 然后转发到swoole的端口 也能实现wss的访问 下面是配置文件 upstream wss server 127 0 0 1
  • ERP、SCM、CRM的区别和联系?

    ERP 企业资源计划 SCM 供应链管理系统 CRM 客户关系管理 是当前企业信息系统三大热门话题 无论是ERP SCM 还是CRM其根本目标都在于提高企业管理水平 提高企业核心竞争力 本文怡海软件将分别从其含义 区别 联系 集成方面进行简
  • Unity Scroll View踩坑记录

    Unity要想实现游戏中的背包滑动效果 最简单的方法就是使用ScrollView这个组件 这个组件可以实现拖动滑动的效果 如上图所示 我希望将这些Item实现背包的排版效果 第一步 现将所有的Item移到Content下 此时我们会发现部分
  • Java集合——Java8之HashMap

    一 前言 在分析jdk1 8后的HashMap源码时 发现网上好多分析都是基于之前的jdk 而Java8的HashMap对之前做了较大的优化 其中最重要的一个优化就是桶中的元素不再唯一按照链表组合 也可以使用红黑树进行存储 总之 目标只有一
  • Python爬取某短视频热点

    随着短视频的大火 不仅可以给人们带来娱乐 还有热点新闻时事以及各种知识 刷短视频也逐渐成为了日常生活的一部分 本 文以一个简单的小例子 简述如何通过Pyhton依托Selenium来爬取短视频 仅供学习分享使用 如有不足之处 还请指正 涉及
  • pyspark环境安装历史难题终于解决

    path JAVA HOME里面的D JAVA 1 jdk1 8 0 152 bin一定要全并且对应上 踩坑记录 已解决 报错 WARN ProcfsMetricsGetter Exception when trying to comput
  • 基于Java的OA系统的设计与实现

    源码及论文下载 http www byamd xyz tag java 摘 要 学习和研究办公自动化中涉及到的知识和技术是实现办公自动化系统的前提条件 通过学习研究 掌握了其中的关键技术之后 结合自身的理解 对其做出了相应的表述 同时也成功
  • 【第03例】IPD体系进阶

    目录 前言 专栏目录 具体内容 IPD 相关专栏推荐 专栏列表 作者简介 前言 今天继续来讲讲 IPD 中涉及的几个评审点 ADCP 是英文 Av
  • 彻底理解coookie、session、token

    一 发展史 1 很久很久以前 web基本上就是文档的浏览而已 既然是浏览 作为服务器 不需要记录谁在某一段时间里都浏览了什么文档 每次请求都是一个新的HTTP协议 就是请求加响应 尤其是我不用记住是谁刚刚发了HTTP请求 每个请求对我来说都
  • Linux下的Oracle连接

    1 进入Oracle su oracle 2 开启监听器 oracle localhost root lsnrctl status oracle localhost root lsnrctl start oracle localhost r
  • 微信小程序自定义 tab-bar(基于 wepy)

    背景 微信小程序提供的原生 tab bar 功能简单 样式单一 无法满足业务需求 项目中使用的是 wepy 1 x 框架 实现原理与原生类似 方案 一 使用组件 在每个Tab页引入 修改全局配置 app wpy export default
  • CloudCompare--安装和简单的使用方法

    CloudCompare 安装和简单的使用方法 CloudCompare工具是一个非常好的处理点云数据的开源工具 有个不错的框架 很多公司对该工具进行二次开发以满足公司需要 第一次使用CloudCompare感觉非常好用 有兴趣的可以多了解
  • C语言进阶知识点(持续跟新)

    还是有点儿进阶的知识点 1 大段 小段内存模型 int val 0x12345678 int p1 val char p2 char p1 printf x n p2 p2 printf x n p2 short p3 val printf
  • windows server 2012 双网卡配置

    别用route 命令 在使用最新版的windows server 2012的时候 当存在两个或者多个网段的时候 就可以采用双网卡的方式来添加和配置路由 具体的设置方法如下 网段1 192 168 0 0 网段2 192 168 1 0 20
  • Go的 context 包的使用

    文章目录 背景 简介 主要方法 获得顶级上下文 当前协程上下文的操作 创建下级协程的Context 场景示例 背景 在父子协程协作过程中 父协程需要给子协程传递信息 子协程依据父协程传递的信息来决定自己的操作 这种需求下可以使用 conte
  • 337. House Robber III

    The thief has found himself a new place for his thievery again There is only one entrance to this area called the root B
  • 我们来浅谈代码语言的魅力

    01 浅谈 V8 Hidden Classes 和 Inline Caches Javascript 是动态的 基于属性链的语言 V8 是流行的 JavaScript 运行引擎 我们知道在运行时可以改变对象的属性和类型 为了定位对象的属性和