Javascript中值的分类
本文为自己读http://www.adobe.com/devnet/html5/articles/categorizing-values-in-javascript.html?utm_source=javascriptweekly&utm_medium=email 的笔记和部分翻译,不对之处,敬请指正!
在 Javascript 中值 (value) 可以通过以下几种方式进行分类:
(1)通过隐藏的属性[[Class]]。
(2)通过 typeof 操作符。
(3)通过 instanceof 操作符。
(4)通过 Array.isArray 函数。
基础知识复习:
基础类型(primitives) VS 对象(objects)
在 Javascript 中,一个值要么是基础类型要么就是对象。
基础类型,下面的值就是属于基层类型的:
- undefined
- null
- Booleans
- Numbers
- Strings
基础类型是不可改变的;你不能给它们添加属性:
var str = "abc"; str.foo = 123; //给其添加属性“foo” console.log(str.foo); //undefined
并且,基础类型是通过值来进行比较的,意思是说如果它们拥有相同的内容,那么它们就认为是相等的:
“abc” === “abc” //true
对象,所有不是基础类型的值都是对象。对象是可以改变的:
var obj = {};
obj.foo = 123;
console.log(obj.foo);//"foo"属性被添加上了 123
对象是通过引用来进行比较的。每一个对象都有自己独有的标识。因此要两个对象相等,只能是它们是同一个对象。
console.log({} === {}) //false
var obj = {};
console.log(obj === obj); //true
包装对象类型 (Wrapper object types)
基础类型的 boolean, number 和 string 都有相对应的包装对象类型Boolean, Number 和 String.包装对象类型的实例是对象,它们和基础类型值是不相同的:
console.log(typeof new String("abc")) //object
console.log(typeof "abc") //string
new String("abc") === "abc" //false
包装对象类型很少被直接运用,但是他们的原型对象(prototype objects)定义了基础类型的方法。例如,String.prototype 是包装对象类型 String 的原型对象,它所有的方法对于 strings 都是可以使用的。如包装的方法 String.prototype.indexOf. 基础类型 strings 也有相同的方法,两个方法并不是不同的方法拥有相同的名称,而就是相同的方法:
String.prototype.indexOf === "".indexOf //true
内部属性 (internal properties)
内部属性是影响着 javascript 怎样工作但是又不能被直接访问的属性。内部属性的名称是首字母大小并被两个双方括号所包裹着的。例如[[Extensible]]就是一个内部属性,它拥有一个boolean标志,这个标志决定了属性是否可以被添加到一个对象上。它的值只能通过 Object.isExtensible() 进行读取,Object.preventExtensions()将其设置为false。一旦将其值设置成false,将无法通过其他方法改为true。
术语:原型 (prototypes)VS 原型对象 (prototype objects)
在 Javascript 中,原型术语被不幸地进行了重载。
- 一方面,在对象之间存在着 prototype-of 关系。每一个对象都有一个隐藏的属性 (hidden properties) [[Prototype]],它要么指向对象的原型 (prototype),要么指向null。原型是一个延续的对象。如果一个对象的原型是一个可以被访问的对象,并且它不能被继续下去(它的对象的原型指向的是 null),那么就只会延续的上一个对象。多个对象可以拥有相同的原型。
- 另一方面,如果一个类型被一个构造函数Foo实现,那么那个构造函数拥有一个Foo.prototype属性作为类型的原型对象。
为了使两种得到明确的区分,开发者称第一种为“原型 (prototypes)”,第二种为“原型对象(prototype objects)”.下面的方法可以帮助我们处理原型:
- Object.getPrototypeOf(obj)返回的是 obj的原型:
Object.getPrototypeof({}) === Object.prototype //true
- Object.create(proto)是创造了一个空的对象,它的原型是 proto:
Object.create(Object.prototype) //{}
- proto.isPrototypeOf(obj)返回的是 true,如果 proto 是 Obj 的对象(或者一个原型的原型,等等)。
Object.prototype.isPrototypeOf({})
"constructor" 属性
对于一个给定的构造函数 Foo, 它的原型对象Foo.prototype又有一个Foo.prototype.constructor属性,这个属性将会指回Foo。这个属性将会被每个函数自动的实现:
function Foo() {}
Foo.prototype.constructor === Foo //true
RegExp.prototype.constructor === RegExp //true
构造函数的实例从原型对象继承了这个属性。这样你就可以通过它来判断这个实例是通过哪一个构造函数创建的:
new Foo().constructor //[Function: Foo] /abc/.constructor //[Fucntion: RegExp]
值的分类
可以通过四种方面对值进行分类:
- [[Class]] 是一个内部属性,通过一个字符串来描述一个对象的分类。
- typeof 是一个操作符,它可以用来分类基础类型,从而帮助将基础类型和对象进行区分。
- instanceof 是一个操作符,用作分类对象。
- Array.isArray() 是一个函数,用来区分一个值是否是函数。
[[Class]]
[[Class]] 是一个内部属性,它的值是下面中的一个字符串:
"Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", "String"
从 Javascript 访问它的唯一方法就是通过默认的 toString() 方法, 就像下面一样进行调用:
Object.prototype.toString.call(value)
这样的调用将会返回:
- “[object Undefined]”, 如果值是undefined,
- "[object Null]", 如果值是null,
- "[object " + value.[[Class]] +"]" ,如果值是一个对象,
- "[object " + value.[[Class]] +"]" ,如果值是一个基础类型(会将其转换成一个对象)。
例如:
Object.prototype.toString.call(undefined);
// [object Undefined]
Object.prototype.toString.call(Math);
// [object Mach]
Object.prototype.toString.call({});
// [object Object]
因此,下面的函数就可以检测一个值的[[Class]]了:
function getClass(x) {
var str = Object.prototype.toString.call(x);
return /^\[object (.*)\]$/.exec(str)[1];
}
下面是执行上面的函数的结果:
getClass(null)
//Null
getClass({})
//Object
getClass([])
//Array
getClass(JSON)
//JSON
function Foo(){}
getClass(new Foo())
//Object
(function (){ return getClass(arguments)}());
//Arguments
typeof
对于操作的值,会根据下表进行返回:
| Operand | Resulet |
| undefined | "undefined" |
| null | "object" |
| Boolean value | "boolean" |
| Number value | "value" |
| String value | "string" |
| Function | "function" |
| 其他值 | "object" |
typeof操作符对 null 的返回值是 object,是一个无法被修复的 bug, 因为要兼容已有的代码。 需要注意的是尽管函数(function)是一个object,但是 typeof 进行了区分。另一方面,Array也被认为是objects.
这样就使得对对象的判断变得复杂了一些:
function isObject(x) {
return x !== null &&
(typeof x === 'object' || typeof x === 'function');
}
instanceof
instanceof 是一个检查一个值是否是一个类型的实例:
value instanceof Type
这个操作符通过寻找 Type.prototype 来检验它是否在值得原型链中。这样来说,如果你要自己实现 instanceof,那么大体的代码会像下面一样:
function myInstanceof(value, Type) {
return Type.prototype.isPrototypeOf(value);
}
对于基础类型, instanceof 常常会返回false
"" instanceof String //false "" instanceof Object //false
Array.isArray()
Array.isArray()的存在是因为浏览器中的一个特有的问题:每一个 frame 都拥有它自己的全局环境。 比如, 给定一个 frame A 和一个 frame B(任何一个都可以是 document),在 frame A 中向 frame B中传递一个值。 在 frame B中的代码不能通过使用 instanceof Array 来判断一个值是否是 array,因为它的 B Array 和 Array 是不同的 ,例如:
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript" charset="utf-8">
//在 iframe 中调用 test()
function test (arr) {
var iframeWin = frames[0];
console.log(arr instanceof Array);
//false
console.log(arr instanceof iframeWin.Array);
//true
console.log(Array.isArray(arr));
//true
}
</script>
</head>
<body>
<iframe></iframe>
<script type="text/javascript" charset="utf-8">
//填充iframe
var iframeWin = frames[0];
iframeWin.document.write(
'<script>window.parent.test([])</'+'script>');
</script>
</body>
<
因此,ES5引进了Array.isArray(),通过使用 [[Class]]来判断一个值是否是数组。尽管如此,上面描述的关于 frames 的问题再其他的类型,当使用 instanceof 时依然存在。
内置原型对象(Built-in prototype objects)
内置类型的原型对象非常的奇怪:它们的表现就像是这个类型的实例,但是当通过 instanceof 来检验的时候,会发现它们并不是实例。一些对原型对象的其他分类结果页并不如预期。
Object.prototype
Object.prototype 是一个空的对象:它打印出来就只是一个,并没有其他可例举的属性:
Object.prototype //{}
Object.keys(Object.prototype) //[]
没有想到的是, Object.prototype是一个对象,但是它不是 Object 的一个实例。一方面, typeof 和 [[Class]]都将其认为是 object:
getClass(Object.prototype) //object typeof object.prototype //object
另一方面, instanceof并不认为它是 Objec t的实例:
Object.prototype instanceof Object //flase
为了让上面的结果正确,那么 Object.prototype 必须拥有它自己的原型链,这样就会造成从 Object.prototype 开始到 Object.prototype 结束的回环。原型链不再是线性的,这就不是你所想要的数据结构并且不能被遍历。因此,Object.prototype 并没有prototype。 它是为一个没有 prototype 的对象。
Object.getPrototypeOf(Object.prototype) //null
这种悖论对所有的内置原型对象都是正确的:它们被所有的机制认为是所对应类型的实例,除了 instanceof.
对于其他的对象,[[Class]], typeof 和 instanceof 都保持一致:
getClass({})
//Object
typeof {}
//object
{} instanceof Object
//true
Function.prptotype
Function.prototype 自身本来是一个函数,它接受任何函数但是都返回 undefined.
Function.prototype("a", "b", 1, 2)
//undefined
出乎意料的是 Function.prototype 是一个 function,但是它并不是 Function 的实例:
typeof Function.protype //function getClass(Function.prototype) //Function Function.prototype instanceof Function //false
这是因为 Function.prototype 它并没有原型链。相反,它的原型就是 Object.prototype:
Object.getPrototypeOf(Function.prototype) === Object.prototype //true
对于其他的函数,将毫无悬念:
typeof function () {}
//function
getClass(function () {})
//Function
function () {} instanceof Function
//true
typeof Function
//function
getClass(Function)
//function
Function instanceof Function
//true

浙公网安备 33010602011771号