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 中,原型术语被不幸地进行了重载。

  1. 一方面,在对象之间存在着 prototype-of 关系。每一个对象都有一个隐藏的属性 (hidden properties) [[Prototype]],它要么指向对象的原型 (prototype),要么指向null。原型是一个延续的对象。如果一个对象的原型是一个可以被访问的对象,并且它不能被继续下去(它的对象的原型指向的是 null),那么就只会延续的上一个对象。多个对象可以拥有相同的原型。
  2. 另一方面,如果一个类型被一个构造函数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
posted @ 2012-10-13 14:56  shawnXiao  Views(1377)  Comments(0Edit  收藏  举报