分类目录归档:写写代码

JavaScript this 值

JavaScript 中 this 的指向根据上下文及使用方式而有所不同。

全局上下文 Global Context

在全局上下文(不在函数体内)中总指向全局对象,如在浏览器中是 window,在 Node.js 则是 global

函数上下文 Function Context

  • 简单调用
    • 严格模式:undefined
    • 非严格模式:全局对象
    • 可用 callapply 来改变 this
  • 作为对象的方法:指向直接所在的对象,如 a.b.c()cthis 指向 b,适用于普通的对象、原型链方法和 getter/setter
  • 作为构造函数,指向正在构建的对象
  • 作为 DOM 事件处理函数,指向触发事件的元素(指的是外层函数,如果是多层,遵循其余规则)
  • bind:用 f.bind(object) 来返回新的绑定 this 的函数,不论怎么调用,如通过对象方法调用、callapply 都不会改变 this。再次 bind 也不会改变。
  • 箭头函数(arrow function):this 指向词法上下文(实际运行生成箭头函数时上下文)的 this,不会被调用方式改变,但会因生成方式而不同。
var obj = {
    bar: function() {
        var x = (() => this);
        return x;
    }
};

var fn = obj.bar();
console.log(fn() === obj); // true

var fn2 = obj.bar;
console.log(fn2()() == window); // true

继续阅读

import 与 require 的两个差异

Javascript 的模块化开发,除了 RequireJS 代表的 AMD 外,目前广泛使用的是 Node.js 代表的 CommonJS 以及 ES6 的 module。

在 babel / webpack 等工具链下,大多数情况下,CommonJS 的 require 和 ES6 的 import 效果等同,babel 的 preset 是 ES2015 以及 webpack 都是把 import 转为 require(webpack 根据 target 再转为 AMD 或 UMP 等格式)。

但两个规范实质上并不等同,所以存在着不少的区别,如不能 import(var) 但能 require(var),详细可查看 ES6 模块系统,此处记录一下两个可能比较直接影响代码书写的差异点。

继续阅读

Java module

模块的主要作用有:

  • 模块间需要显示地声明(模块)依赖,而不是以前的简单的基于 classpath
  • 更强的封装隔离,模块不主动导出的话无法被 import;不 open 则无法对其使用反射技术

指令

模块通过在模块根目录下的 module-info.java 中的指令来约束使用。

定义模块

最好(或则是必须,没找到规定说明) moduleName 和文件夹名称一致,否则 javac 编译时提示 module 和预期的不一致

module moduleA {
}

引入依赖

requires moduleB;

可加 transitive 来表示依赖传递,如 requires transitive moduleB;,然后在 moduleCrequires moduleA; 后,那么在 moduleC 中可使用在 moduleB 中导出的类或接口。

导出 package

exports packageFullName;

表示当别的模块(A) requires 了当前模块,那么 packageFullName 这个包(及子孙包)里的 publicprotected 的类和接口可在模块 A 里被访问使用。

可用 exports packageFullName to moduleA, moduleB 来表示仅 moduleAmoduleB 可访问 packageFullName

提供服务

provides full.name.Service with full.name.concreateService, full.name.concreateService2;

表示该模块向外提供 full.name.Service 的实现 full.name.concreateServicefull.name.concreateService2

使用服务

uses full.name.Service;

表示该模块需要使用 full.name.Service。同时,不直接依赖具体的实现,而是使用抽象类或接口。然后搭配 ServiceLoader 或别的 IoC 工具来做依赖注入,从而达到和具体实现解耦的目的。

配合反射

opens fullPackageName;

表示 fullPackageName 可被使用反射技术。也可使用 opens fullPackageName to moduleA, moduleB 限定开放反射的范围。

open module moduleD {} 表示整个模块都是开放的。

其它

java --list-modules 查看 JDK 内置的模块

编译运行

可通过 javac -d mods --module-source-path src $(find src -name "*.java") 来编译运行(多个模块),src 表示各模块所在的目录。

也可使用 javac -d dist --module-source-path src -m client 来编译指定的模块。

继续阅读

浏览器端 async/await 还是有点代价的

对于下面这段 js

function async3() {
  createPromise()
    .then((res) => {
        console.log(res);
    })
    .catch((err) => {
        console.log(err);
    })
    .finally(() => {
        console.log('finally');
    });
}

其对应 async/await 版本是

async function async2() {
    try {
      const ret = await createPromise();
      console.log(ret);
    } catch (e) {
      console.error(e);
    } finally {
      console.log('finally');
    }
}

经 babel 转为 ES5 后,除了必要的 babel-polypill,会生成以下代码,其中 _asyncToGenerator 不会多次生成。

var async2 = function () {
  var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
    var ret;
    return regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            _context.prev = 0;
            _context.next = 3;
            return createPromise();

          case 3:
            ret = _context.sent;

            console.log(ret);
            _context.next = 10;
            break;

          case 7:
            _context.prev = 7;
            _context.t0 = _context['catch'](0);

            console.error(_context.t0);

          case 10:
            _context.prev = 10;

            console.log('finally');
            return _context.finish(10);

          case 13:
          case 'end':
            return _context.stop();
        }
      }
    }, _callee, this, [[0, 7, 10, 13]]);
  }));

  return function async2() {
    return _ref.apply(this, arguments);
  };
}();

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

写法上直观了的,但在浏览器端,还是有些代价,生成的代码会多出不少。

fetch

实现中的 fetch 是希望能比 XMLHttpRequest 更好地做网络请求。

fetch('/')
    .then(res => { 
       if(res.ok) {
         return res.text();
       } else {
         throw new TypeError(res.statusText);
       }
    })
    .then(text => console.log(text.length))
    .catch(err => console.error(err.message));

Reference

注意点:

  1. fetch 仅在网络因素下 reject,业务的错误,如 502 或 404 等会 resolve,需要判断 response 是否 ok
  2. 默认不携带 cookie,需要明确指定
  3. 跨域时会受 CSP 控制影响,需要配置

JavaScript 中的零除

一直意味在 JavaScript 中也和别的语言一样会触发错误,所以一直依赖使用除法都先小心地做个判断。但偶然中却发现并不需要在做除法前判断,因为不会报错,只需要拿到结果后进行处理即可。

发现不会报错后,稍微去看了一下规定:

Infinity / Infinity = NaN
Infinity / 0 = Infinity
-Infinity / 0 = -Infinity
Infinity / nonZeroFinite = Infinity
-Infinity / nonZeroFinite = -Infinity
aPositive / Infinity = 0
aNegative / Infinity = -0
0 / 0 = NaN
aPositive / 0 = Infinity
aNegative / 0 - -Infinity

奇偶校验位

奇偶校验位是一种简单的错误检测码。

分为偶检验位和奇校验位两种。对于偶校验位来说,如果 01 序列中的 1 的个数是奇数,那么校验位是 1,使得添加校验位后的 1 的个数是偶数,否则校验位是 0;奇校验位刚好相反。

从定义来说,可以看到,奇偶校验仅能检测奇数个位数发生反转的错误,不能检测偶数个位发生反转的情况,也不能确定哪些位发生了反转,故不能纠错。
继续阅读

Grunt files 扩展名注意点

假设 src 目录下有以下文件:

src/
├── a.js
└── c.test.js

然后使用以下 grunt 配置文件来把 src 里的 js 文件复制到 dest 目录,把扩展名改为 -min.js

grunt.initConfig({
  copy: {
    main: {
      expand: true,
      cwd: 'src/',
      src: '*.js',
      dest: 'dest/',
      filter: 'isFile',
      ext: '-min.js'
    }
  }
});

复制后的文件将是:

dest/
├── a-min.js
└── c-min.js

继续阅读

Touch 事件备忘

过了一次文档,做个备忘。

Touch 接口

每一个单独的触摸点,不可变。

interface Touch {
    readonly attribute long        identifier;
    readonly attribute EventTarget target;
    readonly attribute long        screenX;
    readonly attribute long        screenY;
    readonly attribute long        clientX;
    readonly attribute long        clientY;
    readonly attribute long        pageX;
    readonly attribute long        pageY;
};

继续阅读