第五章 引用类型

引用类型的值(对象)是引用类型的一个实例。也就是说,对象是某个特定引用类型的实例。如:

var person = new Object(); 
//这段代码创建了一个Object引用类型的一个新实例;

一、 Object类型

对于在应用程序中存储和传输数据而言,Object对象是非常理想的选择。
创建Object实例有两种方式:

1.使用构造函数
var person = new Object();
person.name = "Nicholas";
person.age = 29;
2.使用对象字面量
var person = {
      name: "Nicholas",
      age:29
}

访问对象属性有两种方法:

1. 使用方括号表示法
alert(person["name"]);
var propertyName = "name";
alert(person[propertyName ]); //可以使用变量来访问属性。
alert(person["first name"]); //若属性名存在语法错误的字符,也可以使用方括号表示法
2. 使用点表示法
alert(person.name)

二、Array类型

ECMAScript数组的每一项可以保存任何类型的数据。创建数组有两种基本方式:

1.使用Array构造函数
var colors = new Array();
2.使用数组字面量表示法
var colors = ["red","blue","green"];
2.1 length属性

使用length属性不仅可以返回项数,还可以从数组的末尾移除或添加新项。

alert(colors.length) //3
colors.length = 2; //设置项数为2,删除数组第3项
alert(colors[2]);  //undefined 
colors[colors.length] = "black";
colors[colors.length] = "brown"; //利用length属性可以方便的向数组末尾添加项。
2.2 相关方法
1. 检测数组
   Array.isArray(value); 
2. 转换方法
    var colors = ["red","blue","green"];
    alert(colors.toString()) ; // red,blue,green
    alert(colors.valueOf()); // [red,blue,green]
    alert(colors); // red,blue,green
    //alert方法接收字符串参数,所以会自动调用toString()方法
    alert(colors.join(",")); //join方法接收一个参数,即作为分隔符的字符串。
3. 增删方法
   //push()和pop()
    var colors = ["red","blue"];
    var count = colors.push("brown"); // 3 在数组后端添加一项,返回数组长度。
    var item = colors.pop(); // “brown”  删除数组最后一项,并返回删除的项。
   // shift()和unshift()方法
    var item = colors.shift(); //"red" 移除数组的第一项并返回该项
    var count = colors.unshift("red","green");// 在数组前端推入两项,并返回数组长度。
4. 重排序方法
   //reverse();
    var values = [1,2,3,4,5];
    values.reverse();
    alert(values); //5,4,3,2,1
    //sort()
    var values = [0,1,5,10,15];
    values.sort();
    alert(values); // 0,1,10,15,5 sort()方法会默认调用数组的toString()方法后比较,往往不是想要的结果。
    //比较函数,升序
    function compare(value1,value2){
      if(value1 < value2){
         return -1; //返回负数,value1在value2之前
      } else  if (value1 > value2){
         return 1; //返回正数,value1在value2之后
      } else {
         return 0; //返回0,两数相等
      }
    }
    values.sort(compare);
    alert(values); //0,1,5,10,15
    //比较函数简化。适用于数值类型或valueOf返回数组类型的对象类型
    function compare1(value1,value2){
       return value1-value2;
    }
5.操作方法
    //concat()接连数组,不影响原数组
    var colors = ["red","green","blue"];
    var colors2 = colors.concat("yellow",["black","brown"]);
    alert(colors); //red,green,blue不改变原数组
    alert(colors2);//red,green,blue,yellow,black,brown

    //slice方法 截取数组,不影响原数组
    var colors = ["red","green","blue","yellow","purple"];
    var colors2 = colors.slice(1);//传入1个参数,截取参数位置到参数末尾
    var colors3 = colors.slice(1,4); //传入两个参数,截取1,3
    alert(colors2); // green,blue,yellow,purple
    alert(colors3); // green,blue,yellow

    //splice()方法 删除/插入/替换
    var colors = ["red","green","blue"];
    //(1) 传入2个参数: 要删除的第一项的位置,要删除的项数,返回删除的数组
    var removed = colors.splice(0,1); 
    alert(colors);// green,blue;
    alert(removed); // red
    //(2) 传入3个或以上参数:起始位置,0(要删除的项数),要插入的项
    removed = colors.splice(1,0,"yellow","orange");
    alert(colors); // green,yellow,orange,blue
    alert(removed); // [ ]
    //(3) 传入3个或以上参数:起始位置,1(要删除的项数),要插入的项
    removed = colors.splice(1,1,"red","purple");
    alert(colors);//green,red,purple,orange,blue。
6. 位置方法
   // indexOf()和lastIndexOf()。接受两个参数:要查找的项和起始位置(可选)。
    var numbers = [1,2,3,4,5,4,3,2,1];
    alert(numbers.indexOf(4)); //3  返回索引位置
    alert(numbers.indexOf(4,4));//5
7.迭代方法
    // 每个方法都接收两个参数:要在每一项运行的函数和作用域对象
    // (1) every() 对数组中的每一项运行给定函数,如果函数每一项都返回true,则返回true.
    var numbers = [1,2,3,4,5,4,3,2,1];
    var erveryResult = numers.every(function(item,index,array){
      return(item >2)
    })
    alert(everyResult);  //false
    //(2)some() 对数组中的每一项运行给定函数,如果任一项返回true,则返回true
    var someResult = numers.some(function(item,index,array){
      return(item >2)
    })
    alert(someResult);  //true
    //(3) filter()对数组中的每一项运行给定函数,如果返回true,则返回该项
    var filterResult = numbers.filter(function(item,index,array){
      return (item > 2)
      })
    alert(filterResult);//[3,4,5,4,3]
    //(4) map()对数组中的每一项运行给定函数,返回数组计算后的每一项。
    var mapResult = numbers.map(function(item,index,array){
      return item * 2;
    })
    alert(mapResult);//[2,4,6,8,10,8,6,4,2];
    //(5) forEach()对数组中的每一项运行给定函数,没有返回值
    numbers.forEach(function(item,index,array){
      //执行某些操作
    })
8. 归并方法
    //reduce() 和 reduceRight(),接收两个参数,在每一项上调用的函数和作为初始值和计算函数。
   //第一次迭代发生在数组的第二项上,因此prev为数组第一项,cur为数组第二项。每次函数返回值再作为prev传给下一次迭代
    var values = [1,2,3,4,5];
    var sum = values.reduce(function(prev,cur,index,array){
    return prev + cur;
    })
    alert(sum); //15

三、Date类型

创建一个日期对象:

1. 使用构造函数
   var now = new Date();//不传入参数会自动创建当前日期对象,表示GMT+0800 中国标准时间。
   var now1 = new Date("2019/12/30"); //传入字符串参数格式年/月/日,自动调用Date.parse();
   var now2 = new Date("Dec 30,2019");//  格式英文 月 日,年
   var now3 = new Date("2019-12-30T13:34:00");// 格式YYYY-MM-DDTHH:mm:ss:sssZ
   var now4 = new Date(2019,12,30,13,34,0);//依次传入参数,注意月份是基于0开始,调用Date.UTC();
2. Date.now()方法
   var time = Date.now(); //返回调用这个方法时的毫秒数。1577684655442

3.1 相关方法

var now4 = new Date(2019,12,30,13,34,0);
1. 继承的方法
   now4.toString(); //Thu Jan 30 2020 13:34:00GMT+0800 (中国标准时间)
   now4.toLocaleString();//2020/1/30 下午1:34:00
   now4.valueOf(); //1580362440000
2.日期格式化方法
   now4.toDateString();  //Thu Jan 30 2020
   now4.toLocaleDateString();//2020/1/30
   now4.toTimeString();//13:34:00 GMT+0800 (中国标准时间)
   now4.toLocaleTimeString()   //下午1:34:00
3. 日期/时间组件方法
   now4.getTime();  //1580362440000
   now4.getFullYear(); //2020
   now4.getMonth(); //0
   now4.getDate();//30 返回几号
   now4.getDay();//4 返回星期几
   now4.getHours();//13
   now4.getMinutes();//34
   now4.getSeconds();//0

四、RegExp类型

可通过以下语法创建正则表达式:

var expression = / pattern / flags
//匹配第一个" [bc]at",不区分大小写
var pattern = /\[bc\]at/i;

pattern部分可以使任何简单或复杂的正则表达式。每个正则表达式都可带有一个或多个flags。

  • g: 表示全局模式,即模式将被用于所有字符串,而非在发现第一个匹配项时立即停止。
  • i:表示不区分大小写模式
  • m:表示多行文本模式

可使用构造函数创建正则表达式:

var pattern = new RegExp(pattern,flags);
//匹配第一个" [bc]at",不区分大小写
var pattern = new RegExp("[bc]at","i");

4.1 实例属性

  • global : 布尔值,表示是否设置了g标志
  • ignoreCase: 布尔值,表示是否设置了i标志
  • lastIndex: 整数,表示开始搜索下一匹配项的字符位置。
  • multiline: 布尔值,表示是否设置了m标志。
  • source: 正则表达式的字符串表示。

4.2 实例方法

1.exec()方法,专门为捕获组设计的
var text = "mom and dad and baby";
var pattern = /mom( and dad( and baby)?)?/gi;
var matches = pattern.exec(text);
alert(matches.index); //0
alert(matches.input);//"mom and dad and baby"
alert(matches[0]);//"mom and dad and baby"
alert(matches[1]);//"and dad and baby";
alert(matches[2]);//"and baby";
2.test()方法,检查目标字符串与某个模式是否匹配
var text = "000-00-0000";
var pattern = /\d{3}-\d{2}-\d{4}/;
if(pattern.test(text)){
    alert("The pattern is matched");  //返回true
}

4.3 构造函数属性

  • input, 最近一次要匹配的字符串
  • lastMatch, 最近一次的匹配项
  • lastParen, 最近一次的捕获组
  • leftContext, input字符串lastMatch之前的文本
  • multiline, 布尔值表示所有表达式是否都使用多行模式
  • rightContext, input字符串lastMatch之后的文本。
var text = "this has been a short summer";
var partten = /(.)hort/g;
if(partten.test(text)){
    alert(RegExp.input);  //this has been a short summer;
    alert(RegExp.leftContext);//this has been a 
    alert(RegExp.rightContext);//summer;
    alert(RegExp.lastMatch); //short;
    alert(RegExp.lastParen); //s;
    alert(RegExp.multiline); //false;
}

五、Function类型

函数实际上是对象,每个函数都是Function类型的实例,创建函数:

//函数声明语法
function sum (num1, num2){
    return num1+num2;
}
// 函数表达式定义函数
var sum = function(num1, num2){
    return num1+num2;
}

var anotherSum = sum;

"函数是对象,函数名是指针"。可以将变量sum赋值给anotherSum。这时候调用anotherSum也可以返回结果。

JS解析器会把函数声明定义的函数提升到函数顶部,可以在任意位置处调用。对于函数表达式,必须等解析器执行到代码行,才能在之后调用。

5.1 没有重载

将函数名想象为指针,也有助于理解ECMAScript中没有函数重载的概念。

function addSomeNumber(num){
    return num + 100;
}
function addSomeNumber(num){
    return num + 200;
}
var result = addSomeNumber(100); //300

上述例子中声明了两个同名函数,结果为后面的例子覆盖了前面的函数。

5.2 作为值的函数

由于函数名本身是变量,所以函数可以作为值来使用。也就是说,函数不仅可以作为参数传递给另一个函数,也可以作为另一个函数的返回。

//作为参数传递
function callSomeFunction(someFunction, someArgument){
//作为结果返回
    return someFunction(someArgument);
}
function add10(num){
    return num + 10;
}

var result = callSomeFunction(add10,10);

5.3 函数内部属性

在函数内部,有两个特殊的对象:arguments和this.还有一个caller属性。

(1) arguments

arguments是一个类数组对象,包含着传入函数中的所有参数。该对象还有一个callee的属性,它是一个指针,指向拥有arguments对象的函数。

//通过递归算法定义阶乘
function factorial (num) {
    if(num <= 1) {
        return 1;
    } else {
        return num*factorial (num-1);
    }
}

//通过callee属性松散耦合
function factorial (num) {
    if(num <= 1) {
        return 1;
    } else {
        return num * arguments.callee(num-1);
    }
}
(2) this对象

this对象引用的是函数执行的环境对象。在全局作用域中调用,this引用的是全局对象window。

(3) caller属性

这个属性保存着调用当前函数的引用。

function outer () {
    inner();
}
function inner () {
    alert(inner.caller);//会显示outer函数的源代码
}
5.4 函数属性和方法

函数是对象,因此函数也有属性和方法。每个函数都有两个属性:length和prototype ;以及三个方法:apply()、call()和bind()。

  • length属性表示函数希望接收的命名参数的个数
  • prototype是最ECMAScript中耐人寻味的一个属性,所有的属性方法都保存在该属性名下。只不过通过各自的实例访问。
  • apply()和call()的作用相同,都是在改变函数的作用域,区别仅在于接收参数的方式不同。
    -bind()方法也可以改变函数的作用域,但是该方法会先创建一个函数实例,该实例的this为传给bind的参数。
5.5 基本包装类型

ECMAScript提供了3个特殊的引用类型,Boolean,Number和String。它们具有引用类型的属性和方法,同时具有各自的基本类型特殊的行为,以便于操作。

(1) Boolean类型

Boolean对象会将所有表达式转换为true。

//创建Boolean类型
var falseObject = new Boolean(false);
var result = falseObject && true;
alert(result); //true

var falseValue = false;
var result = falseValue && true;
alert(result);//false
(2) Number类型

Number类型重写了valueOf(),返回数值;重写了toLocalString()和toString(),返回字符串形式的数值。
除了继承的方法外,还提供了一些数值格式化的方法。

1.toFixed()方法,按照指定的小数位数返回数值的字符串。
var num = 10;
alert(num.toFixed(2)); //"10.00"
var num = 10.005;
alert(num.toFixed(2));"10.01"; //自动舍入
2.toExponential()方法,按照指定位数返回指数表示法的字符串。
var num = 10;
alert(num.toExponential(1));//"1.0e+1"
3.toPrecision()方法,根据位数返回合适的格式。
var num = 99;
alert(num.toPrecision(1));//"1e+2"
alert(num.toPrecision(2));//"99"
alert(num.toPrecision(3));//"99.0"
(3) Sting类型

String类型继承了valueOf(),toString()和toLocalString()方法,都返回对象所表示的基本字符串值。String类型的每个实例都有一个length属性。除此之外,String类型也提供了许多方法辅助字符串的解析与操作。

1.字符方法,访问特定位置的字符
//charAt()和charCodeAt()
var stringValue = "hello world";
alert(stringValue.charAt(1));  //"e",返回字符
alert(stringValue.charCodeAt(1));// 101,返回e的字符编码

2.字符串操作方法
//concat(),slice(),substr(),substring()
var stringValue = "hello ";
//可以接收任意多个参数,拼接字符串,等于 + 
var result = stringValue.concat("world","!");
alert(result); // "hello world!"
alert(stringValue);//"hello ",不改变原始变量

var stringValue = "hello world";
alert(stringValue.slice(3,7));//"lo w", 返回第一个参数,第二个参数前一个位置的字符串。
alert(stringValue.substring(3,7));//"lo ow",返回第一个参数,第二个参数前一个位置的字符串
alert(stringValue.substr(3,7));//"lo worl",第一个参数为起始位置,第二个参数为返回的字符串个数

3.字符串位置方法
//indexOf(),lastIndexOf()
var stringValue = "hello World";
alert(stringValue.indexOf("o")); //4,从头检索
alert(stringValue.lastIndexOf("o"));//7,从末尾检索
alert(stringValue.indexOf("o",6)); //7,第二个参数为检索的起始位置

4.trim()方法
//删除前缀和后缀所有空格,不改变原始变量
var stringValue = "    hello world   ";
var trimmedStringValue = stringValue.trim();
alert(trimmedStringValue);//"hello world"

5.字符串大小写转换方法
toLowerCase();toUpperCase();toLocalLowerCase();
toLocalUpperCase()

6.字符串的模式匹配方法
//match(),本质上与RegExp的exec()方法相同,只能传入一个正则表达式或一个RegExp对象。
var text = "cat, bat, sat, fat";
var pattern = /.at/;
var matches= text.match(pattern);
alert(matches.index); //0;
alert(matched[0]);//cat
//search(),参数同match(),返回字符串中第一个匹配项的索引,否则返回-1
var text = "cat, bat, sat, fat";
var pattern = /at/;
alert(text.search(pattern)) ; //1
//replace(),该方法接收两个参数,第一个可以是一个RegExp对象,或字符串。
//第二个可以是一个字符串或一个函数。
var text = "cat, bat, sat, fat";
result=text.replace(/at/g,"ond");
alert(result); //"cond,bond,sond,fond"
//split(),接受指定的分隔符,将字符串分割为多个子字符串。第二个参数为限制数组的大小
var colorText = "red,blue,green,yellow";
var colors1 = colorText.split(","); //["red","blue","green,"yellow"]
var colors2 = colorText.split(",",2);//["red","blue"]
7.localeCompare()方法
//比较字符串的字母表顺序
var stringValue = "yellow";
alert(stringValue.localeCompare("brick")); //1
alert(stringValue.localeCompare("yellow"));//0
alert(stringValue.localeCompare("zoo"));-1
8.fromCharCode()方法
//接收多个字符编码,转换为一个字符串
alert(String.fromCharCode(104,101,108,108,111));//hello

5.6 单体内置对象

内置对象是由ECMAScript提供的,不依赖与宿主环境的对象,也就是说,在Js执行之前就已经存在了。之前已经介绍过的如Object, Array, String,除此之外还有Global和Math

(1) Global对象

Global对象是最特别的对象,因为它无法访问,看起来就像不存在。但所有在全局作用域中定义的属性和函数都是它的属性和方法。诸如isNaN(), isFinite(), parseInt()以及parseFloat()。除此之外还有一些方法。
URL编码方法

//encodeURI()主要对整个URL编码,
//decodeURI()
//encodeURIComponent()主要对url中的某一段进行编码
//decodeURIComponent();
var url = "http://www.wrox.com/illegal value.htm#start";

//http://www.wrox.com/illegal%20value.htm#start"
alert(encodeURI(url));//只替换了空格
//http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.htm%23start
alert(encodeURIComponent(url));//替换了所以非字母数字字符。
eval()方法

eval()方法大概是是ECMAScript语言中最强大的一个方法。它接受一段js字符串,执行代码后将结果插入到原位置。被执行的代码与该次调用的执行环境一样。

eval("function sayHi(){alert('hi')}");
sayHi();  //hi

在eval()中定义的任何函数或变量都不会被提升。
在严格模式下,外部访问不到eval()中定义的任何变量或函数。
window对象
ECMAScript虽然不能直接访问Global对象,但web浏览器是将这个全局对象作为window对象的一部分实现的。因此全局作用域中的所以变量和函数都可以通过window对象的属性来访问。

var color = "red";
function sayColor(){
    alert(window.color);
}
window.sayColor(); //"red"
(2) Math对象

Math对象是ECMAScript为保存数学公式和信息提供的一个公共对象。

Math对象的属性

  • Math.E------自然对数的底数,即常量e的值
  • Math.LN10-----10的自然对数
  • Math.LN2------2的自然对数
  • Math.LOG2E------以2为底e的的对数
  • Math.LOG10E------以10为底e的对数
  • Math.PI------π的值
  • Math.SQRT1_2----1/2的平方根
  • Math.SQRT2------2的平方根

min()和max()

var max = Math.max(13,54,45,3);//54
var min = Math.min(13,54,45,3);//3

舍入方法

  • Math.ceil() 执行向上舍入
  • Math.floor()执行向下舍入
  • Math.round() 执行四舍五入
Math.ceil(25.1) //26
Math.floor(25.9) //25
Math.round(25.9)//26

random()方法
Math.random()随机返回一个大于等于0,小于1的随机数。
想在随机范围内选择一个值
值 = Math.floor(Math.random() * 可能的总数 + 第一个可能的值)

function  selectFrom(lowerValue, upperValue){
  var choices  = upperValue - lowerValue +1;
  return Math.floor(Math.random() * choices + lowerValue);
}
var num = selectFrom(2,9)

其他方法
如Math.abs(),Math.sqrt()等完成复杂计算的方法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  •   引用类型的值(对象)是引用类型的一个实例。   在 ECMAscript 中,引用类型是一种数据结构,用于将数...
    霜天晓阅读 1,052评论 0 1
  • 本章内容 使用对象 创建并操作数组 理解基本的 JavaScript 类型 使用基本类型和基本包装类型 引用类型的...
    闷油瓶小张阅读 681评论 0 0
  • 第五章:引用类型 本章内容: 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型和基本包装...
    穿牛仔裤的蚊子阅读 315评论 0 1
  • 引用类型的值时引用类型的一个实例。在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起。有...
    cooore阅读 283评论 0 1
  • 没有开始的停止 如同烟花般绚烂 曾经 你不知道你在我心里的样子 后来 你想知道你在我口里的位置 然后 你轻了我 我...
    不再单调阅读 127评论 0 0