Javascript Native Object(ECMA定义的类)

ECMA-262 本身定义有很多 native object(类),本文将根据原型链的原理分析类与类之间的关系,欢迎拍砖!文章内容蛮长的做好准备哦!:)

先看一个例子,请务必在 Firefox 下测试:

function example() {}

console.log('example.constructor === Function'); 
console.info(example.constructor === Function) //output 'true'

console.log('example instanceof Function'); 
console.info(example instanceof Function) //output 'true'

以上输出结果足以说明函数 example 是 Function 的实例,其表现形式近似的为:

var example = new Function();

内部实现为:

example.__proto__ = Function.prototype;
/* 此处之所以没有写 Function.call(example) 是由于 Function 内部
没有指定 this 指针 */

先来分析属性 constructor

根据Javascript原型链机制,由于默认 Function.prototype.constructor = Function ,所以example.constructor 也指向 Function 。

再接着分析运算符 instanceof

Function 实例化后的函数 example 的隐式指针 __proto__ 指向 Function.prototype ,由于 instanceof 是根据 __proto__ 判断的,所以 example instanceof Function 为真。

想清楚了 function example 的 instanceof 与 constructor ,我们再进一步思考 Function 与 Object ,下面内容主要分为两大部分:

第一部分 类 Function 与其它内置类之间的关系

console.log('Object.constructor == Function'); 
console.info(Object.constructor == Function) //output 'true'

console.log('Object instanceof Function'); 
console.info(Object instanceof Function) //output 'true'

我们很容易的看出原来 Object 也是 Function 的实例啊!同时也说明:

Object.__proto__ === Function.prototype;  //output 'true'

但为什么不是下面的这种方式呢?

Object = {__proto__:Function.prototype}; 

是因为 Object 是类,也就是

console.log('Object的类型:');
console.info(typeof Object);  //output 'function'

所以只可能是 Object.__proto__ === Function.prototype; ,下面再来验证下别的类:

console.log('验证 Array String RangeError 等类是否为 Function 的实例'); 

console.info(Array.constructor == Function) //output 'true'
console.info(Array instanceof Function) //output 'true'
console.info(String.constructor == Function) //output 'true'
console.info(String instanceof Function) //output 'true'
console.info(RangeError.constructor == Function) //output 'true'
console.info(RangeError instanceof Function) //output 'true'
console.info(RegExp.constructor == Function) //output 'true'
console.info(RegExp instanceof Function) //output 'true'

由于 native object(类)太多了,此处只引用了四个,其它的也都成立的,可以自行测试,得出结论 Javascript 所有的 native object(类)均是 Function 构造的。在此特别强调一下 Math ,它是 Objcet 的实例,不属于 native object(类) 。

既然这么多的类都是 Function 的实例,那么假如添加或更改 Function.prototype 的属性,会影响到这些类么?请看例子:

Function.prototype.FunAttr = '我是 Function.prototype 新增加的属性
FunAttr 的值';

console.log('Function.prototype 上增加属性 FunAttr 后'); 

console.info('example.FunAttr = "' + example.FunAttr + '"') 
//output '我是Function.prototype新增加的属性 FunAttr 的值'
console.info('Object.FunAttr = "' + Object.FunAttr + '"') 
//output '我是Function.prototype新增加的属性 FunAttr 的值'
console.info('Array.FunAttr = "' + Array.FunAttr + '"') 
//output '我是Function.prototype新增加的属性 FunAttr 的值'
console.info('String.FunAttr = "' + String.FunAttr + '"') 
//output '我是Function.prototype新增加的属性 FunAttr 的值'

结果证明影响力挺大的,包括文章开头的自定义的函数 example 在内的所有 Function 的实例都拥有了新的属性 FunAttr 。

测试中还发现一个有意思的现象 – 明明是在 Function.prototype 上添加了新的属性,Function 本身却也拥有了此新增属性。

console.info(Function.FunAttr);
//output '我是Function.prototype新增加的属性 FunAttr 的值'

这证明了:

console.info(Function.__proto__ === Function.prototype);
//output 'true'

或许没什么道理可言,只是ECMA262规定了一些既定的规则!

一般给 Array String … 类添加属性似乎没有什么用处,因为真正在编码的时候,并非在一次次的引用这些类,而是用到这些类的不同的实例,比如 var arr = [] , var str = new String() ,再次想想原型链原理,只有向 Array.prototype String.prototype … 类添加属性(方法)的时候才会影响到其对应的实例。添加属性的时候要注意在类的原型上直接添加,而不要用对象字面量的形式添加。选取 Array 类作为例子:

Array.prototype.ArrAttr = '我是 Array.prototype 新增加的属性 
ArrAttr 的值';
var arr = [];

console.log('Array.prototype 上增加属性 ArrAttr 后'); 
console.info('arr.ArrAttr = "' + arr.ArrAttr + '"');
//output '我是 Array.prototype 新增加的属性 ArrAttr 的值'

可以看出,Array 类的实例取到了新增加在 Array.prototype 上的属性。

如果新增属性名和原 Array.prototype 上的属性同名,则会暂时隐藏或覆盖原 Array.prototype 上的属性,这一点各个浏览器实现机制均不同,如果是暂时隐藏,那么删除新增的属性后,原 Array.prototype 上的属性又会再次可用,如果是覆盖,删除后,再次调用此属性会输出 undefined ,个人认为暂时隐藏会更为合理一些,这让我想起了 linux 中 mount 。

Array.prototype.slice = '我是 Array.prototype.slice 被更改后的值';
var arr = [1,2,3];

console.log('Array.prototype.slice 被更改后的值'); 
console.info('arr.slice = "' + arr.slice + '"');
//output '我是 Array.prototype.slice 被更改后的值'

delete Array.prototype.slice  //将其删除

console.log('还原 Array.prototype.slice 初始值'); 
alert( arr.slice(1) );  //output [2,3] 只是部分浏览器,不包括 Firefox 

切勿 Array.prototype = {ArrAttr:’我是 Array.prototype 新增加的属性 ArrAttr 的值’} ,因为这样会输出 undefined ,试着推测下内部实现:

//在 Javascript 引擎在读入页面代码之前已经完成下面三段代码

Array = {__proto__:Function.prototype}

Array.prototype = {
  slice : function(){ [native code] },
  split : function(){ [native code] }
  ...
}

Array.prototype.__proto__ = Object.prototype;
//这一行在本文第二部分 类Object与其它内置类之间的关系 中会有说明

当代码加载并执行到 Array.prototype = {ArrAttr:’我是 Array.prototype 新增加的属性 ArrAttr 的值’}时 ,预将 Array.prototype 的指针指向一个新的对象字面量,由于结果会造成 Array.prototype 预置的属性(方法)将丢失,所有数组实例的属性(方法)也会丢失,显然这是不会被允许的,所以将含有 ArrAttr 的对象字面量舍去,当输出一个对象不存在的属性时,就会被定义为 undefined 。

在添加 Array.prototype 属性的时候,还要尽可能的避免和 Array 类的属性名重名,比如:

Array.prototype.slice = '我是新的 slice 属性';

这时 Javascript 内置的 slice 属性将被覆盖,

new Array().slice == '我是新的 slice 属性';  //output 'true'

但我们若是

Array.prototype.__proto__ = {slice:'我是新的 slice 属性'};

这样指定同名属性,实际并不能改变属性的值,Array.prototype.slice 还是等于 function(){ [native code] },即使现在

Array.prototype.__proto__ === Object.prototype //output 'false'

javascript语言虽然相对别的语言灵活,但我们也不能更改js引擎预先定义好的原型链,否则,将就天下大乱了!很自然:

new Array().slice == '我是新的 slice 属性';  //output 'false'

这和平时工作中用 function 做继承时的原理不同,需要注意!

还有一种方式:

example.prototype.slice = '我是新的 slice 属性';
Array.prototype = {__proto__:example.prototype};

这次虽然 Array.prototype.__proto__ = Object.prototype 没有变,但这种改变是js引擎阻止的!

其实,用 __proto__ 为内置类添加新属性,可以说是仅仅是一个假设,因为几乎没有人会像下面这么写,同时这也会使浏览器报错:

Array.prototype = new example()  //example 可以为任何自定义函数

知道了 Array.prototype 包含的属性,那么我们也可以想到 String.prototype Number.prototype … 类会包含哪些属性名,虽然我们不知道具体实现细节。

第二部分 类 Object 与其它内置类之间的关系

Object 和 包括 Function 在内的其它类之间还有什么别的关系么?先看下面的例子:

console.log('分别测试,Function Array 等类 .prototype.__proto__ 的
指针是否为 Object.prototype'); 

console.info(Function.prototype.__proto__ === Object.prototype);
//output 'true'
console.info(Function.prototype instanceof Object) //output 'true'
console.info(Array.prototype.__proto__ === Object.prototype);
//output 'true'
console.info(Array.prototype instanceof Object) //output 'true'
console.info(Boolean.prototype.__proto__ === Object.prototype);
//output 'true'
console.info(Boolean.prototype instanceof Object) //output 'true'
console.info(RegExp.prototype.__proto__ === Object.prototype);
//output 'true'
console.info(RegExp.prototype instanceof Object) //output 'true'

注:上述实例没有检测 Math.prototype ,是因为 Math 不同于 Array String 这样的类,自身属于对象实例,所以不存在 prototype 。

又由于

console.info(Object.prototype.__proto__ === null);  //output 'true'

可以看出,Object.prototype 是原型链的终点。

既然 function example 是 Function 的实例, Object 也是 Function 的实例,那 function example 又和 Object 有什么关系呢?请看下面的例子:

console.info(example.prototype.__proto__ === Object.prototype);
//output 'true'

原来自定义的函数和除 Function 自身之外的所有内置类,都是类 Function 的实例,类型均为 function ,每个除 Object 自身之外的内置类原型链的终点又都是 Object.prototype 。

console.info(Object.__proto__ === Function.prototype);  //output 'true'
console.info(Function.prototype.__proto__ === Object.prototype);
//output 'true'

这一切是不是很奇妙呢?

需要注意的是,由于

console.info(typeof Function.prototype)  //output 'function'

所以一定是

Function.prototype.__proto__  = Object.prototype

的形式,而自定义函数

console.info(typeof example.prototype)  //output 'object'

所以一定是

example.prototype = {__proto__ : Object.prototype}

的形式。

如果上面所写的东东已经想明白了,那么请问为什么说 Javascript 中的函数是函数也是一个对象呢?

给我自己和看这篇文章的人考虑5分钟 …

其实,也不难理解,先做一些验证:

console.log('验证各个类以及自定义函数隐式 __proto__ 指针是否为
Object.prototype ');

console.info(example.__proto__ === Function.prototype);//output 'true'
console.info(example.prototype.__proto__ === Object.prototype);
//output 'true'
console.info(Function.__proto__ === Function.prototype);//output 'true'
console.info(Function.prototype.__proto__ === Object.prototype);
//output 'true'
console.info(Array.__proto__ === Function.prototype);//output 'true'
console.info(Array.prototype.__proto__ === Object.prototype);
//output 'true'
console.info(String.__proto__ === Function.prototype);//output 'true'
console.info(String.prototype.__proto__ === Object.prototype);
//output 'true'

这说明了,引擎内置类或是自定义函数都是 Function 类构造的,同时又都是 Object 的实例,也就是:

console.log('验证各个类以及自定义函数是否是 Function 类构造的,同
时又都是 Object 的实例');

console.info(example instanceof Function); //output 'true'
console.info(Function instanceof Function); //output 'true'
console.info(Array instanceof Function); //output 'true'
console.info(String instanceof Function); //output 'true'
console.info(example instanceof Object); //output 'true'
console.info(Function instanceof Object); //output 'true'
console.info(Array instanceof Object); //output 'true'
console.info(String instanceof Object); //output 'true'

通过以上一系列的验证,我们的问题也就见分晓了。

在我的知识范围之内,类与类之间的关系就这么多。

今天感冒,头晕乎乎的,写了一堆的疯话,有待清醒的人验证!

续:

在网上找到一张JavaScript对象模型图,才发现自己辛辛苦苦的写了一大堆的文字,还不如这张图明白了,哎~~~~,下面贴图,点击后放大

Javascript对象模型图

This entry was posted in Javascript/Ajax and tagged , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>