论坛首页 Web前端技术论坛

JavaScript内核系列 第5章 数组

浏览 20124 次
该帖已经被评为精华帖
作者 正文
   发表时间:2010-04-26  
abruzzi 写道
另外谢谢weiqingfei这位朋友的支持,希望你再接再厉,这样这个系列的质量才可以更好。


我觉得能静下心来,认认真真的做,并且做完一件事是值得尊敬的。佩服你,希望能坚持到底。
0 请登录后投票
   发表时间:2010-04-26   最后修改:2010-04-26
abruzzi 写道
zhouyrt 写道


呵,数组的数字索引看起来像其它语言的索引,但实际上它会隐式的转换成字符串。亦可参考
http://snandy.iteye.com/blog/284805
http://snandy.iteye.com/blog/407351


嗯,不错,用一个for..in就可以看到了。JavaScript的数组本质上还是键-值这样的形式,而不是一个连续的内存块(C/Java中都是连续的),而是一个散列表。


其实正好相反。在array['1']=''时,javascript会把它当翻译成array[1],所以用array[1],和array['1']都能访问到,而且array.length会变成2。
但是如果用array['1a']='',那么你只能用array['1a']来访问,因为1a被当做数组对象的一个属性而非数组的内容,所以array的length是0,同时在array的tostring和jion等方法时1a是不会参与的。
至于 for in 语句中只是对于array来说会在array中的内容迭代完会把对象中的属性也迭代出来而已,并不是说1a就是数组的内容
0 请登录后投票
   发表时间:2010-04-26  
Lunatica 写道
abruzzi 写道
zhouyrt 写道


呵,数组的数字索引看起来像其它语言的索引,但实际上它会隐式的转换成字符串。亦可参考
http://snandy.iteye.com/blog/284805
http://snandy.iteye.com/blog/407351


嗯,不错,用一个for..in就可以看到了。JavaScript的数组本质上还是键-值这样的形式,而不是一个连续的内存块(C/Java中都是连续的),而是一个散列表。


其实正好相反。在array['1']=''时,javascript会把它当翻译成array[1],所以用array[1],和array['1']都能访问到,而且array.length会变成2。
但是如果用array['1a']='',那么你只能用array['1a']来访问,因为1a被当做数组对象的一个属性而非数组的内容,所以array的length是0,同时在array的tostring和jion等方法时1a是不会参与的。
至于 for in 语句中只是对于array来说会在array中的内容迭代完会把对象中的属性也迭代出来而已,并不是说1a就是数组的内容


嗯,我觉得你的理解更有道理一些,我再做一些测试,谢谢!
0 请登录后投票
   发表时间:2010-04-27  
php的精华在数组,js也有点像
0 请登录后投票
   发表时间:2010-04-27   最后修改:2010-04-27
weiqingfei 写道
abruzzi 写道
weiqingfei 写道
引用
另一个与其他语言的数组不同的是,字符串也可以作为数组的下标(事实上,在JavaScript的数组中,数字下标最终会被解释器转化为字符串,也就是说,所谓的数字下标只不过是看着像数字而实际上是字符的属性名),比如:


这个是Object最基本的特性,Array源于Object,自然有这个特性,这个不能说是javascript数组特殊的地方。

你的意思我理解,我想表达的是,这个特性是JavaScript数组区别于其他语言的数组的,并不是JavaScrpt数组区别于JavaScript对象的。

Array作为Object,接受key-value序列赋值时,只有当key可转为正整数,才有可能对length进行修改。
比如
var a=[];
a[0] = 0;
a["1"] = 1;
a[new Number(2)] = 2;
a[new String(3)] = 3;
a[new Object(4)] = 4;

var MyObj = function(value){
	this.value = value;
	this.toString = function(){
		return this.value;
	}
}
a[new MyObj(5)] = 5;


不知道你用的是什么解释器?我使用的解释器器是Rhino,可以设置的,我试了下你的例子。
不知为什么,javascript的“数组”让我觉得一阵阵恶心。



嗯,例子就是演示使用这样形式的下标,是可以给数组赋值并重新计算length的,
由此也可以大概猜想一下Array对象的内部实现。



大概的看了一下rhino关于Array部分的实现,和我猜想的差不多。
源文件org/mozilla/javascript/ScriptRuntime.java中1517行方法
    public static Object setObjectElem(Scriptable obj, Object elem,
                                       Object value, Context cx)
    {
        if (obj instanceof XMLObject) {
            XMLObject xmlObject = (XMLObject)obj;
            xmlObject.ecmaPut(cx, elem, value);
            return value;
        }

       [b] [color=red]String s = toStringIdOrIndex(cx, elem);[/color][/b]
        if (s == null) {
            int index = lastIndexResult(cx);
            ScriptableObject.putProperty(obj, index, value);                //属性elem可以作为index
        } else {
            ScriptableObject.putProperty(obj, s, value);                      //属性elem为其它
        }

        return value;
    }

再看看toStringIdOrIndex方法
    static String toStringIdOrIndex(Context cx, Object id)
    {
        if (id instanceof Number) {                           //如果属性为数字
            double d = ((Number)id).doubleValue();
            int index = (int)d;
            if (index == d) {
                storeIndexResult(cx, index);
                return null;
            }
            return toString(id);
        } else {
            String s;
            if (id instanceof String) {                          //如果属性为字符串
                s = (String)id;
            } else {
                s = toString(id);                                 //如果属性为其他,用toString方法得到其字符串值
            }
            long indexTest = indexFromString(s);       //这个方法判断这个字符串是否可以做index
            if (indexTest >= 0) {
                storeIndexResult(cx, (int)indexTest);
                return null;
            }
            return s;
        }
    }

再看一下toString方法
    public static String toString(Object val) {
        for (;;) {
            if (val == null) {
                return "null";
            }
            if (val == Undefined.instance) {
                return "undefined";
            }
            if (val instanceof String) {
                return (String)val;
            }
            if (val instanceof Number) {
                // XXX should we just teach NativeNumber.stringValue()
                // about Numbers?
                return numberToString(((Number)val).doubleValue(), 10);
            }
            if (val instanceof Scriptable) {
                val = ((Scriptable) val).getDefaultValue(StringClass);
                if (val instanceof Scriptable) {
                    throw errorWithClassName("msg.primitive.expected", val);
                }
                continue;
            }
            return val.toString();         //如果只是一般的object,就是用其toString方法的返回值,
        }                                          //这个也就是我例子里所举的那个自定义对象的情况
    }


调用ScriptableObject.putProperty(obj, index, value); 实质上调用的是NativeArray.put(int index, Scriptable start, Object value)方法
引用

    public void put(int index, Scriptable start, Object value)
    {
        if (start == this && !isSealed() && dense != null && 0 <= index &&
            (denseOnly || !isGetterOrSetter(null, index, true)))
        {
            if (index < dense.length) {
                dense[index] = value;
                if (this.length <= index)
                    this.length = (long)index + 1;
                return;
            } else if (denseOnly && index < dense.length * GROW_FACTOR &&
                       ensureCapacity(index+1))
            {
                dense[index] = value;
                this.length = (long)index + 1;
                return;
            } else {
                denseOnly = false;
            }
        }
        super.put(index, start, value);             //这儿调用了super的方法,实际上上面的例子是没有走到这儿的。
        if (start == this) {
            // only set the array length if given an array index (ECMA 15.4.0)
            if (this.length <= index) {
                // avoid overflowing index!
                this.length = (long)index + 1;
            }
        }
    }


看来在rhino的实现中,可作为数组属性,和其他属性是分开管理的。
0 请登录后投票
   发表时间:2010-04-27  
weiqingfei 写道
weiqingfei 写道
abruzzi 写道
weiqingfei 写道
引用
另一个与其他语言的数组不同的是,字符串也可以作为数组的下标(事实上,在JavaScript的数组中,数字下标最终会被解释器转化为字符串,也就是说,所谓的数字下标只不过是看着像数字而实际上是字符的属性名),比如:


这个是Object最基本的特性,Array源于Object,自然有这个特性,这个不能说是javascript数组特殊的地方。

你的意思我理解,我想表达的是,这个特性是JavaScript数组区别于其他语言的数组的,并不是JavaScrpt数组区别于JavaScript对象的。

Array作为Object,接受key-value序列赋值时,只有当key可转为正整数,才有可能对length进行修改。
比如
var a=[];
a[0] = 0;
a["1"] = 1;
a[new Number(2)] = 2;
a[new String(3)] = 3;
a[new Object(4)] = 4;

var MyObj = function(value){
	this.value = value;
	this.toString = function(){
		return this.value;
	}
}
a[new MyObj(5)] = 5;


不知道你用的是什么解释器?我使用的解释器器是Rhino,可以设置的,我试了下你的例子。
不知为什么,javascript的“数组”让我觉得一阵阵恶心。



嗯,例子就是演示使用这样形式的下标,是可以给数组赋值并重新计算length的,
由此也可以大概猜想一下Array对象的内部实现。



大概的看了一下rhino关于Array部分的实现,和我猜想的差不多。
源文件org/mozilla/javascript/ScriptRuntime.java中1517行方法
    public static Object setObjectElem(Scriptable obj, Object elem,
                                       Object value, Context cx)
    {
        if (obj instanceof XMLObject) {
            XMLObject xmlObject = (XMLObject)obj;
            xmlObject.ecmaPut(cx, elem, value);
            return value;
        }

       [b] [color=red]String s = toStringIdOrIndex(cx, elem);[/color][/b]
        if (s == null) {
            int index = lastIndexResult(cx);
            ScriptableObject.putProperty(obj, index, value);                //属性elem可以作为index
        } else {
            ScriptableObject.putProperty(obj, s, value);                      //属性elem为其它
        }

        return value;
    }

再看看toStringIdOrIndex方法
    static String toStringIdOrIndex(Context cx, Object id)
    {
        if (id instanceof Number) {                           //如果属性为数字
            double d = ((Number)id).doubleValue();
            int index = (int)d;
            if (index == d) {
                storeIndexResult(cx, index);
                return null;
            }
            return toString(id);
        } else {
            String s;
            if (id instanceof String) {                          //如果属性为字符串
                s = (String)id;
            } else {
                s = toString(id);                                 //如果属性为其他,用toString方法得到其字符串值
            }
            long indexTest = indexFromString(s);       //这个方法判断这个字符串是否可以做index
            if (indexTest >= 0) {
                storeIndexResult(cx, (int)indexTest);
                return null;
            }
            return s;
        }
    }

再看一下toString方法
    public static String toString(Object val) {
        for (;;) {
            if (val == null) {
                return "null";
            }
            if (val == Undefined.instance) {
                return "undefined";
            }
            if (val instanceof String) {
                return (String)val;
            }
            if (val instanceof Number) {
                // XXX should we just teach NativeNumber.stringValue()
                // about Numbers?
                return numberToString(((Number)val).doubleValue(), 10);
            }
            if (val instanceof Scriptable) {
                val = ((Scriptable) val).getDefaultValue(StringClass);
                if (val instanceof Scriptable) {
                    throw errorWithClassName("msg.primitive.expected", val);
                }
                continue;
            }
            return val.toString();         //如果只是一般的object,就是用其toString方法的返回值,
        }                                          //这个也就是我例子里所举的那个自定义对象的情况
    }


调用ScriptableObject.putProperty(obj, index, value); 实质上调用的是NativeArray.put(int index, Scriptable start, Object value)方法
    public void put(int index, Scriptable start, Object value)
    {
        if (start == this && !isSealed() && dense != null && 0 <= index &&
            (denseOnly || !isGetterOrSetter(null, index, true)))
        {
            if (index < dense.length) {
                dense[index] = value;
                if (this.length <= index)
                    this.length = (long)index + 1;
                return;
            } else if (denseOnly && index < dense.length * GROW_FACTOR &&
                       ensureCapacity(index+1))
            {
                dense[index] = value;
                this.length = (long)index + 1;
                return;
            } else {
                denseOnly = false;
            }
        }
        super.put(index, start, value);             //这儿调用了super的方法,实际上上面的例子是没有走到这儿的。
        if (start == this) {
            // only set the array length if given an array index (ECMA 15.4.0)
            if (this.length <= index) {
                // avoid overflowing index!
                this.length = (long)index + 1;
            }
        }
    }


看来在rhino的实现中,可作为数组属性,和其他属性是分开管理的。


嗯,分析的很好,很认真,呵呵。
String s = toStringIdOrIndex(cx, elem);
        if (s == null) {
            int index = lastIndexResult(cx);
            ScriptableObject.putProperty(obj, index, value);                //属性elem可以作为index
        } else {
            ScriptableObject.putProperty(obj, s, value);                      //属性elem为其它
        }


以及这块:
        if (start == this) {
            // only set the array length if given an array index (ECMA 15.4.0)
            if (this.length <= index) {
                // avoid overflowing index!
                this.length = (long)index + 1;
            }
        }

就可以说明大部分问题了,
0 请登录后投票
   发表时间:2010-04-27  
第六章:正则表达式已经贴出来,地址为:
http://www.iteye.com/topic/654429

希望有更多的像weiqingfei这样热心的朋友来支持,使得这个系列文章的质量更好。
0 请登录后投票
   发表时间:2010-04-28  
好几天没关注楼主的文章了,今天跟上。
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics