UnderScore.jsAPI记录

时间:2014-05-22 14:32:08   收藏:0   阅读:649

Collection Functions (Arrays or Objects)

each         _.each(list, iterator, [context]) 

遍历list中的所有元素,如果传递了context参数,则把iterator绑定到context对象上。iterator的参数是 (value, key, list))。返回list以方便链式调用。

_.each([1, 2, 3], alert);
=> alerts each number in turn...

map    _.map(list, iterator, [context]) 

通过变换函数(iterator迭代器)把list中的每个值映射到一个新的数组中(愚人码头注:产生一个新的数组)。

 

reduce  _.reduce(list, iterator, memo, [context])

reduce方法把list中元素归结为一个单独的数值。

reduceRight_.reduceRight(list, iterator, memo, [context]) 

find_.find(list, predicate, [context]) 

list中逐项查找,返回第一个通过predicate迭代函数真值检测的元素值,如果没有值传递给测试迭代器将返回undefined

filter_.filter(list, predicate, [context]) 

遍历list中的每个值,返回包含所有通过predicate真值检测的元素值。

var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> [2, 4, 6]

where_.where(list, properties) 

遍历list中的每一个值,返回一个数组,这个数组包含包含properties所列出的属性的所有的键 - 值对。

_.where(listOfPlays, {author: "Shakespeare", year: 1611});
=> [{title: "Cymbeline", author: "Shakespeare", year: 1611},
    {title: "The Tempest", author: "Shakespeare", year: 1611}]

 

findWhere_.findWhere(list, properties) 

reject_.reject(list, predicate, [context]) 

返回list中没有通过predicate真值检测的元素集合,与filter相反。

every_.every(list, [predicate], [context]) 

如果list中的所有元素都通过predicate的真值检测就返回true

some_.some(list, [predicate], [context]) 

如果list中有任何一个元素通过 predicate 的真值检测就返回true

contains_.contains(list, value) 

如果list包含指定的value则返回true

invoke_.invoke(list, methodName, *arguments)

list的每个元素上执行methodName方法。 

_.invoke([[5, 1, 7], [3, 2, 1]], ‘sort‘);
=> [[1, 5, 7], [1, 2, 3]]

pluck_.pluck(list, propertyName) 

pluck也许是map最常使用的用例模型的简化版本,即萃取对象数组中某属性值,返回一个数组。

max_.max(list, [iterator], [context]) 

返回list中的最大值。

min_.min(list, [iterator], [context]) 

返回list中的最小值。

sortBy_.sortBy(list, iterator, [context]) 

返回排序后的列表数据

groupBy_.groupBy(list, iterator, [context]) 

把一个集合分组为多个集合,通过 iterator 返回的结果进行分组. 

indexBy_.indexBy(list, iterator, [context]) 

给定一个list,和 一个用来返回一个在列表中的每个元素键 的iterator 函数(或属性名), 返回一个每一项索引的对象。

 var stooges = [{name: ‘moe‘, age: 40}, {name: ‘larry‘, age: 50}, {name: ‘curly‘, age: 60}];

_.indexBy(stooges, ‘age‘);
=> {
  "40": {name: ‘moe‘, age: 40},
  "50": {name: ‘larry‘, age: 50},
  "60": {name: ‘curly‘, age: 60}
}

countBy_.countBy(list, iterator, [context]) 

排序一个列表组成一个组,并且返回各组中的对象的数量的计数。

shuffle_.shuffle(list) 

返回一个随机乱序的 list 副本, 

sample_.sample(list, [n]) ,

从 list中产生一个随机样本。列表,长度。

_.sample([1, 2, 3, 4, 5, 6]);
=> 4

_.sample([1, 2, 3, 4, 5, 6], 3);
=> [1, 6, 2]

toArray_.toArray(list) 

list(任何可以迭代的对象)转换成一个数组,在转换 arguments 对象时非常有用。

size_.size(list) 

返回list的长度。

Array Functions

first_.first(array, [n]) 

返回array(数组)的第一个元素。

initial_.initial(array, [n]) 

返回数组中除了最后一个元素外的其他全部元素。

last_.last(array, [n]) 

返回array(数组)的最后一个元素。

rest_.rest(array, [index]) 

返回数组中除了第一个元素外的其他全部元素。

compact_.compact(array) 

返回一个除去所有false值的 array副本。

 

flatten_.flatten(array, [shallow]) 

将一个嵌套多层的数组 array(数组) (嵌套可以是任何层数)转换为只有一层的数组。 如果你传递 shallow参数,数组将只减少一维的嵌套。

_.flatten([1, [2], [3, [[4]]]]);
=> [1, 2, 3, 4];

_.flatten([1, [2], [3, [[4]]]], true);
=> [1, 2, 3, [[4]]];

without_.without(array, *values) 

返回一个删除所有values值的 array副本。(愚人码头注:使用===表达式做相等测试。)

_.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
=> [2, 3, 4]

partition_.partition(array, predicate) 

 

 

 

union_.union(*arrays) 

返回传入的 arrays(数组)并集:按顺序返回,数组的元素是唯一的,可以传入一个或多个 arrays(数组)

intersection_.intersection(*arrays) 

返回传入 arrays(数组)交集。结果中的每个值是存在于传入的每个arrays(数组)里。

 

difference_.difference(array, *others) 

类似于without,但返回的值来自array参数数组,并且不存在于other 数组.

_.difference([1, 2, 3, 4, 5], [5, 2, 10]);
=> [1, 3, 4]

uniq_.uniq(array, [isSorted], [iterator]) 

返回 array去重后的副本, 使用 === 做相等测试. 如果您确定 array 已经排序, 那么给isSorted 参数传递 true值, 此函数将运行的更快的算法. 如果要处理对象元素, 传参iterator 来获取要对比的属性.

_.uniq([1, 2, 1, 3, 1, 4]);
=> [1, 2, 3, 4]

zip_.zip(*arrays) 

将 每个相应位置的arrays的值合并在一起。在合并分开保存的数据时很有用. 如果你用来处理矩阵嵌套数组时, _.zip.apply 可以做类似的效果。

_.zip([‘moe‘, ‘larry‘, ‘curly‘], [30, 40, 50], [true, false, false]);
=> [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]

object_.object(list, [values]) 

将数组转换为对象。传递任何一个单独[key, value]对的列表,或者一个键的列表和一个值得列表。 如果存在重复键,最后一个值将被返回。

_.object([‘moe‘, ‘larry‘, ‘curly‘], [30, 40, 50]);
=> {moe: 30, larry: 40, curly: 50}

_.object([[‘moe‘, 30], [‘larry‘, 40], [‘curly‘, 50]]);
=> {moe: 30, larry: 40, curly: 50}

indexOf_.indexOf(array, value, [isSorted]) 

返回value在该 array 中的索引值,如果value不存在 array中就返回-1。使用原生的indexOf 函数,除非它失效。如果您正在使用一个大数组,你知道数组已经排序,传递trueisSorted将更快的用二进制搜索..,或者,传递一个数字作为第三个参数,为了在给定的索引的数组中寻找第一个匹配值。

_.indexOf([1, 2, 3], 2);
=> 1

lastIndexOf_.lastIndexOf(array, value, [fromIndex]) 

lastIndexOf_.lastIndexOf(array, value, [fromIndex]) 
返回value在该 array 中的从最后开始的索引值,如果value不存在 array中就返回-1。如果支持原生的lastIndexOf,将使用原生的lastIndexOf函数。 传递fromIndex将从你给定的索性值开始搜索。

_.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
=> 4

sortedIndex_.sortedIndex(list, value, [iterator], [context]) 

使用二分查找确定valuelist中的位置序号,value按此序号插入能保持list原有的排序。 如果提供iterator函数,iterator将作为list排序的依据,包括你传递的value 。iterator也可以是字符串的属性名用来排序(比如length)。

_.sortedIndex([10, 20, 30, 40, 50], 35);
=> 3

var stooges = [{name: ‘moe‘, age: 40}, {name: ‘curly‘, age: 60}];
_.sortedIndex(stooges, {name: ‘larry‘, age: 50}, ‘age‘);
=> 1

range_.range([start], stop, [step]) 

一个用来创建整数灵活编号的列表的函数,便于each 和 map循环。如果省略start则默认为 0step 默认为 1.返回一个从start 到stop的整数的列表,用step来增加 (或减少)独占。值得注意的是,如果stop值在start前面(也就是stop值小于start值),那么值域会被认为是零长度,而不是负增长。-如果你要一个负数的值域 ,请使用负数step.

_.range(10);
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_.range(1, 11);
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
_.range(0, 30, 5);
=> [0, 5, 10, 15, 20, 25]
_.range(0, -10, -1);
=> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
_.range(0);
=> []

Function (uh, ahem) Functions

bind_.bind(function, object, *arguments) 

绑定函数 function 到对象 object 上, 也就是无论何时调用函数, 函数里的 this 都指向这个 object. 任意可选参数 arguments 可以绑定到函数 function , 可以填充函数所需要的参数, 这也被成为 partial application

var func = function(greeting){ return greeting + ‘: ‘ + this.name };
func = _.bind(func, {name: ‘moe‘}, ‘hi‘);
func();
=> ‘hi: moe‘

bindAll_.bindAll(object, *methodNames) 

methodNames参数指定的方法绑定到object上,这些方法就会在对象的上下文环境中执行。绑定函数用作事件处理函数时非常便利,否则函数被调用时this一点用也没有。如果不设置methodNames参数,对象上的所有方法都会被绑定。

var buttonView = {
  label  : ‘underscore‘,
  onClick: function(){ alert(‘clicked: ‘ + this.label); },
  onHover: function(){ console.log(‘hovering: ‘ + this.label); }
};
_.bindAll(buttonView, ‘onClick‘, ‘onHover‘);
// When the button is clicked, this.label will have the correct value.
jQuery(‘#underscore_button‘).bind(‘click‘, buttonView.onClick);

partial_.partial(function, *arguments) 

局部应用一个函数填充在任意个数的 参数改变其动态this值。和bind方法很相近。 You may pass _ in your list of arguments to specify an argument that should not be pre-filled, but left open to supply at call-time.

var add = function(a, b) { return a + b; };
add5 = _.partial(add, 5);
add5(10);
=> 15

memoize_.memoize(function, [hashFunction]) 

Memoizes方法可以缓存某函数的计算结果。对于耗时较长的计算是很有帮助的。如果传递了 hashFunction 参数,就用 hashFunction 的返回值作为key存储函数的计算结果。 hashFunction 默认使用function的第一个参数作为key

var fibonacci = _.memoize(function(n) {
  return n < 2 ? n: fibonacci(n - 1) + fibonacci(n - 2);
});

delay_.delay(function, wait, *arguments) 

elay类似setTimeout,等待wait毫秒后调用function。如果传递可选的参数arguments,当函数function执行时, arguments 会作为参数传入。

var log = _.bind(console.log, console);
_.delay(log, 1000, ‘logged later‘);
=> ‘logged later‘ // Appears after one second.

defer_.defer(function, *arguments) 

延迟调用function直到当前调用栈清空为止,类似使用延时为0的setTimeout方法。对于执行开销大的计算和无阻塞UI线程的HTML渲染时候非常有用。 如果传递arguments参数,当函数function执行时, arguments 会作为参数传入。

_.defer(function(){ alert(‘deferred‘); });
// Returns from the function before the alert runs.

throttle_.throttle(function, wait, [options]) 

创建并返回一个像节流阀一样的函数,当重复调用函数的时候,最多每隔 wait毫秒调用一次该函数。对于想控制一些触发频率较高的事件有帮助。(愚人码头注:详见:javascript函数的throttle和debounce

默认情况下,throttle将在你调用的第一时间尽快执行这个function,并且,如果你在wait周期内调用任意次数的函数,都将尽快的被覆盖。如果你想禁用第一次首先执行的话,传递{leading: false},还有如果你想禁用最后一次执行的话,传递{trailing: false}

var throttled = _.throttle(updatePosition, 100);
$(window).scroll(throttled);

debounce_.debounce(function, wait, [immediate]) 

返回 function 函数的防反跳版本, 将延迟函数的执行(真正的执行)在函数最后一次调用时刻的 wait 毫秒之后. 对于必须在一些输入(多是一些用户操作)停止到达之后执行的行为有帮助。 例如: 渲染一个Markdown格式的评论预览, 当窗口停止改变大小之后重新计算布局, 等等.

传参 immediate 为 true 会让debounce 在 wait 间隔之后 触发最后的函数调用而不是最先的函数调用.在类似不小心点了提交按钮两下而提交了两次的情况下很有用.

var lazyLayout = _.debounce(calculateLayout, 300);
$(window).resize(lazyLayout);

once_.once(function) 

创建一个只能调用一次的函数。重复调用改进的方法也没有效果,只会返回第一次执行时的结果。 作为初始化函数使用时非常有用, 不用再设一个boolean值来检查是否已经初始化完成.

var initialize = _.once(createApplication);
initialize();
initialize();
// Application is only created once.

after_.after(count, function) 

创建一个函数, 只有在运行了 count 次之后才有效果. 在处理同组异步请求返回结果时, 如果你要确保同组里所有异步请求完成之后才 执行这个函数, 这将非常有用.

var renderNotes = _.after(notes.length, render);
_.each(notes, function(note) {
  note.asyncSave({success: renderNotes});
});
// renderNotes is run once, after all notes have saved.

now_.now() 

一个优化的方式来获得一个当前时间的整数时间戳。 可用于实现定时/动画功能。

_.now();
=> 1392066795351

wrap_.wrap(function, wrapper) 

将第一个函数 function 封装到函数 wrapper 里面, 并把函数 function 作为第一个参数传给 wrapper. 这样可以让 wrapper 在 function 运行之前和之后 执行代码, 调整参数然后附有条件地执行.

var hello = function(name) { return "hello: " + name; };
hello = _.wrap(hello, function(func) {
  return "before, " + func("moe") + ", after";
});
hello();
=> ‘before, hello: moe, after‘

compose_.compose(*functions)

返回函数集 functions 组合后的复合函数, 也就是一个函数执行完之后把返回的结果再作为参数赋给下一个函数来执行. 以此类推. 在数学里, 把函数 f()g(), 和 h() 组合起来可以得到复合函数 f(g(h())).

var greet    = function(name){ return "hi: " + name; };
var exclaim  = function(statement){ return statement.toUpperCase() + "!"; };
var welcome = _.compose(greet, exclaim);
welcome(‘moe‘);
=> ‘hi: MOE!‘

Object Functions

keys_.keys(object) 

获取object对象所有的属性名称。

_.keys({one: 1, two: 2, three: 3});
=> ["one", "two", "three"]

values_.values(object) 

返回object对象所有的属性值。

_.values({one: 1, two: 2, three: 3});
=> [1, 2, 3]

pairs_.pairs(object) 

把一个对象转变为一个[key, value]形式的数组。

_.pairs({one: 1, two: 2, three: 3});
=> [["one", 1], ["two", 2], ["three", 3]]

invert_.invert(object) 

R返回一个object副本,使其键(keys)和值(values)对换。对于这个操作,必须确保object里所有的值都是唯一的且可以序列号成字符串.

_.invert({Moe: "Moses", Larry: "Louis", Curly: "Jerome"});
=> {Moses: "Moe", Louis: "Larry", Jerome: "Curly"};

functions_.functions(object) 

返回一个对象里所有的方法名, 而且是已经排序的 — 也就是说, 对象里每个方法(属性值是一个函数)的名称.

_.functions(_);
=> ["all", "any", "bind", "bindAll", "clone", "compact", "compose" ...

extend_.extend(destination, *sources) 

复制source对象中的所有属性覆盖到destination对象上,并且返回 destination 对象. 复制是按顺序的, 所以后面的对象属性会把前面的对象属性覆盖掉(如果有重复).

_.extend({name: ‘moe‘}, {age: 50});
=> {name: ‘moe‘, age: 50}

pick_.pick(object, *keys) 

返回一个object副本,只过滤出keys(有效的键组成的数组)参数指定的属性值。

_.pick({name: ‘moe‘, age: 50, userid: ‘moe1‘}, ‘name‘, ‘age‘);
=> {name: ‘moe‘, age: 50}
_.pick({name: ‘moe‘, age: 50, userid: ‘moe1‘}, function(value, key, object) {
  return _.isNumber(value);
});
=> {age: 50}

omit_.omit(object, *keys) 

返回一个object副本,只过滤出除去keys(有效的键组成的数组)参数指定的属性值。

_.omit({name: ‘moe‘, age: 50, userid: ‘moe1‘}, ‘userid‘);
=> {name: ‘moe‘, age: 50}
_.omit({name: ‘moe‘, age: 50, userid: ‘moe1‘}, function(value, key, object) {
  return _.isNumber(value);
});
=> {name: ‘moe‘, userid: ‘moe1‘}

defaults_.defaults(object, *defaults) 

defaults对象填充objectundefined属性。并且返回这个object。一旦这个属性被填充,再使用defaults方法将不会有任何效果。

var iceCream = {flavor: "chocolate"};
_.defaults(iceCream, {flavor: "vanilla", sprinkles: "lots"});
=> {flavor: "chocolate", sprinkles: "lots"}

clone_.clone(object) 

创建 一个浅复制(浅拷贝)的克隆object。任何嵌套的对象或数组都通过引用拷贝,不会复制。

_.clone({name: ‘moe‘});
=> {name: ‘moe‘};

tap_.tap(object, interceptor) 

用 object作为参数来调用函数interceptor,然后返回object。这种方法的主要意图是作为函数链式调用 的一环, 为了对此对象执行操作并返回对象本身。

_.chain([1,2,3,200])
  .filter(function(num) { return num % 2 == 0; })
  .tap(alert)
  .map(function(num) { return num * num })
  .value();
=> // [2, 200] (alerted)
=> [4, 40000]

has_.has(object, key) 

 

对象是否包含给定的键吗?等同于object.hasOwnProperty(key),但是使用hasOwnProperty 函数的一个安全引用,以防意外覆盖

_.has({a: 1, b: 2, c: 3}, "b");
=> true

property_.property(key) 

返回一个函数,这个函数返回任何传入的对象的key         属性。

var moe = {name: ‘moe‘};
‘moe‘ === _.property(‘name‘)(moe);
=> true

matches_.matches(attrs) 

返回一个断言函数,这个函数会给你一个断言             可以用来辨别 给定的对象是否匹配指定键/值属性的列表。

var ready = _.matches({selected: true, visible: true});
var readyToGoList = _.filter(list, ready);

isEqual_.isEqual(object, other) 

执行两个对象之间的优化深度比较,确定他们是否应被视为相等。

var moe   = {name: ‘moe‘, luckyNumbers: [13, 27, 34]};
var clone = {name: ‘moe‘, luckyNumbers: [13, 27, 34]};
moe == clone;
=> false
_.isEqual(moe, clone);
=> true

isEmpty_.isEmpty(object) 

如果object 不包含任何值(没有可枚举的属性),返回true

_.isEmpty([1, 2, 3]);
=> false
_.isEmpty({});
=> true

isElement_.isElement(object) 

如果object是一个DOM元素,返回true

_.isElement(jQuery(‘body‘)[0]);
=> true

isArray_.isArray(object) 

如果object是一个数组,返回true

(function(){ return _.isArray(arguments); })();
=> false
_.isArray([1,2,3]);
=> true

isObject_.isObject(value) 

如果object是一个对象,返回true。需要注意的是JavaScript数组和函数是对象,字符串和数字不是。

_.isObject({});
=> true
_.isObject(1);
=> false

isArguments_.isArguments(object) 

如果object是一个参数对象,返回true

(function(){ return _.isArguments(arguments); })(1, 2, 3);
=> true
_.isArguments([1,2,3]);
=> false

isFunction_.isFunction(object) 

如果object是一个参数对象,返回true

_.isFunction(alert);
=> true

isString_.isString(object) 

如果object是一个字符串,返回true

_.isString("moe");
=> true

isNumber_.isNumber(object) 

如果object是一个数值,返回true (包括 NaN)。

_.isNumber(8.4 * 5);
=> true

isFinite_.isFinite(object) 

如果object是一个有限的数字,返回true

_.isFinite(-101);
=> true

_.isFinite(-Infinity);
=> false

isBoolean_.isBoolean(object) 

如果object是一个布尔值,返回true

_.isBoolean(null);
=> false

isDate_.isDate(object) 

如果object是一个日期时间,返回true

_.isDate(new Date());
=> true

isRegExp_.isRegExp(object) 

如果object是一个正则表达式,返回true

_.isRegExp(/moe/);
=> true

isNaN_.isNaN(object) 

如果object是 NaN,返回true。 
注意: 这和原生的isNaN 函数不一样,如果变量是undefined,原生的isNaN 函数也会返回 true 。

_.isNaN(NaN);
=> true
isNaN(undefined);
=> true
_.isNaN(undefined);
=> false

isNull_.isNull(object) 

如果object的值是 null,返回true

_.isNull(null);
=> true
_.isNull(undefined);
=> false

isUndefined_.isUndefined(value) 

如果valueundefined,返回true

_.isUndefined(window.missingVariable);
=> true

Utility Functions

noConflict_.noConflict() 

放弃Underscore 的控制变量"_"。返回Underscore 对象的引用。

var underscore = _.noConflict();

identity_.identity(value) 

返回与传入参数相等的值. 相当于数学里的: f(x) = x
这个函数看似无用, 但是在Underscore里被用作默认的迭代器iterator.

var moe = {name: ‘moe‘};
moe === _.identity(moe);
=> true

constant_.constant(value) 

创建一个函数,这个函数 返回相同的值 用来作为_.constant的参数。

var moe = {name: ‘moe‘};
moe === _.constant(moe)();
=> true

times_.times(n, iterator, [context]) 

random_.random(min, max) 

返回一个min 和 max之间的随机整数。如果你只传递一个参数,那么将返回0和这个参数之间的整数。

_.random(0, 100);
=> 42

mixin_.mixin(object) 

uniqueId_.uniqueId([prefix]) 

escape_.escape(string) 

转义HTML字符串,替换&<>", and /字符。

_.escape(‘Curly, Larry & Moe‘);
=> "Curly, Larry &amp; Moe"

unescape_.unescape(string) 

escape相反。转义HTML字符串,替换&&lt;&gt;&quot;&#x27;, and&#x2F;字符。

_.unescape(‘Curly, Larry &amp; Moe‘);
=> "Curly, Larry & Moe"

result_.result(object, property) 

 

如果对象 object 中的属性 property 是函数, 则调用它, 否则, 返回它。

var object = {cheese: ‘crumpets‘, stuff: function(){ return ‘nonsense‘; }};
_.result(object, ‘cheese‘);
=> "crumpets"
_.result(object, ‘stuff‘);
=> "nonsense"

 

template_.template(templateString, [data], [settings]) 

将 JavaScript 模板编译为可以用于页面呈现的函数, 对于通过JSON数据源生成复杂的HTML并呈现出来的操作非常有用。 模板函数可以使用 <%= … %>插入变量, 也可以用<% … %>执行任意的 JavaScript 代码。 如果您希望插入一个值, 并让其进行HTML转义,请使用<%- … %>。 当你要给模板函数赋值的时候,可以传递一个含有与模板对应属性的data对象 。 如果您要写一个一次性的, 您可以传对象 data 作为第二个参数给模板 template来直接呈现, 这样页面会立即呈现而不是返回一个模板函数. 参数 settings 是一个哈希表包含任何可以覆盖的设置 _.templateSettings.

var compiled = _.template("hello: <%= name %>");
compiled({name: ‘moe‘});
=> "hello: moe"

var list = "<% _.each(people, function(name) { %> <li><%= name %></li> <% }); %>";
_.template(list, {people: [‘moe‘, ‘curly‘, ‘larry‘]});
=> "<li>moe</li><li>curly</li><li>larry</li>"

var template = _.template("<b><%- value %></b>");
template({value: ‘<script>‘});
=> "<b>&lt;script&gt;</b>"

Chaining

chain_.chain(obj) 

value_(obj).value() 

 

相关内容来自网络,http://www.css88.com/doc/underscore/#each

来自@愚人码头 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

UnderScore.jsAPI记录,布布扣,bubuko.com

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!