JS-函数
-
函数简介
—函数也是一个对象;
—函数中可以封装一些功能(代码),在需要时可以执行这些功能(代码);
—函数中可以保存一些代码,在需要是时调用;
—使用
typeof
检查一个函数对象时,会返回function
;—函数对象具有对象的基本功能(添加属性等功能),同时它可以封装代码;
使用构造函数创建函数对象
—语法:
var fun = new Function();
—可以将要封装的代码以字符串的形式传递给构造函数;
var fun = new Function("console.log('hello,这是我的第一个函数');");
—封装到函数中的代码不会立即执行,函数中的代码会在函数调用的时候执行;
—实际开发中,很少使用构造函数创建函数对象;
使用函数声明创建函数
—语法:
function 函数名([形参1,形参2,...形参N]){ 函数体 }
注意:形参可写可不写,根据自己构建函数的情况
使用函数表达式创建函数
—语法:
var 函数名 = function([形参1,...形参N]){ 函数体 };
注意:可以理解为匿名函数赋值语句,因此后面需要加上分号;
调用函数
—语法:函数对象();
—当调用函数时,函数中封装的代码就会按照顺序执行;
-
函数的参数
形参
—可以在函数中()指定一个或者多个形参(形式参数);
—多个形参之间用逗号隔开,声明形参就相当于在函数内部声明了对应的变量,但是并不赋值;
实参
—在调用函数时,可以在()中指定实参(实际参数);
—实参将会赋值给函数中对应的形参;
—调用函数时,浏览器不会检查实参的类型,所以要注意是否有可能接收到非法的参数,如果有可能,则需要对参数的类型进行检查;
—调用函数时,浏览器解析器不会检查实参的数量,多余的实参不会被赋值;如果实参的数量少于形参,则没有对应实参的形参将会是
undefined
;—函数的实参可以是任意的数据类型;
<script> //创建函数 function sum(a,b){ console.log(a+b); } //调用函数 sum(1,2); </script>
—实参可以是任意值,也可以是一个对象!!!
—当我们参数过多时,可以将参数封装到一个对象中,然后通过对象传递;
<script> //创建函数 function sayHello(obj){ console.log("Hello,everyone!My name is"+obj.name+",my age is"+obj.age); } //创建对象 var obj={ name:"Tom", age:18 }; //调用函数 sayHello(obj); </script>
—实参也可以是一个函数;
注意:
fun()
—调用函数;如果将调用函数传递给实参,实际上是将函数的返回值传递给实参!!fun
—函数对象;如果将函数对象传递给实参,相当于直接使用函数对象;<script> //创建函数 function fun(a){ a(obj); } //将函数作为实参传递给形参a并调用函数 fun(sayHello); //传递匿名函数 fun(function(){alert("hello")}); </script>
-
函数的返回值
—可以使用
return
来设置函数的返回值;—语法:
return 返回值
—
return
后的值将会作为函数的执行结果返回;—可以定义一个变量接收这个结果;
—在函数中,
return
后的语句都不会执行;—如果
return
语句后不跟任何值就相当于返回一个undefined
;—如果函数中,不写
return
,也会返回undefined
;—使用
break
可以退出当前循环,使用continue
可以结束当次循环,而使用return
可以结束整个函数,其后边的语句都不会执行;<script> //判断是否为偶数,如果是返回true function isEven(num){ return num%2==0; } //存储返回值 var result=isEven(2); console.log("result = "+result); </script>
—
return
返回值可以是任意数据类型,也可以是对象,也可以是一个函数;<script> function fun1(){ //函数内部再次声明一个函数 function fun2(){ alert("我是fun1中的fun2"); } //将fun2函数对象作为返回值 return fun2; //将fun2函数返回值作为返回对象 return fun2(); } //调用函数fun1 a=fun1(); console.log(a); </script>
-
立即执行函数
—函数定义完,立即被调用执行;
—立即执行函数只会执行一次;
<script> //用小括号括起来的函数即为立即执行函数 (function(a,b){ alert(a+b); })(1,2); </script>
-
全局作用域
—作用域:一个变量作用的范围;
—在JS中,一共分为两种作用域:全局作用域和函数作用域(局部作用域);
—直接编写在
<script>
标签中的JS代码,都在全局作用域中;—全局作用域在页面打开时创建,在页面关闭时销毁;
—在全局作用域中有一个全局对象
window
,它代表的是一个浏览器的窗口,由浏览器直接创建,我们可以直接使用;—在全局作用域中,我们创建的变量都会作为
window
的属性保存;—在全局作用域中,我们创建的函数都会作为
window
的方法;—全局作用域中的变量都是全局变量,在页面中的任意部分都可以访问到;
<script> var a=10; //创建的变量可以作为全局对象window的属性进行读取 console.log(window.a); //创建函数 function fun(){ console.log("My Name is Jack"); } //可以通过访问全局对象的方法进行访问创建的函数 window.fun(); </script>
变量的声明提前
—使用
var
关键字声明的变量,会在所有代码执行前被声明好(不会赋值);但是如果声明变量时,不使用var
关键字,则变量就不会声明提前;<script> //不会报错,b的返回值为undefined console.log(b); //变量声明提前 var b=10; //相当于以下写法 var b; console.log(b); b=10; </script>
函数的声明提前
—使用函数声明创建的函数
function 函数名(){}
它会在所有代码执行前就被创建;所以可以在函数声明前调用函数;—但是使用函数表达式创建的函数
var 函数名 = function(){}
不会被声明提前,所以不能在声明前调用函数<script> //函数声明提前,可以执行 fun1(); function fun1(){ console.log("我是一个fun函数"); } //使用变量表达式创建函数,无法声明提前,报错 fun2(); var fun2 = function(){ console.log("我也是一个fun函数"); }; </script>
-
函数作用域
—调用函数时创建函数作用域,函数执行完毕后,函数作用域销毁;
—每调用一次函数就会创建一个新的作用域,它们之间是相互独立的;
—在函数作用域中可以访问到全局作用域中的变量;
—在全局作用域中无法访问到函数作用域的变量;
—当在函数作用域中操作一个变量,现在自身作用域中寻找,如果有就直接使用;如果没有就向上一级作用域(不一定是全局作用域,也有可能是函数)访问(就近原则),直到全局作用域,如果全局作用域没有,则报错
ReferenceError
;—在函数作用域中如果想要访问全局作用域的变量,可以通过**
window.变量名
**的形式进行访问;—在函数作用域中也有声明提前的特性;使用
var
关键字声明的变量会在函数中会在所有代码执行之前被声明;—函数声明也会在函数中所有代码执行之前执行;
—在函数中,不使用
var
关键字声明的变量都会变成全局变量;—定义形参就相当于在函数作用域中声明了变量;
<script> var a=33; function fun(){ //未使用var关键字定义变量,因此变成全局变量 a=10;//相当于window.a=10 } //调用函数 fun(); console.log(a);//返回10 var e = 23; function fun2(e){ //定义形参相当于在函数作用域中声明了变量 //var e alert(e);//返回undefined } </script>
-
this
—解析器在调用函数时,每次都会向函数内部传递进一个隐含的参数,这个隐含的参数为
this
;—
this
指向的是一个对象,这个对象是函数执行的上下文对象;—根据函数的调用方式不同,
this
指向不同的对象;- 以函数的形式调用this,this指向的是Window;
- 以方法的形式调用this,this指向的是调用方法的对象;
- 当函数以构造函数创建时,this就指向构造函数新建的对象;
- 使用call和apply调用时,this是参数中指定的对象;
- 在事件的响应函数中,响应函数是给哪个对象绑定的,this就是哪个对象;
<script> function fun(){ //以函数的形式调用,this指向window对象 console.log(this) } fun();//返回window var obj = { name:"Tom", sayName:fun } //以方法的形式调用,this指向调用方法的对象obj obj.sayName();//返回Object </script>
<script> //通过this可以访问不同的对象 function fun(){ console.log(this.name); } var obj1 = { name:"Tom", sayName:fun }; var obj2 = { name:"Jack", sayName:fun }; obj1.sayName();//返回Tom obj2.sayName();//返回Jack </script>
-
构造函数
—构造函数就是一个普通函数,创建方式和普通函数无区别;
—不同的是,构造函数的首字母习惯上为大写;
—构造函数和普通函数调用方式不同:
普通函数调用方式是直接调用;
构造函数需要使用
new
关键字进行调用,其返回对象类型;—构造函数执行流程:
- 调用构建函数时,立即创建一个新的对象;
- 将新建的对象设置为函数中的
this
,在构造函数中可以通过this
来引入新建的对象; - 逐行执行函数代码;
- 将新建的对象作为返回值返回;
—使用同一个构造函数创建的对象,称为一类对象,也将一个构造函数称为一个类;使用构造函数创建的对象称为该类的实例;
—
instanceof
可以检查一个对象是否是一个类的实例;如果是返回true
,否则返回false
;—任何对象都是
Object
的实例,所以任何对象和Object
做instanceof
都会返回true
;<script> //创建构造函数 function Person(name,age){ //this指向构造函数中新建的对象 this.name=name; this.age=age /* 在构造函数内部创建方法,构造函数每执行一次都要创建一个sayName()方法,所有创建的实例都是唯一的sayName()方法; 导致构造函数执行一次就会创建一个新的方法,占据较多的内存;具有一定的局限性; */ //可以使所有对象共享同一个方法 this.sayName=function(){ alert(this.name); }; this.sayName=fun; } //将sayName()方法在全局作用域中定义,避免重复创建 function fun(){ alert(this.name); } //将函数定义在全局作用域中,污染了全局作用域的命名空间,并且定义在全局作用域中不安全 //因此可以通过原型对象进行创建方法 Person.prototype.sayName=function(){ alert(this.name); }; //调用构造函数创建类的实例 var per = ner Person("Tom",18); //检查是否实例属于类 console.log(per instanceof Person); </script>
-
toString()
—当我们直接在页面中打印一个对象时,实际上是输出的对象的
toString()
方法的返回值(Object
);—如果我们希望在输出对象时,不输出
toString()
方法的返回值,可以为对象添加一个toString()
方法以修改其输出;<script> function Person(name,age,gender){ this.name=name; this.age=age; this.gender=gender; } //修改原型的toString()方法 Person.prototype.toString=function(){ return "Person[name="+this.name+",age="+this.age+",gender="+this.gender+"]" }; //创建Person的一个实例 var per = new Person("Tom",18,"boy"); console.log(pe); </script>
-
函数的方法
call()
和apply()
—这两个方法都是函数对象的方法,通过函数对象来调用;
—当对函数调用两个方法,都会调用函数执行;
—在使用以上两个方法时,可以将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的
this
;(传递参数是谁,this
指定的对象就是谁)—
call()
方法可以将实参在对象之后依次传递;apply()
方法需要将实参封装到数组中统一传递;<script> var obj1 = { name:"Tom", age:18, sayName:function(){ alert(this.name); } }; var obj2 = { name:"Jack" }; obj1.sayName.apply(obj2);//返回Jack //call()和apply()的区别 function fun(a,b){ console.log("a="+a); console.log("b="+b); } var obj3 = { name:"Tom", sayName:function(){ alert(this.name); } }; //将实参在对象之后依次传递 fun.call(obj3,1,2); //将实参封装到数组中统一传递 fun.apply(obj3,[1,2]); </script>
-
arguments
在调用函数时,浏览器每次都会传递两个隐含的参数:
- 函数的上下文对象
this
; - 封装实参的对象
arguments
;
—
arguments
是一个类数组对象,伪数组;—他可以通过索引来操作数据,以及
arguments.length
获取实参长度;—在调用函数时,我们所传递的实参都会封装到
arguments
中,我们可以获取其长度和元素arguments[索引]
;即使不定义形参,也可以通过arguments
来获取实参;—
arguments
中callee
属性,该属性对应函数对象,即指向当前正在执行的函数对象; - 函数的上下文对象