这篇是 上篇 固有对象和非固有对象的成员,值类型赋值,非值型引用的差异
的继续.
首先我们这里说固有对象就是 布尔值 字符串 数值 通用对象 数组 函数 (未定义 空值 太特殊,就不提了 )
对应的对象就是 Boolean,String,Number,Object,Array,Function(这几个字母在这里指的是对象,不是指类型表示,这几个对象的类型都是 function/函数 )
还记得上篇中说的
无法对 值类型赋予新的可变成员
布尔值 字符串 数值 就是值类型, Boolean,String,Number 不是值类型,是function
这个绕嘴的说法都是因为文字上的写法造成的,我们用 小写 boolean,string,number (其实javascript也就是这么表示的)或中文的布尔值,字符串,数值表示类型,Boolean,String,Number表示JavaScript 的3个对象.
所以:
/*以下有效*/ Boolean.foo='foo'; String.foo='foo'; Number.foo='foo'; Object.foo='foo'; Array.foo='foo'; Function.foo='foo'; /*以下无效*/ 'string'.foo='foo'; (1).foo='foo'; true.foo='foo';
下面看看用prototype(原型定义)方法扩展对象在使用上的差别:
String.prototype.foo='foo'; var v=new String('string'); v.foo=3; alert(v.foo);//3 var v='string'; v.foo=3; alert(v.foo);//foo
噢,原来new这个关键字用和不用还是有差别的,同样的道理也适用于Boolean,Number,但是:
Object.prototype.foo='foo'; var v=new Object(); v.foo=3; alert(v.foo);//3 var v={}; v.foo=3; alert(v.foo);//3,原型都被改了 Object.prototype.foo={v:'foo'};//非值对象也同样 var v={}; v.foo.v=3; alert(v.foo.v);//3 var v={}; alert(v.foo.v);//3,原型都被改了
进一步:
String.prototype.foo={v:'foo'}; var v=new String('string'); v.foo.v=3; alert(v.foo.v);//3 var v='string'; alert(v.foo.v);//3,原型都被改了
值对象和非值对象就是不一样.
好像有些乱,我也没有找到合适的语言来组织这个
var v=new String('string');//和 var v='string';//以及 String.prototype.foo={foo:'foo'};//对JavaScript内建 string的影响到底如何描述
不过对于非固有对象 也就是对象 就没有那么复杂了,
var foo=function(){}; foo.prototype={v:'foo',foo:{v:'foo'}}; var o=new foo; o.v=3; o.foo.v=4; var o =new foo; alert(o.v);//foo alert(o.foo.v);//,原型都被改了
原因很简单,因为foo.prototype只不过由于名称的特殊,会让JavaScript对new 操作进行特殊的继承处理,除此之外,
foo.prototype也具有普通对象的特性,符合值类型拷贝副本 ,非值型引用 的规律
那容易让人迷惑的o.foo.v的v不也是值类型 么?是可是别忘了前面的foo是非值型 ,
o.foo.v 中的foo已经指向 了 foo.prototype.foo
当然o.foo.v 和foo.prototype.foo.v 是同一个 对象了
同样道理:由于
var o=new foo;
o是foo的一个实例,是两个对象,那
o!=foo; o!=foo.prototype; o.v!=foo.prototype.v;//因为是值类型 o.foo===foo.prototype.foo;//非值型
非值类型就引用到同一内存对象上了.
不过非值型的赋值也有意思,就是改变的是引用,而不是单纯的改变值.
var foo=function(){}; foo.prototype={v:'foo',foo:{v:{v:'foo'}}}; var o=new foo; o.v={}; o.foo.v={v:'new'}; var o =new foo; alert(o.foo.v.v);//原型都被改了
这回原型又被改了.这样
var foo=function(){}; foo.prototype={v:'foo',foo:{v:{v:'foo'}}}; var o=new foo; o.v={}; o.foo={v:'new'}; var o =new foo; alert(o.foo.v.v);//没有被改
一会儿改一会儿不改,到底问题在哪里,差别在哪里问题就在那里:
差别在:
o.foo={v:'new'}; o.foo.v={v:'new'};
注意o是独立的实例对象,o.foo是引用,和 foo.prototype.foo是同一个对象.直接对o.foo赋值当然改变的是引用,掐断了引用,引用到一个新的对象了,和原来的foo.prototype.foo不一样了,虽然
{v:'foo'}
也是一个对象,不过要注意o.foo.v中的foo已经明确范围 ,这个范围就是同一个对象,当然原型也被改了.回头再看看前面的o,
哈哈,原来o是个新的实例,范围不同 呀!同样适用这个确定范围的说法
说了这么多,其实就一句话,引用的问题,先 确定范围 吧,是不同的,还是相同的.
如果用标准的说法这是个Scope Chain的问题.抄一段规范的翻译文章:
2. 调用结果(1)的[[HasProperty]]方法, 传递Identifier作为参数
3. 如果结果(2)是true, Reference(引用)类型的值,它的base object是结果(1)而它的
property name是Identifier
4. 跳到第1步
5. 返回一个Reference类型,它的base object是null它的property name 是Identifier.
注:Reference(引用)类型的值是JS引擎使用的一种数据类型,它分为base object和property name两个部分。假设在JS代码中有obj.prop这样的表达式,那么解释成Reference类型,base object是对象obj,而property name是字符串”prop”
Scope Chain开始时被设为宿主对象,所以在全局代码中的变量就是宿主对象的属性。Scope Chain在执行时由JS引擎自动维护,编译型的引擎也会创建相应的运行时环境来做此事。Scope Chain一般在函数调用或者执行进入with块的时候改变。
说起来麻烦,用起来其实很简单.
还有一点是在JavaScript里继承这个词不是很准确,连祖先都随时可以被修改,这那是继承,明显是颠覆嘛!所以我觉得用Mixin
混入来说更合适,也无所谓,反正大家都知道在JavaScript里说的都是一回事.