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 的业务拆分为两个服务;

ES6中Map和Object的区别

JavaScript 的对象(Object)和 Map,本质上是键值对的集合(Hash 结构)

ES6 中 Map 相对于 Object 对象有几个区别:

  • 对象(Object)和只能用字符串和 Symbol 当作键,Map 的 key 可以是任何基本类型或者对象
  • Map 可以通过 size 获取到长度,Object 获取长度的比较复杂
  • Map 具有 Symbol.iterator 属性,可通过 for…of 循环遍历

Map 转为对象:

  • 如果所有 Map 的键都是字符串或者 Symbol,它可以无损地转为对象。
  • 如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。
阅读更多

在koa中使用装饰器

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

阅读更多

JavaScript装饰器

随着 ES6 里引入了类,目前 ECMAScript 有一个提案,引入了装饰器(Decorator)函数来标注极或修改类及其成员。
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。 装饰器使用 @expression 这种形式,expression 求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。

关于装饰器的详细介绍请参阮一峰的《ECMAScript 6 入门》一书

阅读更多

JavaScript面向对象有感

面向对象程序设计(简称 OOP)是现在最流行的程序设计方法,JavaScript 的核心是支持面向对象的,Prototype 是 JavaScript 实现与管理继承的一种机制。最近在读《你不知道的 JavaScript》一书中了解到除了使用类实现外,还可以用一种“对象关联”(OLOO,objects linked to other objects)的编程风格

阅读更多

《你不知道的JavaScript》笔记-闭包

闭包就好像从 JavaScript 中分离出来的一个充满神秘色彩的未开化世界,只有最勇敢的人才能够到达那里。但实际上它只是一个普通且明显的事实,那就是我们在词法作用域的环境下写代码,而其中的函数也是值,可以随意传来传去。

当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。

阅读更多

《你不知道的JavaScript》笔记-this用法

this 绑定规则

this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。

当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息。this 就是这个记录的一个属性,会在函数执行的过程中用到。

阅读更多