JS - 基本语法

JavaScript是一种脚本语言,主要功能是:动态修改html页面内容,包括创建、删除html页面元素、修改html页面元素的内容、外观、位置、大小等。

数据类型和变量

任何语言都离不开数据类型和变量,虽然JavaScript语言是弱类型的语言,但它一样支持变量声明,变量一样存在作用范围,即有局部变量和全局变量之分。

定义变量的方式

因为JavaScript是弱类型的脚本语言,所以使用变量之前,可以无须定义,想使用某个变量直接使用即可,JavaScript支持两种方式来引入变量:

1)隐式定义:直接给变量赋值

2)显示定义:使用var关键字定义变量

隐式定义的方式简单、快捷、需要使用变量时,直接给变量赋值即可。如:

<!DOCTYPE html>
<html>
     <body>
     <script type="text/javascript">
     <!-- 隐式定义变量a -->
     a = "Hello JavaScript"
     <!-- 使用警告框输出a的内容 -->
     alert(a);
     </script>
     </body>
</html>

执行效果


这里涉及到了在html中使用JavaScript,在html页面中嵌入执行JavaScript代码有两种方式

1)使用javascript:前缀构建执行JavaScript代码的URL。

对于这种方式,所有可以设置URL的地方都可以使用这种方式来执行,当用户触发该URL的时候,javascript:之后的JavaScript代码会获得执行。如:

<!DOCTYPE html>
<html>
     <body>
     <a href="javascript:alert('hello JavaScript')">run javaScript</a>
     </body>
</html>

2)使用<script.../>元素来包含JavaScript代码,<script.../>元素既可以作为<head.../>子元素,也可以作为<body.../>子元素,上面的代码我们就是使用了第二种方式。

在实际的开发中,为了让html页面和JavaScript脚本更好的分离,我们可以将JavaScript脚本单独保存为一个*.js文件,在html页面倒入该*.js文件即可。导入的语法方式如下:

<script src="test.js" type="text/javascript">

src指定了JavaScript脚本文件所在的URL。OK,说了这么多,继续我们的定义变量。

显示声明的方式说采用var关键字声明变量,声明时变量可以没有初始值,变量的变量数据类型是不确定的。当第一次给变量赋值时,变量的数据类型菜确定下来,而且使用过程中数据类型也可随意改变。

<script type="text/javascript">
    //显示声明变量a
    var a;
    //给变量a赋值
    a = true;
    //弹出警告框
    alert(a);
</script>

与其它变成语言类似,JavaScript也允许一次定义多个变量,并可以指定初始值,如:

var a, b=0, c;


类型转换

Javascript支持自动类型转换,并且类型转换的功能非常强大。如:

 <script type="text/javascript">
     var a = "3.145"
     var b = a - 2
     var c = a + 2
     alert(b + "\n" + c)
 </script>

执行结果


上面的代码中a是字符串,其值为3.145,让a和数值执行减法,则自动执行算术运算,并将a的类型转换为数值;让a和数值执行加法,则a的值转换为字符串,使用加号相当于给字符串进行拼接操作,这就是自动类型转换。

虽然自动类型转换非常方便,但是持续的可读性非常差,而且有时候我们就是希望让字符串和数值进行加法运算,那么就需要使用强制类型转换了。JavaScript提供了以下几个函数来执行强制类型转换。

toString():将布尔值、数值等转换为字符串

parseInt():将字符串、布尔值等转换为整数

parseFloat():将字符串、布尔值等转换为浮点数

如:我们想要让“3.145”+2执行表达式之后结果为5.145

 <script type="text/javascript">
     var a = "3.145"
     var b = a - 2
     var c = parseFloat(a) + 2
     alert(b + "\n" + c)
 </script>

但是并不是所有的转换都是成功的,对于包含其它字符的字符串,将转换为NaN。对于使用toString()函数将各种类型的值向字符串转换,结果全部是object。


变量

JavaScript是弱类型语言,同一个变量可以一会存储数值,一会存储字符串。变量有个重要的概念:作用范围。根据变量定义的作用范围不同,变量有全局变量和局部变量之分。直接定义的变量是全局变量,全局变量可以被所有的脚本访问,在函数内定义的变量为局部变量,局部变量只能够在函数内有效,如果全局变量和局部变量有相同的变量名,则局部变量将覆盖全局变量。

<!DOCTYPE HTML>
<html>
     <body>
     <script type="text/javascript">
     var test = "全局变量";
     function checkScope() {
         var test = "局部变量";
         alert(test)
    checkScope();
     </script>
     </body>
</html>

上面局部变量test覆盖了全局变量,所以执行方法checkScope()之后,会alert局部变量的值。注意:JavaScript的变量没有块范围,比如:

<script type="text/javascript">
    function test(o) {
       var i = 0; 
       if (typeof o == "object") {
           var j = 5;
           for(var k=0; k < 10; k++) {
               document.write(k);
           }
       }
       alert(k + "\n" + j);
    }
    test(document);
</script>
执行结果


即k的值为10,j的值为5,是不是有点奇怪,k和j怎么会有值?因为JavaScript的变量没有块范围,所以变量i,j,k的作用域是整个函数内而不是在对应的块范围内。也正是因为变量没有块范围,所以有时候会出现一些奇怪的结果,在看个例子:

 <script type="text/javascript">
     //1
     var scope = "全局变量";
     function test() {
        //2
        document.writeln(scope + "<br />");
        //3
        var scope = "局部变量";
        //4
        document.writeln(scope + "<br />");
     }
     test();
</script>

执行结果:

undefined

局部变量

第一次使用scope并没有使用全局变量而是undefined,第二次使用局部变量,因为

1、定义全局变量scope

2、使用scope变量,但并不是全局变量,因为全局变量被test函数中scope被覆盖了,局部变量在整体test函数内部都是有效的,但是此时scope还没有被赋值,所以会输出undefined。

3、定义局部变量并赋值,所以输出局部变量。

注意:定义变量使用var和不使用var是有差异的,如果使用var定义变量,那么程序会强制定义一个新变量。如果没有使用var定义变量,系统会优先在当前上下文中搜索是否存在该变量,只有在该变量不存在的前提下,系统才会重新定义一个新变量。


基本数据类型

JavaScript的基本数据类型有5个:

数值类型:包含整数和浮点数

布尔类型:只有true或false两个值

字符串类型:字符串变量必须使用引号括起来,引号可以是单引号,也可以是双引号

undefined类型:专门用来确定一个已经创建但是没有初值的变量

null类型:用于表达某个变量的值为空

数值类型

JavaScript的数值类型不仅包括所有的整型变量也包括所有的浮点型变量,JavaScript语言中的数值都以IEEE 754-1985双精度浮点数格式保存。JavaScript中的数值类型非常丰富,完全支持科学计数法表示,格式如下:

<num1>E<num2>

这种形式的值为:num1*10的num2次方。E为间隔符号,E不区分大小写.

<script type="text/javascript">
     var a,b;
     a = 5E2;
     b = 2e-2;
     alert(a + "\n" + b);
</script>

执行结果


字符串类型

JavaScript的字符串必须是要引号,此处的引号既可以是单引号,也可以是双引号。例如:

a = 'Hello JavaScript'
b = "Hello JavaScript"

上面a,b两个变量完全相等,JavaScript没有字符类型。JavaScript以String内建类来表示字符串,String类里包含了一系列方法操作字符串:

String():类似于面向对象语言的构造器,使用该方法可以构造一个字符串

charAt():获取字符串特定索引处的字符

charCodeAt():返回字符串中特定索引处的字符所对应的Unicode值

toUpperCase():返回字符串的长度,JavaScript中文字符算一个字符

toLowerCase():将字符串的所有字母转换为大写字母

fromCharCode():静态方法,直接通过String类调用该方法,将一系列Unicode值转换为字符串

indexOf():返回字符串中特定字符串第一次出现的位置

lastIndexOf():返回字符串中特定字符串最后一次出现的位置

substring():返回字符串的某个子字符串

slice():返回字符串的某个子字符串,功能比substring更强大支持负数参数

match():使用正则表达式搜索目标子字符串

search():使用正则表达式是谁目标子字符串

concat():用于将多个字符串拼接成为一个字符串

split():将某个字符串分割成为多个字符串,可以指定分割符号

replace():将字符串中某个子串以特定字符串替代

var a = "abc";
var b = a.length;
var c = String.fromCharCode(97,98,99);

console.log(b + "---" + a.charAt(2) + "---" + a.charCodeAt(2) + "---" + c);

执行如下:

3---c---99---abc

布尔类型

布尔类型的值只有两个:true和false,通常用于逻辑判断。

var numberOne = 200
var numberTwo = 100

if (numberOne > numberTwo) {
    console.log(numberOne);
} else {
    console.log(numberTwo);
}

undefined和null

undefined类型的值只有一个undefined,该值用于表示某个变量不存在,或者没有为其分配值,也用于表示对象的属性不存在。null用于表示该变量的值为空。undefined于null之间的差别在于:undefined表示没有为变量设置值或者不存在而null表示变量有值,只是其值为null。注意:很多时候null和undefined是相等的,即null==undefined,如果要区分,那么需要使用===进行判断。

var x , y = null;
if (x === undefined) {
   console.log("声明变量后默认值为undefined");
}
if (x === null) {
   console.log("声明变量后默认值为null");
}
if (x == y) {
   console.log("x: (undefined) == y: (null)");
}
if (String.xy === undefined) {
   console.log("不存在的属性值默认是undefined");
}
if (x === y) {
   console.log("x等于y");
} else {
   console.log("x不等于y");
}

执行结果:

声明变量后默认值为undefined
x: (undefined) == y: (null)
不存在的属性值默认是undefined

x不等于y


复合类型

复合类型是由多个基本数据类型(也可以包括复合类型)组成的数据体,JavaScript中的复合类型大致有如下3种:

Object:对象

Array:数组

Function:函数

对象

对象是一系列命名变量、函数的集合。其中命名变量的类型既可以是基本数据类型,而可以是复合类型。对象中的命名变量称为属性,而对象中的函数称为方法。对象访问属性和函数的方法都是使用“.”(点)实现。JavaScript是基于对象的脚本语言,它提供了大量的内置对象供用户使用,JavaScript提供了如下常用的内置类。

Array:数组类

Date:日期类

Error:错误类

Function:函数类

Math:数学类,该对象包含相当多的执行数学运算的方法

Number:数值类

Object:对象类

String:字符串类


数组

数组是一系列的变量,于其他强类型语言不同的是,JavaScript中数组元素的类型可以不同。定义应该数组由如下3种方法:

var a = [3, 5, 20]; // 定义的时候初始化数组
var b = []; // 创建一个空数组
var c = new Array(); // 创建一个空数组

简单使用

var a = [3, 5, 20]; // 定义的时候初始化数组
var b = []; // 创建一个空数组
var c = new Array(); // 创建一个空数组

b[0] = "你好";
b[1] = 100;
b[2] = true;

c[3] = true;

console.log(a + "\n" + b + "\n" + c);
执行结果

3,5,20
你好,100,true

,,,true

JavaScript作为动态、弱类型语言,归纳起来,其数组有如下3个特征:

1)JavaScript的数组长度可变,数组长度总等于所有元素索引最大值+1

2)同一个数组中的元素类型可以互不相同

3)访问数组元素时不会产生数组越界,访问并未赋值的数组元素时,该元素的值为undefined。


函数

函数可以包含一段可执行的代码,也可以接收调用者传入参数,正如弱类型语言一样,JavaScript的函数声明中,参数列表不需要数据类型声明,函数的返回值也不需要数据类型声明,函数定义的语法格式如下:

function functionName(param1, param2,...) {
	
}

简单使用

function judgeAge(age) 
{
if (age > 60) 
{
console.log("老人");
} 
else if (age > 40 ) 
{
console.log("中年人");
} 
else if (age > 15) 
{
console.log("年轻人");
} 
else 
{
console.log("儿童");
}
}

judgeAge(26); //年轻人

虽然调用judgeAge(26)程序能够正常运行,但是如果传入的不是数值那么程序就会有问题,所以最好先对参数类型进行判断,判断变量的数据类型可以使用typeof运算符,该函数用于返回变量的数据类型。

function judgeAge(age) 
{
if (typeof age === "number") 
{
if (age > 60) 
{
	console.log("老人");
} 
else if (age > 40 ) 
{
console.log("中年人");
} 
else if (age > 15) 
{
console.log("年轻人");
} 
else 
{
console.log("儿童");
}
} else
{
console.log("must be number")
}
}


运算符

JavaScript提供了相当丰富的运算符,包括了赋值运算符、算术运算符、逻辑运算符、位运算符等。

赋值运算符

赋值运算符用于位变量指定变量值,于Java, C类似,也使用“=”作为赋值运算符,通常使用赋值运算符将一个常量值赋给变量。

var a = "JavaScript"
var pi = 3.14
var visited = true

也可以使用赋值运算符将一个变量的值赋给另一个变量

var b = a;

算术运算符

JavaScript支持所有的基本算数运算符:

1)加法运算符

var a = 5;
var b = 10;

var sum = a + b;

2)减法运算符

var a = 5;
var b = 10;

var sub = b - a;

3)乘法运算符

var a = 5;
var b = 10;

var sub = b * a;

4)除法运算符

var a = 5;
var b = 10;

var sub = b / a;

5)取余运算符

var a = 5;
var b = 10;

var sub = b % a;

6)自加(++)运算符

++既可以出现在操作数的左边也可以出现早操作数的右边,但是效果不一样,跟c语言中的自增效果是一样的

var a = 5;
var b = 10;

var sum = b++ + a;
console.log(sum); // 15

++在前

var a = 5;
var b = 10;

var sum = ++b + a;
console.log(sum); // 16

7)自减(--)

--运算符在前

var a = 5;
var b = 10;

var sum = --b + a;
console.log(sum); // 14

--运算符在后

var a = 5;
var b = 10;

var sum = b-- + a;
console.log(sum); // 15


位运算符

JavaScript支持的位运算符有如下几种:

&:按位与

|:按位或

~:按位非

^:按位异或

<<:左移位运算符

>>:右移位运算符

>>>:无符号右移位运算符

简单使用

console.log(5 & 9); //1
5 -> 00000101
9 -> 00001001
            =
     00000001 


比较运算符

比较运算符用于判断两个变量或者常量的大小,比较运算的结果是一个布尔值,JavaScript支持的比较运算符如下:

1)>:大于,如果前面变量的值大于后面变量的值,则返回true

2)>=:大于等于,如果前面变量的值大于等于后面变量的值,则返回true

3)<:小于,如果前面变量的值小于后面变量的值,则返回true

4)<=:小于等于,如果前面变量的值小于等于后面变量的值,则返回true

5)==:等于,如果前面两个变量的值相同,则返回true

6)!=:不等于,如果前后两个变量的值不相等,则返回true

7)===:严格等于,前后两个变量的值必须相同并且类型也相同才会返回true。

8)!==:严格不等于,如果前后两个变量的值不相等,或者数据类型不同,都江返回true.

if (5 == "5") 
{
   console.log("==")
}
if (5 === "5") 
{
   console.log("===")
} else 
{
   console.log("not equal")
}

执行结果:

==
not equal


逻辑运算符

逻辑运算符用于操作两个布尔值的变量或者常量,主要有以下3个:

&&:与,必须前后两个操作数都为true才返回true,否则返回false

||:或,只要两个操作法中有一个为true,就可以返回true,否则返回false

!:非,只操作一个操作数,如果操作数为true,则返回false,如果操作数为false,则返回true

var a = 10;
var b = 12;

if (a > 10 || b < 14)
{
    console.log("true")
}


三目运算符

三目运算符的格式如下:

(expression) ? if-true-statement : if-false-statement;

运算规则很简单,先对逻辑表达式expression求值,如果逻辑表达式返回true,则执行第二部分的语句,如果逻辑表达式返回false,则返回第三部分的语句。

var a = 10;
var b = 12;

var result = (a > 10) ? a : b;
console.log(result) // 12


typeof和instance运算符

typeof前面简单了解过,用于判断某个变量的数据类型,它既可以作为函数来使用,例如:typeof(a),返回变量a的数据类型,也可以作为一个运算符来使用,例如:typeof a 可以返回变量a的数据类型。不同类型参数使用typeof运算符的返回值类型如下:

undefined值:object

null值:object

布尔型值:boolean

数字型值:number

字符串值:string

对象:object

函数:function

var a = 5; //number
var b = true; //boolean
var str = "hello"; // string
console.log(typeof(a) + "\n" + typeof(b) + "\n" + typeof(str));
与typeof类似的运算符还有instanceof,该运算符用于判断某个变量是否为指定类的实例,如果是,则返回true,否则返回false。
var a = [3];
console.log(a instanceof Array); //true
console.log(a instanceof Object); //true,JS中所有类都是object的子类

逗号运算符

逗号运算符允许将多个表达式排在一起,整个表达式返回最右边表达式的值。

var a, b, c, d;
a = (b = 5, c = 7, d = 10);
console.log(a); // 10


void运算符

void运算符用于强制指定表达式不会返回值

var a, b, c, d;
a = (b = 5, c = 7, d = 10);
console.log(a); // undefined

语句

语句时JavaScript的基本执行单位.JavaScript要求所有的语句都以分号(;)结束。语句既可以是简单的赋值语句,也可以是算法运算语句,还可以是逻辑运算语句。除此之外还有一些特殊的语句:

异常抛出语句

JavaScript支持异常处理,支持手动抛出异常,JavaScript的所有异常都是Error对象,当JavaScript需要抛出异常总是通过throw语句抛出error对像,语法如下:

throw new Error(errorString);

JavaScript既允许再代码执行的过程中抛出异常,也允许再函数定义中抛出异常,在代码执行过程中,一旦遇到异常,立即寻找对应的异常捕获块(catch块),如果没有对应的异常捕获块,异常将传播给浏览器,程序非正常中止。

for (var i = 0; i < 10; i++) {
	if (i > 4) {
            throw new Error("用户自定义错误");
	}
}

捕获异常

当程序异常出现时,这种异常不管是用户手动抛出的异常还是系统本身的异常,都可以使用catch捕获异常,JavaScript代码运行中一旦出现异常,程序就跳转到对应的catch块,语法如下:

try 
{
    statement
}
catch 
{
    statement
}
finally 
{
    statement
}

上面的捕获语句,finally块是可以省略的,如果指定了finally块,那么finally代码块就会总获得执行的机会。

try 
{
   for (var i = 0; i < 10; i++) {
        console.log(i);
        if (i>5)
	{
	 throw new Error("error");
        }
   }
}
catch (error)
{
    console.log("system wrong: " + error.message);
}
finally
{
    console.log("finally");
}

执行结果:

0
1
2
3
4
5
6
system wrong: error

finally

简单总结:

1)JavaScript只有一个异常类,Error,无须在定义函数时声明抛出该异常,所以没有throw关键字

2)JavaScript是弱类型语言,所以catch语句后括号里的异常实例无须声明类型

3)JavaScript只有一个异常类,所以try块最多只能有一个catch块

4)获取异常的描述信息是通过异常对象message属性,而不是通过getMessage()方法实现


with语句

with语句的作用是避免重复书写同一个对象。语法格式如下

with(object)
{
    statements
}

如果with后的代码块只有一行语句,则可以省略花括号。


流程控制

JavaScript支持的流程控制丰富,有基本的分支语句if,if-else,也有循环语句for,while,for-in等

分支语句

分支语句主要有if和switch语句,其中if有三种格式:

// 形式1
if (expression)
{
  statement
}

// 形式2
if (expression) 
{
    statement
} 
else 
{
    statement
}

//形式3
if (expression) 
{
    statement
}
else if (expression)
{
    statement	 
}
else 
{
    statement
}

通常情况下不要省略if、else、else if后执行块的花括号,但是如果语句执行块只有一行代码时,则可以省略花括号。

var  a = 5;

if (a > 4) 
    console.log("a大于4");
else
    console.log("a小于4");

switch语句的语法如下:

switch (expression)
{
    case condition 1: statemnt
	 break;
    case condition 2: statemnt
	 break;
    default: statemnt
}

先执行expression表达式,然后依次匹配条件condition1,condition2,遇到了匹配的条件就执行相应的代码,如果前面的条件都没有正常匹配,则执行default后的执行体。

function inputScore(score) 
{
	switch (score)
    {
	case 'A': console.log("优秀");
	break;
        case 'B': console.log("良好");
	break;
	case 'C': console.log("及格");
	break;
	case 'D': console.log("不及格");
	break;
	default: console.log("error");
   }
}

注意2点:

1)JavaScript的switch语句可以省略case块后面的break;如果省略了,那么就会一直执行case之后的代码,直到遇见break语句为止

2)switch语句的条件变量不仅可以是数值类型也可以是字符串类型。


while语句

while语句的语法格式如下:

while (expression)
{
    statement
}

当循环体只有一行语句时,循环体的花括号可以省略,while循环语句的作用是:先判断expression逻辑表达式的值,当expression表达式的值为true时,执行循环体,如果微false则结束循环。但是要注意:一定要有false的时候,不然会造成死循环。


do-while循环

do-while循环跟while的区别在于:while是先判断循环条件,只有条件为真才会执行循环体,但是do-while则先执行循环体,然后判断循环条件,如果条件为真,则执行下一次循环。否则中止循环。

do 
{
  statement
} 
while (expression)


for循环

for循环是常用的循环语句,大部分情况下,for循环可以代替while循环、do-while循环。基本语法格式如下:

for (var i = 0; i < Things.length; i++) {
}
循环中两个分号分开了三个语句,其中第一个语句是循环的初始化语句,每个循环语句只会执行一次,而且完全可以省略,因为初始化语句可以放在循环语句之前完成,第二个语句是一个逻辑表达式,用于判断是否执行下一次循环,第三个语句是循环体执行完后最后执行的语句。


for in 循环

for in循环的本质是一种foreach循环,它主要有两个作用:

1)遍历数组里的所有数组元素

2)遍历JavaScript对象的所有属性

语法格式:

for (index in object)
{
    statement
}

如果循环体只有一行代码,则可以省略循环体的花括号,当遍历数组时,for in 循环的循环计数器是数组元素的索引值。

break和continue

break和continue都可用于中止循环,区别在于continue只是中止本次循环,接着开始下一次循环,而break则是完全中止整个循环,开始执行循环体后面的语句。


函数

JavaScript是一种基于对象的脚本语言,JavaScript代码复用的单位是函数,但它的函数比结构化程序设计语言的函数功能更加丰富,JavaScript语言中的函数是“一等公民”,它可以独立存在,而且JavaScript的函数完全可以作为一个类使用,而且它还是该类唯一的构造器,函数本身也是一个对象,函数本身是Function实例。

定义函数的3种方式

JavaScript是弱类型语言,因此定义函数时,既不需要声明函数的返回值类型,也不需要声明函数的参数类型。

定义命名函数

定义命名函数的语法格式:

function functionName(parameter-list)
{
    statements
}
function greeting(name)
{
    console.log("hello " + name);
}
greeting("Jack"); //hello Jack

函数最大作用是提供代码复用,所以应该将需要重复使用的代码块定义成为函数,提供更好的代码复用。函数可以有返回值也可以没有返回值,函数的返回值使用return语句返回,在函数的运行过程中,一旦遇到了第一条return语句,函数就返回返回值,函数运行结束

定义匿名函数

JavaScript提供了定义匿名函数的方式,语法格式如下:

function(parameter list)
{
    statements
};

这种函数定义语法无须指定函数名,而是将参数列表紧跟function关键字,在函数定义语法的最后不要忘记紧跟分号(;)。当通过这种语法格式定义函数之后,实际上就是定义了一个函数对象(即Function实例),接下来可以将这个对象赋给另一个变量,之后可以通过变量来执行调用函数。

var f = function(name)
{
   console.log(name);
};
f("Jack"); //Jack

使用Function类匿名函数

JavaScript提供了Function类,该类可以用于定义函数,Function类的构造器的参数个数可以不受限制,Function可以接受一系列的字符串参数,其中最后一个字符参数是函数的执行体,执行体的各语句可以以分号(;)隔开,而前面的各字符串参数则是函数的参数。

var fun = new Function('name', "console.log(name);");
fun('hua');

上面代码使用了new Function语法定义了一个匿名函数,并将匿名函数赋给fun变量,从而允许通过fun来访问匿名函数。调用Function类的构造器来创建函数虽然能够明确地表示创建一个Function对象,但由于Function()构造器的最后一个字符代表函数执行体,当函数执行体的语句很多时,Function的最后一个参数将变得十分臃肿,因此可读性也差。

局部变量和局部函数

在函数里定义的变量成为局部变量,在函数外定义的变量成为全局变量,如果局部变量和全局变量的变量名相同,则局部变量会覆盖全局变量,局部变量只能够在函数里访问,而全局变量可以在所有的函数里面访问。局部函数即在函数里面定义的函数,并且在在函数内部有效。

function outer()
{
    function inner1()
    {
        console.log("inner1");
    }

    function inner2() {
	console.log("innner2");
    }
    inner1();
    inner2();
}
outer(); // inner1 innner2

函数、方法、对象和类

函数:就像JavaScript的方法一样,这个函数可以被调用,JavaScript的函数不仅是一个函数更是一个类

对象:定义一个函数时,系统也会创建一个对象,该对象是Function类的实例

方法:定义一个函数时,该函数通常都会附加给某个对象,作为该对象的方法

类:在定义函数的同时,也得到了一个与函数同名的类

下面程序定义了一个Person函数,也就是定义了一个Person类,该Person函数也会作为Person类的唯一构造器。

function Person(name, age)
{
	this.name = name; // 将参数name值赋值给name属性
	this.age = age; // 将参数age赋值给age属性

	this.info = function() // 为info创建一个匿名函数
	{
         console.log("My name is: " + this.name);
         console.log("age = " + this.age);
	};
}

var p = new Person("Jack", 26);
p.info();

// My name is: Jack
// age = 26

上面程序中使用了this关键字,被this关键字所修饰的变量不再是局部变量,它是该函数的实例属性。而且info属性是一个函数,即JavaScript定义的函数可以被赋值给对象,作为对象的方法,如果没有明确指定赋值的对象将被赋值到window对象上,作为window对象的方法。


函数的实例属性和类属性

前面也讲到了JavaScript函数不仅仅是一个函数,而且是一个类,该函数还是此类唯一的构造器,只要在调用函数时使用new关键字,就可以返回一个Object,这个Object不是函数的返回值,而是函数本身产生的对象。因此在JavaScript中定义的变量不仅有局部变量,还有实例属性和类属性两种。根据函数中声明变量的方式,函数中的变量有3种:

局部变量:在函数中以普通方式声明的变量,包括以var或者不加任何前缀声明的变量

实例属性:在函数中以this前缀修饰变量

类属性:在函数中以函数名为前缀修饰变量

function Person(name, age)
{
	this.name = name; // 将参数name值赋值给name属性
	Person.age = age; // 将参数age赋值给age属性

	this.info = function() // 为info创建一个匿名函数
	{
         console.log("My name is: " + this.name);
         console.log("age1 = " + this.age);
         console.log("age2 = " + Person.age);
	};
}

var p = new Person("Jack", 26);
p.info();

p.age = "30";
p.info();

// result:
My name is: Jack
age1 = undefined
age2 = 26

My name is: Jack
age1 = 30
age2 = 26

可以看到刚刚开始没有实力属性age,但是当我们执行p.age = "30";之后,自动为对象增加了age属性,这是因为JavaScript是动态语言,它允许为对象增加属性和方法,当我们直接为对象的某个属性赋值时,即可视为给对象增加属性。


调用函数的3种方式

定义一个函数之后,JavaScript提供了3种方式来调用函数。

直接调用函数

直接调用函数是最常见的方式,这种方式直接以函数附加的对象作为调用者,在函数括号内传入参数来调用函数,前面已经大量使用了。

以call()方法来调用函数

直接调用函数的方式简单,但是不够灵活,有时候调用函数时需要动态地传入一个函数引用,此时为了动态的调用函数,就需要使用call()方法来调用函数。

假如我们需要定义一个形如each(array, fn)的函数,这个函数可以自动迭代处理array数组元素,而fn函数则负责对数组元素进行处理,此时需要在each函数中调用fn函数,但目前fn函数并未确定,因此无法采用直接调用的方式来调用fn函数,需要通过call()方法来调用函数。

var each = function(array, fn)
{
 for (var index in array) {
 	fn.call(null, index, array[index]);
 }
};

each([5, 10 ,3], function(index, element)
{
  console.log("第" + index + "个元素是:" + element + "\n");
});

上面的代码使用call()动态的调用函数,call()方法的语法格式如下:

函数引用.函数(调用者,参数1,参数2...),

这里也可以得到直接调用函数与通过call()方法调用函数的关系:

调用者.函数(参数1,参数2...) = 函数.call(调用者,参数1,参数2...)

apply()方法调用函数

apply()方法与call()方法功能基本相似,它们都可以动态地调用函数。apply()与call()的区别如下:

1)通过call()调用函数时,必须在括号中详细的列出每个参数

2)通过apply()动态地调用函数时,可以在括号中以arguments来代表所有参数。

var example = function (num1, num2)
{
	myfun.apply(this, arguments);
};
example(20, 40); // 直接调用
example.apply(null, [10, 15]); // 使用apply方法调用函数


函数的参数

对于基本类型参数,JavaScript采用值传递的方式,当通过实参调用函数时,传入函数里的并不是实参本身,而是实参的副本,因此在函数中修改参数值并不会对实参有任何影响。

function change(paramter) 
{
	paramter = 20;
	console.log(paramter); //20
}
var x = 10;
change(x);
console.log(x); //10

对于复合类型的参数,实际上采用的依然是值传递的方式

function changeAge(person) 
{
    person.age = 10;
    console.log(person.age); // 10
    person = null; // 将person对象设置为null

}
var person = { age: 5}; // 使用JSON语言创建对象
changeAge(person);
console.log(person.age); // 10
console.log(person); // { age: 10 }

上面代码和结果可知,person对象的age属性其值是发生了改变,但是person对象被设置为null之后却依然存在,这表明person对象本身并没有被传入函数中,传入的只是person对象的副本而已,这里可能很容易让人混淆。

复合类型的变量本身并未持有对象本身,复合类型的变量只是一个引用,该引用指向实际的JavaScript对象,当把person复合类型的变量传入changeAge()函数时,传入的依然是person变量的副本,只是该副本和原person变量指向同一个JavaScript对象,因此不管是修改副本所引用的JavaScript对象,还是修改person变量所引用的JavaScript对象,实际上修改的是同一个对象。


空参数

function changeAge(person) 
{
	if (typeof person == 'object')
	{  
	    person.age = 10;
        console.log(person.age); // 10
	}
    else
    {
        console.log("参数类型不符合");
    }
}
changeAge(); //参数类型不符合

由上面的代码可知,函数声明中包含了一个参数,但是调用函数时并没有传入任何参数,这种情况对于强类型语言,如:Java,C那是绝对不允许的,但是对于JavaScript却没有任何语法问题,因为JavaScript会将没有传入实参的参数值自动设置为undefined。

由于JavaScript调用函数时对传入的实参并没有要求,即使定义函数时声明了多个形参,调用函数时也并不强制要求传入相匹配的实参。因此JavaScript没有所谓的函数“重载”,对于JavaScript而言函数名就是唯一的标识。





猜你喜欢

转载自blog.csdn.net/longshihua/article/details/80335765