从有类型声明的语言,比如C++/Java转到无类型声明语言时,最难理解的地方是参数类型。
比如javascript中,isNaN
这个函数,有以下其怪的行为:
isNaN("abc") // true
按类型语言的思维,这里应该先判断参数是否number
类型,再做判断是否NaN
的值。
事实上,isNaN
先对参数做了包装,调用Number
把参数包装成Number
类型,再进行判断。
类型声明表达了一个强约定,参数必须满足约定才能完成功能,大多数强类型语言中需要通过多态达成通用型扩展。无类型声明语言这种对参数包装成期望类型的操作方式,以另一种方式达成了通用扩展。
isNaN({ valueOf: function() { return 0 }}) //false
isNaN({ valueOf: function() { return Number.NaN }}) //true
Javascript的通用性通过抽象操作来实现,所有对象通过调用通过valueOf()
方法转型为基本对象,而Number
和子类型对象返回自己的引用,实现操作一致性,且效率无损。
这就是在接口,鸭子类型之外的另一个参数类型约定。它可以简化为,实参只要可转化成形参类型。
在C++中可以通过实参定义一个类型转换函数,例如上面在C++中可能表达为:
class Number {
public:
Number(int value);
static const Number NaN;
bool operator == (Number const& other);
};
class SomeObject {
operator Number() const {
return Number(0); //或者Number::NaN;
}
};
bool isNaN(Number value) {
return value == Number::NaN;
}