原生Javascript(语言基础和流程控制语句)—1_Sander_2020的博客—CSDN博客

   日期:2020-11-01     浏览:85    评论:0    
核心提示:原生Javascript1本篇文章主要介绍js的语言基础及流程控制语句语言基础Javascript历史布兰登·艾奇(Brendan Eich,1961年~),1995年在网景公司,发明的JavaScript。JavaScript诞生于1995年。它当时的目的是为了验证表单输入的验证。一开始JavaScript叫做LiveScript,但是由于当时Java这个语言特别火,所以搭上Java的顺风车,就改名为JavaScript。同时期还有其他的网页语言,比如VBScript、JScript等等,但

原生Javascript1

本篇文章主要介绍js的语言基础及流程控制语句

语言基础

Javascript历史

布兰登·艾奇(Brendan Eich,1961年~),1995年在网景公司,发明的JavaScript。
JavaScript诞生于1995年。它当时的目的是为了验证表单输入的验证。一开始JavaScript叫做LiveScript,但是由于当时Java这个语言特别火,所以搭上Java的顺风车,就改名为JavaScript。
同时期还有其他的网页语言,比如VBScript、JScript等等,但是后来都被JavaScript打败,所以现在的浏览器中,只运行一种脚本语言就是JavaScript。
经过许多年的发展,JavaScript从一个简单的输入验证成为一门强大的编程语言
2003年之前,JavaScript被认为“牛皮鲜”,用来制作页面上的广告,弹窗、漂浮的广告。什么东西让人烦,什么东西就是JavaScript开发的。所以浏览器就推出了屏蔽广告功能。
2007年乔布斯发布了iPhone,这一年开始,用户就多了上网的途径,就是用移动设备上网。JavaScript在移动页面中,也是不可或缺的。并且这一年,互联网开始标准化,按照W3C规则三层分离,人们越来越重视JavaScript了。
今天,JavaScript工程师是能够和iOS、Android工程师比肩,毫不逊色的。
JavaScript是一种具有面向对象能力的、解释型的程序设计语言。更具体一点,它是基于对象和事件驱动并具有相对安全性的客户端脚本语言。

Javascript的组成

  • 核心(ECMAScript)
  • 文档对象模型(DOM)
    Document Object Model
  • 浏览器对象模型(BOM)
    Browser Object Model

Javascript引入及写法

  • 内部书写
    在html文件中直接进行代码的书写
1.位于head部分的脚本
当把脚本放置在head部分后,必须等到全部的JavaScript代码都被下载,解析和执行完成后,才能呈现页面的内容(浏览器在遇到body标签才开始呈现内容)。

2.位于body部分的脚本
为了避免上述问题,现代web应用程序一般b把javascript引用放在body中,放在页面内容后面,这样,在解析包含的JavaScript代码之前,页面内容也将完整呈现
  • 外部引入
    所有的<script> 元素都放在页面的<head>元素中
<link rel="stylesheet" href="./css/a.css">
所有herf指向文件中所用到的地址都是指向这个对于链接文件的,而不是指向当前网页的
<script src="./js/a.js" async> </script>
<script src="./js/b.js" defer> </script>
所有src指向的文件中所用到的地址都是指向相对当前页面的
async 异步加载,不需要等待a.js加载完就可以继续向后同时加载b.js   
所有的js被加载后立即执行
defer  当所有内容加载完成后执行当前js内容(即在形成DOM渲染树之后开始执行)

同步与异步
   script和link加载外部文件时是一个加载完成后再继续加载下一个,这种叫做同步
   img标签在加载过程中,是并行同时加载,这就叫做异步

树
DOM树就是html标签树
css根据DOM树结构形成了CSS树
DOM树与CSS树合并形成DOM渲染树
在加载图片
defer在执行
  • 直接写在标签
    <p onlick="alert(“你好”);">点击我</p>
<a href="javascript:void(0)">超链接</a>
<a href="javascript:alert(123)">超链接</a> 
阻止页面的刷新,历史生成和地址的变化

注释

  • 单行注释
//注释内容  不可换行
//快捷键   ctrl + /
  • 块级/多行注释


调试

这里主要介绍一下Console控制台  
Console控制台: 
用于打印和输出相关的命令信息,其实console控制台除了我们熟知的报错,打印console.log信息外,还有很多相关的功能,
比如打断点正好执行到获取的数据上,由于数据都是层层嵌套的对象,这个时候查看里面的key/value不是很方便,既可用这个指令开查看,obj的json string格式的key/value,即可一目了然

常用方法

  • console.log()
输出日志文件 控制台输出
  • alert()
    警告消息框
弹出框or对话框    
只有一个确认按钮
  • confirm()
    确认消息框
确认取消对话框 
可以得到确认或者取消的结果true , false
  • prompt()
    提示消息框 专门用来给用户提供输入窗口的
prompt("你今年多大了?""20岁");
//弹出输入框,允许用户输入,并且返回结果
//prompt(输入框的提示内容,默认结果)
  • document
//document.body 获取body标签
document.body.innerHTML="你好";   //将body里面的内容换成你好字符

//doucument 文档
document.write("你好");  //在文档中写入你好

//write是只针对document的,innerHTML是针对所有标签的
//根据id在文档中获取div1的元素
var div1=documnent.getElementById("div1");
div1.innerHTML="你好";
//点击事件
div1.onclick=function(){ 
	div1.innerHTML="不错,你点中了";
}
//给div1增加行内样式
div1.style.width="50px";
div1.style.height="50px";
div1.style.backgroundColor="red";

语句块

语句块(blocks)是由一些相互有关联的语句构成的语句集合
通常来说,用{}括起来的一组JavaScript语句称为语句块(blocks)
在语句块里面的每句语句以分号(;)表示结束,但是语句块本身不用分号
语句块(blocks)通常用于函数和条件语句中。

变量

  • JavaScript关键字
    可用于表示控制语句的开始或结束,或者用于执行特定操作等
    按照规则,关键字也是语言保留的,不能用作标识符!!!
break do instanceof typeof case else new var catch finally  return void continue for
switch while function this with default if throw delete in try
  • JavaScript保留字
    保留字是指有可能以后被当作关键字来使用,不能用作标识符!
abstract int short boolean export interface static byte extends ling super char final native class float 
throws const goto private double importn public
  • JavaScript变量
    变量 可变的量 var定义

所有变量名称必须使用字母或下划线开头,后面的内容包含有字母,数字和下划线
变量名要表意且不能命名中文
一个变量不能由数字开头
在JS中严格区分大小写 =>num Num NUm NUM 这是四个变量
不能使用关键字或保留字
不能使用window属性命名

var status = 10;

_width 临时变量,参数,类中使用

补充:

//ESS5中
a = 3;
console.log(a);
//如果没有定义直接赋值,该变量就是这个值
console.log(a);
//如果没有定义和赋值,直接调用就会报错
var a;
console.log(a);
//如果没有赋值,直接调用就是undefined
1.一次声明一个变量。例句如下:
var a;
2.同时声明多个变量,变量之间用都逗号相隔。例句如下:
var a,b,c;
3.声明一个变量时,同时赋予变量初始值。例句如下:
var a = 2;
4.同时声明多个变量,并且赋予这些变量初始值,变量之间用逗号相隔。例句如下:
var a = 2,b = 5;
  • JavaScript常量
    const
const RECT_WIDTH=200;
常量设置值后,不能重新赋值
要求const定义常量时,必须全部字母大写,单词之间使用下划线区分

数据类型

//不规定变量的类型,可以任意修改类型的语言叫做弱类型
var a = 3;
a = "abc";
a = ture;
  • 数字
Number 数值
	+一切十进制表示的数字  var a = 3;
	+一切浮点数(小数)  var a = 3.33;
	+其他进制表示的数字
		=>十六进制,以0x开头  var a = 0xff;
		=>八进制,以0开头,不允许出现大于7的值,ES6取消使用
		=>二进制,以0b开头
	+科学计数法
		=>比较大的数字使用科学计数法表示
		=>var a = 2e+5;        2*10的5次方
		=> var a = 2e-5;        2*10的-5次方
	+NaN
		=>Not a Numnber
	//补充:在控制台输出任何其他进制数字的时候,都会自动转换成十进制	
  • 字符串
String   字符串
	+在JS里面一切使用引号(双引号,单引号,反引号)包裹的内容都是字符串
	+表示一段文本内容,是一个字符一个字符连接起来的内容
		=>'hello'
		=>"hello"		
		=>`hello`
	+当你在字符串里面只写数字的时候,也不是数值类型
		=>也是字符串类型
	+在字符串里面,空格是占位的
  • 布尔
var a = true;
var b = false;
Boolean   布尔
	+在JS里面,布尔只有两个值
		=>true 表示真,在计算机存储的时候就是1		
		=>false表示假,在计算机存储的时候就是0
	+在运算过程中有什么意义
		=>主要用来做判断		
		=>例子
			->  a>b  如果得到ture,表示这个表达式是真的
			->  a<b 如果得到false,表示这个表达式是假的  
  • Null
Null  这里有一个值 有一个空值
  • Undefined
这里本该有有一个值,但是没有 就是undefined
未定义  定义了没有赋值
var a = undefined;
var b;
console.log(a,b);   //undefined undefined
  • 对象
Object   对象
var a = { a:1,b:2};
  • 补充
检测数据类型
	+在运算过程中,有的数据不是我们自己写的
	+有可能是通过运算得到的,有可能是别人给的
	+为了保证程序的正确运行,我们需要检测数据类型变量
关键字 Typeof
	是用来检测变量的数据类型
	+两种用法
		1.typeof 变量
			=>语法:typeof 要检测的变量
			=>返回值(结果):以字符串的形式给你的变量数据类型
		2.typeof(变量)、
			=>语法:typeof(要检测的变量)
			=>返回值:以字符串的形式给你的变量数据类型
	+两种语法的区别
		1.typeof 只能检测紧跟着的一个变量
		2.typeof()先运算小括号里面的结果,然后使用typeof去检测结果的数据类型
	+typeof的返回值
		=>注意:typeof的返回值是一个字符串
		=>当两个及以上typeof连用的时候,一定得到string
		=>只能准确的检测基本数据类型
			->数值:number
			->字符串:string
			->布尔:boolean
			->undefined:undefined
			->null: object
var n1 = 100;       
var n2 = 200;        
var res2 = typeof n1 + n2;        
console.log(res2);	//number20 
var res3 = typeof(n1 + n2);        
console.log(res3);        //number
var res = typeof typeof n1;        
console.log(res);	//string

console.log(typeof 100);	//number
console.log(typeof '100');	//string
console.log(typeof true);	//boolean
console.log(typeof undefined);	//undefined
console.log(typeof null);	//object

数据类型转换

  • xx转数值型
a = Number(a);   //强制转换
a -= 0;
a = parseFloat(a);  //转换为浮点数
//从头开始 解析到字符串末尾或者解析到一个无效的浮点数值字符为止
a = parseInt(a);//转换为整型数值
//从头开始向后转换遇到字符停止(如果开始是数值就可以转换)
a = parseInt(a,16);//将字符串按照指定的进制类型转换,第二个是进制类型
1.字符型转为数值型
	NaN非数值-->数值类型
	1)字符串只要含有非数字就会转换为NaN,否则转换为数值
	2)空字符串(不包含字符),直接返回0;
2.布尔值
	true 转换为1
	false 转换为0
3.数值直接返回
4.null返回0
5.undefined返回NaN
6.对象Object返回NaN 
  • ----->转字符
a = String(a);	//强制转换
a += "0";

a = a.toString();
a = a.toString(36);//将数值按照指定进制类型转换为字符串,进制2-36
a = a.toFixed();//保留整数部分 四舍五入
a = a.toFixed(2);//保留小数点后2位
a = a.t0Exponential(2);//保留两位小数科学计数法
a = a.toPrecision(2);//保留数值的数量
a = a.toLocaleString();//本地字符串

//布尔值,undefined,null
//转换为字符串toString();
//"true" "false"
//"undefined"
//"null"

//object转换为字符串
var a = { a:1,b:2};
var a = { a:2,c:4};
a = String(a);
console.log(a);//[object,Object]
  • ----->转布尔值
"",0,undefined,null,false,NaN转换后都是false 除此之外都是true
//数组Array
var arr = [];
var b = Boolean(arr);
console.log(b);//true

console.log(![]==[]);//true
//![]-->false;
//[1,2,3]--->"1,2,3"
//[]--->""--->false;

//数组进行比较时,如果遇到布尔值,优先将数组转换为字符串
//遇到!先转换为布尔值,再取反
var a ="";
if(!a){ 
	//只有当a为"",0,null,NaN,undefined,false时才进入
}
//遇到!需要将值转换为布尔值取反

var a = 3;
a = Object(a);
console.log(a==3); //true 

运算符

  • 算术运算符
+  加
	——(隐式转换)所有的隐式转换都是使用强制转换方法
	1.只要有一个是字符类型,另一个就会被转换为字符类型,并且首尾相连
	2.若一侧是布尔值,就会根据另一侧转换
	3.如果两侧都没有字符或数值,就会转换为数值运算
	4.一旦遇到对象就会隐式转换为字符类型相加
var a = true;
var b = 1;
console.log(a+b);//2
var b = "1";
console.log(a+b);//true
var b; 
console.log(a+b);//NaN
var b = null;
console.log(a+b);//1
var b = { a:1};//一旦遇到对象都会隐式转换为字符相加
console.log(a+b);//true[object Object]
var b = [];
console.log(a+b);//true
任何- * / %全部遵照数值运算规则,两侧都转换为数值运算
-   减
*   乘
/   除
%  求余数(保留整数)  连续取模是没有意义的
  • 一元运算符
++  累加
   ++a  先进行+1运算 在返回a的值
   a++  先返回a的值 在进行+1运算
--  递减
   规则同累加相同
不论是++a(a++)还是--a(a--)全部都是数值运算
  • 关系运算符
    关系运算符结果返回一个布尔值
< > <= >= ==  === != !===

==(比较运算)

//1)字符串与布尔值比较,数值与布尔值比较都是布尔值转换成相应的字符串或数值,在进行比较
//2)undefined,null,对象与布尔值进行比较时 布尔值无法转换为相应的类型 所以不同 即false
//3)数组[]与布尔值比较 优先将数组转换为字符串 在比较
//![]优先将[]看作对象,所以[]就是true,取反后变成false


console.log("3" == 3);//true
console.log("3" == true);//false
console.log("" == true);//false 
console.log(2 == true);//false 
console.log(undefined == false);//false
console.log(null == false);//false
console.log({ a:1} ==true);//false
console.log([] == false);//true
console.log(![] == false); //true
console.log(![] == []);//false

console.log("" == 0);//true
console.log(undefined == undefined);//false
console.log("null" == null);//false
console.log("" == null );//false
console.log("" == undefined);//false
console.log("[object Object]" == { a:1});//true
console.log("a" == ["a"]);//数组转换为字符串 true
//当字符类型和引用类型比较时,优先将引用类型执行toString()转换为字符类型

console.log(undefined == null);//true
console.log(NaN ==NaN);//false
console.log(isNaN(Number("a")));//true 判断是不是NaN

=== 精确比较运算符

//a 为 "" 0 null undefined false NaN
if(!a){ 

}

//a 为 0 "" false
if(a == false){ 

}

//a 为 null undefined
if(a == undefined){ 

}

//a 为 undefined
if(a === undefined){ 

}


//Object.is 类似于===
	console.log(Object.is("3",3));//false
	console.log(Object.is(3,3));//true
	console.log(Object.is(NaN,NaN));//true
  • 逻辑运算符
! 逻辑非
&& 逻辑与
	true && true =true  返回最后一个值
        true && false =false 返回false对应的值
        false && true =false  返回false对应的值
        false && false =false 返回第一次遇见false对应的值
|| 逻辑或
	true || true =true; 第一个遇到true的对于值
        true || false =true; 返回true对应的值
        false || true =true; 返回true对应的值
        false || false =false 返回最后一个false对应的值
  • 赋值运算符
var a = 1;
//看到一个=号,就是表示将符号右侧的结果赋值给等号左侧,并且返回一个值
乘法/赋值(*=)
除法/赋值(/=)
取模/赋值(%=)
加法/赋值(+=)
减法/赋值(-=)
左移/赋值(<<=)
有符号右移赋值(>>=)
无符号右移/赋值(>>>=)
  • 位运算符
~ 位非运算符
   ~-1 == 0;//实质上是对其加一取负
var str = "abcdef";
console.log(str.indexOf("c"));//3

if(~str.indexOf("z"));{ 
	console.log("找到了z");//因为str字符串内不含z 所以返回-1 进行位非运算后得0 即不可进入
}
//indexOf()方法 可返回某个指定的字符串值在字符中首次出现的位置
// 如果要检索的字符串值没有出现,则该方法返回-1 
& 位与运算符
	进行二进制计算 两个数位都是1 才为1
	1&1=1
	1&0=0
	0&1=0
	0&0=0
| 位或运算符
	二进制运算 两个数位都是0 才为0 都为1 仍取0
	1|0=1
	0|1=1
	0|0=0
	1|1=1
^ 位异或运算符
	二进运算 当只有一个数位存放的是1时,才返回1  可用于加密解密
	1^1=0
        0^0=0
        1^0=1
        0^1=1
var pass = 7856;
var key = 2534;
var value = pass^ key;
console.log(pass ^ key);// 5974 加密 
console.log(value ^ key); //解密
<< 左移位运算符
	左移位运算由两个小于号表示(<<)
	它把数字中的所有数位向左移动指定的数量。
	例如,把数字2(等于二进制中的10)左移5位,结果为64(等于二进制中的1000000)
>>右移位运算符
	有符号右移运算符由两个大于号表示(>>)。
	它把32位数字中的所有数位整体右移,同时保留该数的符号(正号或负号)。
	有符号右移运算符恰好与左移运算符相反
	例如,把64右移5位,将变为2
  • 条件运算符(三目运算符)
    variable = boolean_expression?true_value:false_value;
var b = a>5 ? 2 : 1;
//前面的条件判断 如果返回true 就将第一个结果返回前面 否则将第二个结果返回前面

//当三元运算符遇到返回布尔值时,直接返回条件即可
//三目优先级高于赋值运算 += -= ...

运算符优先级

补充:instanceof运算符用来检测constructor.prototype是否存在与参数object的原型链上

表达式

javascript表达式(expressions)相当于JavaScript语言中的一个短语,包括变量、字面量和运算符,
这个短语可以判断或者产生一个值,这个值可以是任何一种合法的JavaScript类型-数字、字符串、对象等

最简单的表达式是字符:
表达式示例:
3.9//数字字符
“Hello!”  //字符串字符
false  //布尔字符
null  //null值字符
Age + 3
以下是比较复杂的表达式示例:
3 * (4/5)+ 6
Math.PI * radins * radius + 12

流程控制语句

条件语句
各类条件语句的顺序问题

  • if
//if就是”如果“的意思
if(条件表达式){ 
	语句1
}
语句2
表达式为真时,执行if控制的语句

//表达式如果不是布尔值就会隐式转换为布尔值
if(表达式){ 
   //满足条件后执行的语句块
}

if(a>5){ 

}
//如果条件后执行语句就一句,可以省略{}
if(a>5){ 
   b =6;
}

if(a>5)b =6;
  • if else
if(条件表达式){ 
	语句1
}else{ 
	语句2
}
语句3
//if语句也称为“选择语句”,“条件判断语句”

//意味着条件后的一句表达式 为满足该条件执行 其他不执行
if(a>5)
b = 6;
a++;

if(a>5){ 
	b = 6;
}
a++;

if(a>5){ 
	b = 10;
}else{ 
	b = 0;
}

if(a>5) b = 10;
else b =0;
  • if else if
if...else if...else语句:使用该语句来选择多个代码块之一来执行
if(条件表达式1){
	语句1
}else if(条件表达式2){
	语句块2
}else if(条件表达式3){
	语句块3
}
...
else{
	语句块4
}
语句块5
//用户输入成绩,如果成绩大于等于85,那么提示优秀;
//否则如果成绩大于等于70,提示良好;
//否则如果成绩60~69,提示及格;
//否则,不及格
var value = 79if(value >= 85){ 
	console.log("优秀");
}else if(value >= 70){ 
	console.log("良好");
}else if(value >60 && value <69){ 
	console.log("及格");
}else{ 
	console.log("不及格");
}

  • if语句的嵌套
if(条件){ 
	if(条件){ 
	}
}
  • switch case
switch(n){ 
case 1:
	代码块1
	breakcase 2:
	代码块2
	breakdefault;
	n与case 1case 2 不同时执行的代码
}
//工作原理:首先设置表达式 n(通常是一个变量)
//表达式的值与结构中的每个case的值作比较
//存在则执行与该case关联的代码块/使用break来阻止代码自动地向下一个case运行。
switch()中的表达式不一定返回布尔值 所以不会做隐私转换
表达式与值1,值2,值3是否相等(绝对相等)、

break  跳出该条件语句
default  默认情况,如果以上值都不相同,则执行默认内容语句块·

注意:
case值:这里是:指要执行的语句块内容,并没有{},:代表开始

结束时需要break,如果没有break就会向下执行,直到遇到break或者全部执行完毕

如果没有break,完成下一个内容穿越时,不需要判断是否与该值相同,这种情况叫穿越

switch中表达式可以是任何内容,重要的是表达式返回的值

switch是一个判断是否绝对相等的条件,因此不能做其他类型的判断

default一般写在switch最后,并且一般不加break

switch可以在内容与if等语句混合使用

switch一般用于状态机


重点在于遇到冲突时 分区块处理
1.直穿 等同于逻辑或
2.穿越中会经历代码
少代码的目的,要注意代码经过是必须执行的,所以要多考虑执行的顺序和必要性

switch中的表达式如果描述的是true就可以完成多元条件的判断 
但是因为switch就是一个绝对相等,
所以不支持隐式转换,判断是后面的内容需要主动转换为布尔类型

循环语句

  • while
//语法格式
while(判断语句){ 
	循环体;
}
语句2
//while语句是一种先判断,后运行的循环语句。
//也就是说,必须满足条件了之后,方可运行循环体。

var i = 1;	//赋初值语句
while(i <= 10)	//控制循环条件
{ 
	document.write("hello world!</br>");//循环体
	i++//循环增量
}
关于while循环:
while循环先判断表达式,后执行循环体。循环体有可能一次也不执行
循环体应包含有使循环趋向结束的语句:
下列情况,退出while循环
条件表达式不成立
循环体内遇break

死循环出现的两种情况,
1.条件不对
2.没有向条件运行的过程

while循环三个重要部分 
1.起始值
2.条件
3.向条件外运行的过程

双重循环时,内部循环需要重置
//生成一个列表
var str = "<ul>";
var i = 1;
while(i <= 10){ 
	str += "<li>"+i+"</li>";
	i++;
}
str +="</ul>";
document,body.innerHTML= str;
  • do while
赋初值;
do{ 
	循环体;
	循环增量;
}while(循环条件);

//do...while语句是一种先运行,后判断的循环语句。
//也就是说,不管条件是否满足,至少先运行一次循环体
var i = 1;
do{ 
	docunment.write("hello world!</br>");
	i++;
}while(i <= 10);

//循环三要素:
//表达式1,判断表达式2,表达式3
//循环变量赋初值,循环判定条件,循环增量)

//循环体:需要重复执行的语句。
for(表达式1;判断表达式2;表达式3{ 
	循环体;
}
语句5
  • for
for(var i = 0; i<10;i++){ 
	console.log(i)
}
  • 循环嵌套
for (var i = 0;i < 10; i++){ 
	for(var j = 0;j< 10; j++){ 
		console.log(i,j,i*j);
	}
}

break

//break语句会立即退出循环,
//强制继续执行循环后面的语句,结束本层循环。
//一般出现在循环语句和switch中

//满足条件跳出循环,不会在进入循环
var i = 0;j = 0;
loveYou:while(i<10){ 
	console.log(i+"+++++++++++");
	j= 0;
	while(j<10){ 
		if(i*j===30)break loveYou;
		console.log(i+"----------------------------------");
		j++;
	}
	i++;
}

continue

//continue语句仅用于循环语句。虽然也是立即退出循环,但是退出循环后会从循环的顶部继续执行,结束本次出现循环进行下一次
//出现在循环语句中
for(var box = 1;box <= 10; box++){ 	
	if(box == 5break/如果box是5,就退出当前循环
	documnent.write(box);
	docunment.write("</br>");
}


  1. js解释性语言 ↩︎

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服