Front-end web developer——[to be a better man]
[Mobile Version] | 文章字体大小Font Size文章字体大小:12px, 14px

javascript

十二 21

上次写了一篇《Array.prototype.slice.call自解》,主要是讲我在学习Currying in Javascript时所遇到的一个小问题,即为什么很多人把[].slice()写成了Array.prototype.slice.call(ArrObj),有朋友说此文质量太差,所以偶重写一次,看看能不能说清楚问题。

Array.prototype.slice.call 其实是对slice方法进行的静态调用。这里说的静态调用,主要区别于OOP方式。OOP方式需要先建立对象,如下:

[].slice(begin,end);

在JS中,[]就是一个数组对象,在执行时,先会建立对象,再执行自身对象下的slice方法,对这个数组进行切割

而静态调用与OOP方式调用主要目的是静态方式的机器执行效率较高,因为不需要先经过建立对象,而不需要为对象增加内在开销,所以这种写法在较底层的JS程序(如JS框架,兼容性基础库)里面使用得比较多。

Array.prototype.slice就是这样的一个函数,如果要知道为什么不直接写slice而写那么长的三个单词,这不是三言两语说和清楚的,请看JavaScript中的prototype(原型)属性研究 ,如果简单点说,可以用伪代码表示:

Array.prototype.slice=function(begin,end){
  var arr=this;//this就是被切割的函数
  var newArr=我只要arr数组的begin到end中间的部分;//这才是真正的中文编程
  return newArr;//返回被切割后的新数组,强大吧!
}

然后,通过调用函数的call方法:call(thisObj,begin,end),把里面的this强制转换成将要被切割的数组,即术语中的改变函数的scope,从而完成切割。

使用情况:
这个语句通常用于把arguments转换为数组。

function fn() {
  a = Array.prototype.slice.call(arguments,0);
  alert(a);
}(1,2,3,4,5,6,7,'never-online.net/blog')

该代码片段摘自CSDN WEB开发社区的某帖的2楼。

函数的argements在JS1.5中只是一个类似数组的对象,有length属性,但并不是真正的数组,具体请看:Array-like Objects in JavaScript。(据说在ECMA script2中将会是个真正的数组了)

注意:这个语句可不能随便用哦,有的同学把String作为对象转进去,把abcde变为[a,b,c,d,e],这可不是万试万灵的哦,不同的浏览器对slice的解释是有所区别的哦。例如Prototype.js中的$A函数也可以把arguments转为单纯数组,但为了适应更多的情况,它直接使用循环来实现了。

相关文章:

  1. Currying JavaScript Functions
  2. JavaScript Currying Redux
  3. JavaScript currying
  4. functional.js 介绍及源码分析
  5. Array.prototype.slice.apply 使用技巧
  6. Array-like Objects in JavaScript
  7. JavaScript中的prototype(原型)属性研究
  8. http://topic.csdn.net/u/20080725/09/162cb47f-be44-44bf-adcf-2a0ea7538454.html

UPDATE:我写的研究和学习总结性的技术文章,除非是比较重要和大型的项目,否则不会给出直接的代码和可下载的例子,更加不会像幼儿园教小朋友那样手把手的第一步第二步,也不想那样做,以后也不打算那样做,因为我发现越来越多人根本就不想自己动手,习惯性的拿来主义不利于业界的长久发展。

11

我找到一个非常不错的水平滑动菜单的javascript特效:Horizontal JavaScript Accordion 1kb。我曾经把它应用到好些制作的页面上,但我的需求越来越多,所以情况也比Michael的样例复杂多了,而且,我需要更多的可选选项,就像这样的:

l found Horizontal JavaScript Accordion 1kb and it is very great! I have ever planted it in some pages. But my App is more complicated then the Michael‘s demo. And it need to be more optional.
link this:

通过改变物件的宽度去实现像风琴一样的水平滑动是最常用的方法。不过我的是通过改变物件的left位置,并且提供了更多的可选选项,但脚本文件会大一点,达到了8.46kb。还有,我用面向对象的方式编写的,所以可以不用iframe,就可以实现一个页面中有多个风琴滑动效果菜单了。这就有一个简单的样例:

Making horizontal sliding with changing the width of each panel is the most normal way. But my one is by changing the left style of each panel, and it is more optional, but the file is a bit larger: 8.46kb. And it is programmed in OO way, we can have more instances in a page without iframe.Here is a simple demo:



The codes are:

<ul id="hslidemenu" >
<li><img src="images/1.gif" alt="" /></li>
<li><img src="images/2.gif" alt="" /></li>
<li><img src="images/3.gif" alt="" /></li>
<li><img src="images/4.gif" alt="" /></li>
</ul>

And the javascript:

HSlideMenu("hslidemenu").build();

下载:
Download:
hslidemenu.js
hslidemenu-plug.js
点击这里看完整的简介!
Click here to see the full introduce page!

01

Iframe onload event is not a good idea to listening child page loading state on Internet Explorer. Here are some solutions to make things better.

None-Cross-Domain

If the parent page and the child page is in the same domain, they can share the objects between the windows. For example: “window.parent.document.getElementById(‘div_01′)” will be fine. So we can easily get things done. Just like this:

<!--This is an inner page in the iframe-->
<script type="text/javascript">
window.onload=function{
  window.parent.iframeCall();
}
</script>

Cross-Domain

According to REFERENCES Q239638 and Q188763, there are two ways:

1.Timer & document.readyState

var fm1=window.frames["iframe1"];
var fmState=function(){
  var state=null;
  if(document.readyState){
    try{
      state=fm1.document.readyState;
    }catch(e){state=null;}
    if(state=="complete" || !state){//loading,interactive,complete
      //onComplete();
      return;
    }
    window.setTimeout(fmState,10);
  }
};
window.setTimeout(fmState,400);//call this statement when iframe page start to change

A few microseconds is need, because DOM actions is asynchronous, we need to wait until the action has started.

2.onreadystatechange & counting state

var stateID={};
var fmStChange=function(){
  if(ifFirstLoad) return;
  stateID[this.id]=stateID[this.id] ? stateID[this.id]+1:1;
  switch (stateID[this.id]){
    case 1:
      //state loading
      //onComplete(STEP1);
      break;
    case 2:
      //state interactive
      //onComplete(STEP2);
      break;
    case 3:
      //state complete
      //onComplete(LASTSTEP);
      break;
  }
  if(stateID[this.id]&gt;=3) stateID[this.id]=null;
};
$("iframe1").onreadystatechange=fmStChange;
//if you want to ignore the parent page load
//add the following two line
var ifFirstLoad=true;
$("iframe1").onload=function(){ifFirstLoad=false;}

The process will return three types of state: loading,interactive and complete. But don’t disturb the loading process untill one is finished, or else the stateID will not tell the correct state .

document.domain property

An article said document.domain property will be use to fix “Access Denied Error”:

The document.domain property can be set to ease the restriction somewhat.For example, if a document at www.example.com wants to communicate with a document at forums.example.com, the document.domain property could be set to example.com in both documents to allow JavaScript interaction between them.

I never try out, but I don’t think this will work.

None-IE

Iframe onload event is good for Firefox, Opera and Safari.

26

Steve has write an article about faster javascript trim . He had collected many implementations of trim in javascript, and take a test to find the most efficient approach. At last, he wrote a new implementation and it is the fastest!

This article it’s great . And I found this is a very interesting subject. I was stuck on it. But Steve didn’t show up the testing codes, so I build one, with the codes here.

My Testing Page:

In this page, you’ll see three textarea at the top of page. Those are Template String list. The methods are all from faster javascript trim.

About Template String:

The string there is including the same leading whitespace and the same trailing whitespace. and these whitespace is including several kinds of characters like \s,\t,\n,\u3000.

In Firefox and opera, these whitespace are all match by \s, but not in IE. In IE, It is different from \u3000 and \s , So I replace all the \u3000 to \s in IE in order to make all the trim methods can work fine.

You can see the details of the temlate strings by making a simple test. Just double on the method or string list to start , or the start button too.

From the detail column of the result table, you’ll see something like this:

[\s28][有限状态机[...]平稳流畅。][\s61].length(898)

What does this mean? From the left to right, they are the length of leading whitespace, the trimed string, the length of trailing whitespace and the total length of the string before triming.

About the String List:

By the name of the string, you will see what the main different they are. And they are all from the template string area, so you can change them too. For example, the _shortStr and _longStr is from the first template string textarea. The different is, the _shortStr is just one time of the template string, but the _longStr 100 times of it.

Notic: the _bigWhiteSpaceStr string is real a big white leading and trailing space, and the length are 28,028 and 61,061!

Thank you.

This is a javascript practice for me,you can right click and view the page resrource any time. And I am still learning how to make codes better and better, please leave a reply if any suggestions, Thank you!

16

Iframs很多时候,我们会需要改变一个iframe的地址(src属性),或者使用表单(form)的target在指定的iframe进行提交后,在iframe加载完毕(onload)时立即响应某个操作,以提高WEB应用程序的价值。本文讨论了跨浏览器的iframe onload事件的监听方法。

如果你没时间去阅读全文,可以看解决方案的内容概要:

  1. 同域的页面嵌套,最好的是让内嵌的页面调用父页面的函数,如 window.parent.callparentFunctoin()。
  2. 如果是异域,或者子页面已存在且无法修改,那么:在Firefox/Opera/Safari中,可以直接使用iframe onload事件;而在IE中,可以通过定时器测定子页面的document.readyState,或者使用iframe onreadystatechange事件计算该事件的响应次数。

以上内容基于参考文档: Q239638Q188763.

如果你对这个话题很感兴趣,请一定要继续阅读哦。。。

注[1]:为了使问题更集中,本文所述的<iframe>均直接写在父页面中,对使用document.createElement(“iframe”)或者其它方式建立的iframe不作讨论,因为这样会使问题在IE下变得更加复杂,但只要使用本文的结论,无论何方式下建立的iframe,问题仍然会得到解决。

一个简单的包含iframe的父页面将是如下样子的:

<!DOCTYPE ...>
<html xmlns="...">
  <head>
  <meta ... />
  <title>Iframe</title>
  <body>
    <iframe name="iframe1"  id="iframe1" width="300" height="50" src="#" ></iframe>
    <script type="text/javascript">//codes here</script>
  </body>
</html>

开始:

让我们从一种简单的情形和解决方法开始:

1.window.parent 对象

1.1 调用父页面对象

<!–This is an inner page in the iframe–>
<script type=”text/javascript”>
window.onload=function{ window.parent.iframeCall();}
</script>

在网上找到的方法中,最令人开心的一个,莫过于在能子页面中调用父页面的对象了:

window.parent.callFunciton()。

不过我想:可能这点全地球人都已经知道了。只是这个方法有一个缺点,那就是子父页面必须在同域中。

还有一点,就是前端工程师们需对子页面有修改权;或者,可以请负责此子页面的同事为我们添加一段代码:

<script type=”text/javascript”>
if(window.parent!=window) window.parent.iframeCall();
</script>

把它放到window.onlad中,或者直接放在</body>之前。

注[2]:在对iframe或其它窗口性质的前端编程中,同域名是最完美的先天条件。只要在同一域名中,各个窗口间的对象是共享的,我们完全可以自由发挥,在不同的窗口间来回驾驭。总之,只有想不到,没有做不到。

1.2 异域

在不同域名的页面,浏览器出于安全考虑,几乎完全封锁了页面间的对象来往,这里没有鹊桥,牛郎和织女只能远远想望。当然,用iframe嵌套页面还是可以的,毕竟还可以思念。

面对家族的封锁,罗密欧还是很想见朱丽叶,他在夜里架起梯子抓到朱丽叶的窗前与她见面;在异域的页面嵌套中,子页面总是可以直接改变父窗口的location以防止被嵌套,但父页面对这个一点办法也没有。

当然,子页面除了仅仅永恒地拥有父窗口.location的修改权外,也没有其它了。例如,在IE下,子页面只能直接修改父页面的.location为另一个源:

<script tyle=”text/javascript”>parent.location=”http://anotherPage.com/”;</script>

但无法访问其它对象,如window.name,document等,连location.href等location的子属性就无法访问。当然,在防止嵌套方面,使用top.location会更强大。

但Firefox中,似乎还可以为top.location添加一些东西,但这是在我不严谨的测试中出现过的情况,未经找到相应的权威文档哦。

2. iframe onload 事件

在Firefox/Opera/Safari中,直接使用frame元素的onload事件即可:
document.getElementById(“iframe1”).onload=function(){
//your codes here.
};
只可惜它在IE下经常无效,因为在IE下它最多只能被激活一次,而且无论你有多少个iframe,被激活的也只能是最后一个的。更详细的描述请看:Q239638Q188763

原因
这些事件是在IFRAME内的文档对象模型中激活的,而不是父页面的。在IFRAME加载完毕的时候,这个事件就被激活了,而且ReadyState已经是“完成”状态。所以你无法通过这个事件来查检一个IFRAME是否加载完毕。

为了得到更好的表现,我们再稍稍研究一个问题:IFRAME递归。

3.IFRAME 递归

在处理IFRAME时,浏览器应该有一个基本规则,那就是防止递归,防止页面无限的自我加载,使客户端设备崩溃。事实上,文中出现的几个浏览器均做到这点,只是不同的浏览器有不同的处理方式。请分别尝试以下代码:
<iframe src=”” onload=”finish()” name=”iframe1”></iframe>
<iframe src=”#hashonly” onload=”finish()” name=”iframe2”></iframe>
<iframe src=”?search” onload=”finish()” name=”iframe3”></iframe>
<iframe src=”http://anotherPage.com” onload=”finish()” name=”iframe4”></iframe>
执行的结果是,在父页面加载时,上面的iframe onload函数在IE/Opera/Safari中均会被激活,Firefox对第二个没有反应。这主要因为他们在防止递归方面的处理是不同的。
对于#hashonly和?search这样的URL,浏览器会解释为页面本身。但hash和search的不同之处是,改变 search可以组成新的源,而改变hash不会。通常地,浏览器一遇到同源的iframe内页即会停止加载,但Safari却会加载多一次。
假如把finish()函数写成如下:
var finsh=function(){alert(”onload from :”+this.src);}
运行时分别弹出的消息弹出框的次数如下:

ifm/brw:    IE    |    Firefox    |    Opera    |    Safari
iframe1:    1     |       1       |      1      |      0
iframe2:    1     |       0       |      1      |      1
iframe3:    2     |       1       |      2      |      2
iframe4:    1     |       1       |      1      |      1

再结合页面所呈现的内容,可得看出这些浏览器在处理递归问题上的一些细则:

  • Firefox 不会在iframe中加载任何东西和激活onload事件(可能是任何事件)
  • IE和Opera不会在iframe中加载页面,但会激活onload事件。
  • Safari(windows版本)会在iframe中加载页面一次且仅仅一次,并会激活onlaod事件且仅激活依附在父页面上那个iframe的onload事件。

关于本节,如果仅把iframe用于页面嵌套,那意义不大;如果用于动态加载/呈现内页,或者用于良好用户体验的form target表单提交处理(不是Ajax),并且要求较高的浏览器兼容性时,作用才会显示出来。根据本节结果,为了提高兼容性,最好事先把iframe指向一个空页面——blank.html,因为它在4种浏览器中的表现是一样的。如果不想事先加载页面,那就得花多点心思去判断浏览器类型了。

4.代码实现

4.1Firefox/Opera/Safari,直接使用iframe onload事件

document.getElementById(“iframe1”).onload=function(){
    //your codes here.
};

4.2在IE下,定时器测document.readyState或者注册iframe onreadystatechange事件

4.2.1定时器以及document.readyState

var fm1=window.frames["iframe1"];
var fmState=function(){
  var state=null;
  if(document.readyState){
    try{
      state=fm1.document.readyState;
    }catch(e){state=null;}
    if(state=="complete" || !state){//loading,interactive,complete
      //onComplete();
      return;
    }
    window.setTimeout(fmState,10);
  }
};
//在改变src或者通过form target提交表单时,执行语句:
if(fmState.TimeoutInt) window.clearTimeout(fmState.timeoutInt);
fmState.timeoutInt = window.setTimeout(fmState,400);

为什么要延时400毫秒?因为javascript对DOM的操作是异步的,我们必须等待脚本对DOM落实执行后才开始下一步。400秒这个数取决与客户端的设备和浏览器的响应速度,好的设备的响应速度能在10毫秒以内甚至更快,但100毫秒左右可能比较大众化,400毫秒应该是十分保守的了。总之,较大的毫秒数能适合更多的用户设备状况,并能减少了客户端设备的工作量。

至于document.readyState,指的是iframe内子页的docuent.readyState,而不是父页面的。只要允许,即在同域情况下,document.readyState会返回5个状态:

uninitialized 对象未初始化.
loading 对象正在加载数据.
loaded 对象已加载完数据.
interactive 在这个状态下,用户可以参与互动,即使在对象未加载完毕也可以
complete 对象已完成初始化.

为什么使用try和 catch?因为在异域的情况下,当iframe的子页到达interactive状态时,父页面就会失去访问权,所以最多只能返回到loaded这一步,因此IE出一个未知错误——其实就是没有权限,所以try和catch,让这个错误沉默下去。

幸好这个方法只针对IE(目前我能使用到的版本:IE6/7),否则麻烦大了:Opera不等页面加载完就开始交互了,而IE会等页面加载完毕才进行交互,所以感觉用Opera打开网页的速度相对比IE快。

4.2.2.onreadystatechange 事件三步曲

var stateID={};
var fmStChange=function(){
  if(ifFirstLoad) return;
  stateID[this.id]=stateID[this.id] ? stateID[this.id]+1:1;
  switch (stateID[this.id]){
    case 1:
      //state loading
      //onComplete(STEP1);
      break;
    case 2:
      //state interactive
      //onComplete(STEP2);
      break;
    case 3:
      //state complete
      //onComplete(LASTSTEP);
      break;
  }
  if(stateID[this.id]&gt;=3) stateID[this.id]=null;
};
$("iframe1").onreadystatechange=fmStChange;
//if you want to ignore the parent page load
//add the following two line
var ifFirstLoad=true;
$("iframe1").onload=function(){ifFirstLoad=false;}

每当iframe加载页面,过程内会激活onreadystatechange事件三次,相应的状态分别是loading,interactive和complete,而最后一次才是complete,所以我们得计算一下,直到第三次才算是完成。

注意:这个方案中的stateID在状态判断时非常重要,要进行必要的修正和保护,如在每次应用iframe时,把stateID[iframe_id]复位为null,或者在第三次响应完成之前,不要对iframe进行新轮页面加载,或者在新一轮的页面加载前消除之前的事件并复位。

29

Javascript的每个Function对象中有一个apply方法:

function.apply([thisObj[,argArray]])

此外,还有另一个很极之相似方法:

function.call([thisObj[,arg1[, arg2[, [,.argN]]]]])

他们的共同之处是,都“可以用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。”——摘自JScript5.5 .chm

她们的不同之处是,apply最多只能有两个参数——新this对象和一个数组,如果给该方法传递多个参数,则把参数都写进这个数组里面,当然,即使只有一个参数,也要写进数组里面。而call则是直接的参数列表,对比如下: 阅读全文(Read the rest of this entry) »

22

在Javascript前端编程中,使用过Prototype.js的人,会知道,只要使用一个简单的语句:

$("msg").hide();

就可以把id为msg的元素隐藏;同样的,$("msg").show()则可以把隐藏的元素显示出来。像这种对元素的扩展,是怎样做出来的呢?

在说明这个问题之前,先问一个问题:为什么要这样做?

为什么要对DOM element进行扩展?

如果你在Javascript编辑中,不使用任何库,即祼编去实现一些说大不大说小不小应用时,相信你一定会事先$一下:

var $=function (s){
return typeof(s)=="string" ? document.getElementById(s) : s;
};

或者更简单的

function $(){return document.getElementById(s);}

这样一来可以加快你写代码的速度,二来可以为页面节省不少字节。

但如果我们还需要很多对DOM的操作,而且这些操作具有一定的复杂性重用,需要分离出来,如对属性className的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
rmCls=function(el,_c){// the full name is removeClassName
    el=$(el);if(!el) return null;
    var c=el.className.split(" "),str="";
    for(var i in c){if(c[i]!=_c) str+=" "+c[i];}
    el.className=str;
    return el;
},
adCls=function(el,c){// the full name is addClassName
    el=$(el);if(!el) return null;
    this.rmCls(el,c).className+=" "+c;
    return el;
},
hasCls=function(el,_c){// the full name is hasClassName
    var el=$(el),c=(el.className || "").split(" "), l=c.length;
    while(l--){if(c[l]==_c) return true;};
    return false;
};

当然,我们也可以直接使用$(“msg”).className=”on”来操作,但这对存在多个类名的元素来说就不那么便利了。但现在也不太便利,因为我们要把元素也写到参数中:
adCls($("msg"),"on");
如果可以这样似乎会更清爽些:
$("msg").adCls("on");
阅读全文(Read the rest of this entry) »

30

A CION HAS TWO SIDES.

“一枚硬币有两个面。”

这句话通常用于说明事物有两面性。Javascript的闭包也一样。

所谓的闭包(Closure):

所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

闭包是 ECMAScript (JavaScript)最强大的特性之一,但用好闭包的前提是必须理解闭包。闭包的创建相对容易,人们甚至会在不经意间创建闭包,但这些无意创建的闭包却存在潜在的危害,尤其是在比较常见的浏览器环境下。如果想要扬长避短地使用闭包这一特性,则必须了解它们的工作机制。而闭包工作机制的实现很大程度上有赖于标识符(或者说对象属性)解析过程中作用域的角色。 (From 《理解Javascript闭包》)

问题所在: 阅读全文(Read the rest of this entry) »

06

UPDATE:20080530最新修改

UPDATE:20081221补充说明::oops: 小弟写这篇东西实在是班门弄斧了,这是当时我刚理解完时就写下的,说的口水多,没什么实例,非但没把问题说清楚也把大家搞糊涂了,实在不好意思。如果想要知道实例,请搜索一下Currying in Javascript 吧,用到Currying的地方,几乎都会出现这种写法。其实这里主要说的不是把参数换成数组,而是更玄妙一点的JS运用。这里见笑了,如果可以,以后会换个更直接明了的书写风格的。

同时更正了一处链接。

关于本文所说明的问题有更简洁的另一个解释方法:什么是Array.prototype.slice.call。

为了研究Currying in Javascript,翻阅了很多外文网站,为的是找一个比较好的Currying方案,找到的比较详细的有如下(文章内部链接可能包含更多的相关文章):

  1. Currying JavaScript Functions
  2. JavaScript Currying Redux
  3. JavaScript currying
  4. functional.js 介绍及源码分析

发现大多人都用了Array.prototype.slice.call(argments,0),一直不明白这句是干什么的。而昨天温习了slice()方法,再参考Function.call(thisArg[, arg1[, arg2[, ...]]]),还是不得而知(我脑筋转得慢:|)。上网查,第一时间查老外的Google.com in English也没解释,真到查找“Array.prototype.slice 用法”时,才得到数篇。虽然我很鄙视搞收集的人,搞得互联网文章像是天下一大抄似的,但现在却恨少,只得三篇(一模一样的三篇,责任编辑为admin或者超级管理员,作者是佚名。)

下面就我的理解,对Array.prototype.slice.call进行解说,菜鸟留下,大虾吐口水:
Update: 下面写的可能有点难懂,怕刚接触Javascript的人可能会看不懂,请大家看完后留个言,谢谢!

Array.prototype.slice.call(thisArg[, arg1[, arg2[, ...]]])

成员介绍:

Array [object]

Array对象(即数组对象)

prototype [property]

Javascript的原型,此prototype非Prototype.js,可以暂且理解为Java中的静态属性/方法。总之这个不好说,详细请看JavaScript中的prototype(原型)属性研究

slice [Function]

数组切分方法,很有用的一个方法,具体请看这里,或那里

call [Function]

神奇的方法,该方法是所有Function对象里的方法,所以slice就有这样一个方法。用于把Function对象里的this替换为目标对象。看这里

串起来就是: 阅读全文(Read the rest of this entry) »

05

为了研究Currying in Javascript,所以温习一下slice()方法。

Array.slice(begin[,end])

该slice方法返回数据的指定区段。该slice方法不会改变原始的数组,返回的是一个全新的数组拷贝,而不是原始数组引用,所以无论是改变原始数组还是新数组,双方均不受影响。该slice方法通过由零开始计算的数组下标来确定切割的部分,创建一个由下标begin开始,到下标end-1止的新数组(如果end未被指定,默认为最后一个元素)。如:
代码:

1
2
trees = ["oak", "ash", "beech", "maple", "sycamore"]
document.write(trees.slice(1,4))

输出:
ash,beech,maple
阅读全文(Read the rest of this entry) »