JavaScript之常量

简单类型常量

众所周知 ES6 新增的 const 关键字可以用来声明常量。常量是块级作用域,很像使用 let 语句定义的变量。常量的值不能通过重新赋值来改变,并且不能重新声明。

1
2
const name = 'zubin';
name = 'zhang'; //TypeError: Assignment to constant variable.

const 实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动,对于基本数据类型(Number、String、Boolean 等),值就保存在变量指向的内存地址,因此等同于常量。
对于复合类型(Object、Array 等)变量指向的内存地址,保存的只是一个指向实际数据的指针,const 只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。

1
2
3
4
5
6
7
8
9
10
11
12
13
const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop; // 123

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError

const a = [];
a.push('Hello'); // 可执行
a.length = 0; // 可执行
a = ['Dave']; // TypeError

复合类型常量

如果真的想将对象冻结,应该使用 Object.freeze 方法,冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。该方法返回被冻结的对象。

1
2
3
4
5
const foo = Object.freeze({});

// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;

除了将对象本身冻结,对象的属性也应该冻结。

1
2
3
4
5
6
7
8
var constantize = obj => {
Object.freeze(obj);
Object.keys(obj).forEach((key, i) => {
if (typeof obj[key] === 'object') {
constantize(obj[key]);
}
});
};

参考文档

阮一峰:ECMAScript 6 入门
MDN:Object.freeze

egg中egg-sequelize和egg-mongoose不能同时使用

今天在项目中加入 egg-mongoose 插件,配置好 config 文件和 plugin.ts 信息后,运行代码,在加载 mongoose 插件的时候报错了,错误信息:

1
nodejs.TypeError: Cannot assign to read only property 'model' of object '#<Application>'

回看下代码发现原有的 egg-sequelize 已经挂载 model 到 app 上面

去掉其中一个插件都可以正常运行,看来 egg-sequelize 和 egg-mongoose 插件冲突了,excuse me?

错误原因

浏览 egg-sequelize 源码发现已经定义好 model 不能被重写

1
2
3
4
5
Object.defineProperty(model, delegate[len - 1], {
value: sequelize,
writable: false,
configurable: true,
});

egg-mongoose 挂载 model 到 app 上时就会报Cannot assign to read only property错误了。

解决方案

  1. 给官方提 issue:把问题和解决方案描述一下;
  2. 降级方案:去掉 MongoDB 的 model 层使用其他插件如 egg-mongo-native 或者自己封装一个;
  3. model 层使用一种数据库,将使用 MongoDB 和 MySQL 的业务拆分为两个服务;

在koa中使用装饰器

在使用 koa 开发的过程中,经常会忘记把 controller 的方法加到 router 中去,期望使用 decorator 实现路由配置及一些参数校验。

阅读更多