函数
目录1. 函数的声明与定义
-
函数也是一个对象,函数中可以封装一些代码,在需要时可以执行这些代码
-
常规函数
- 封装到函数中的代码不会立即执行,函数内的代码会在函数被调用时按顺序执行
- 调用函数时,解析器不会检查实参的数量。如果实参数量多余形参,则多余的实参不会被赋值;如果形参数量多余实参,则多余的形参将是undefined
function fun2(a,b){ alert("函数fun2"+(a+b)); } fun2(1,1); function sum(num1,num2){ return num1+num2; } alert(sum(1,1));
-
匿名函数
var fun3 = function(){ alert("匿名函数fun3"); } fun3();
-
立即执行函数
立即执行函数往往只会执行一次。立即执行函数必须要用括号囊括,否则会报错
(function(){ console.log("hi"); })() (function(a,b){ console.log("a = " + a); console.log("b = " + b); })(123,456)
注意:Js中不允许重载函数,一旦函数重名,后续书写的函数会直接覆盖掉前面的同名函数
2. 方法
当函数作为对象的属性保存,则称这个函数是这个对象的方法
-
对象创建时定义方法
var obj{ name:"Jack", sayName:function(){ console.log(obj.name); } }
-
对象创建后追加定义方法
var obj = new Object(); obj.name = "jack"; obj.sayName = function(){ console.log(obj.name); }
3. 作用域
- JS中的作用域和Java中的略有不同
全局作用域
- 直接编写在script标签中的JS代码,全都在全局作用域
- 全局作域用在页面打开时创建,在页面关闭时销毁
- 在全局作用域中有一个全局对象window
- window代表的是一个游览器的窗口,由游览器自动创建
- 创建的变量都会作为window对象的属性保存
- 创建的函数都会作为window对象的方法保存
- 全局作用域中的变量都是全局变量,在页面的任意部分(包括函数),都可以访问的到
函数作用域
-
每次调用函数时都会创建函数作用域,函数作用域之间是相互独立的,函数执行完毕后销毁
-
在函数作用域中可以访问到全局作用域中的变量,反之则不行
-
当函数作用域操作一个变量时,它会先在自身作用域中寻找并使用,如果没有,则在上一级作用域中寻找,直到报错
-
注意:定义形参就相当于在函数作用域中声明了变量
var e = 23; function fun(e){ alert(e); } fun6(); //输出undefined
这就是什么es6中let类型直接禁止了相关操作
function test(e){ let e = 23; //重复声明 alert(e); } test(e); //报错
声明提前
-
变量的声明提前
-
使用var关键字声明的变量,会在所有的代码执行之前被声明,但是不会被赋值
如果声明变量时不使用var关键字,则变量不会被声明提前
console.log(a); //输出undefined,意味着a已被定义尚未赋值 var a = 123;
-
-
函数的声明提前
- 使用函数声明形式创建的函数
function 函数(){}
,会在所有的代码执行之前就被创建,所以可以在函数被定义前来调用函数
fun1(); //正常调用 function fun1(){ console.log("fun函数") }
- 使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用
fun2(); //报错 var fun2 = function(){ console.log("fun2函数") };
- 使用函数声明形式创建的函数
4. 构造函数
-
JS中没有构造器,所以要初始化一个对象需要借助构造函数。构造函数和一般函数唯一区别就是,普通函数可以直接调用,构造函数需要用new关键字调用
-
构造函数的执行流程:
- 立刻创建一个新的对象
- 将新建的对象设置为函数中的this,在构造函数中可以使用this来引用新建的对象
- 逐行执行函数中的代码
- 将新建的对象作为返回值返回
-
使用同一个构造函数创建的对象,称为类对象,构造函数也是一个类。将通过构造函数创建的对象,称为是该类的实例
function Person(name,age,gender){ this.name = name; this.age = age; this.gender = gender; //此处的方法设计仍有缺陷,详情见"原型对象" this.sayName = function(){ alert(this.name); }; } var person = new Person("Jack","18","男");
-
单纯通过以上方式创建的构造函数具有巨大缺陷。不像Java等主流语言,JS不存在共有的方法区,由于每个对象都是独立的,所以创建多个对象就会创建多个相同的,独立的属性和方法,占用很多内存。为了解决这一问题,需要使用原型函数
function Person(name,age,gender){ this.name = name; this.age = age; this.gender = gender; } Person.prototype.sayName = function(){ alert(this.name); }; var person = new Person("Jack","18","男"); person.sayName();
instanceof
-
作用和java中的类似,检查一个对象是否是一个类的实例。若是返回true,反之返回false.
语法:
对象 instanceof 构造函数
-
所有对象都是Object后代,所以任何对象和Object使用instanceof检查时都会返回true
console.log(dog instanceof Person);
5. call()和apply()
-
两个方法都是函数对象的方法,需要通过函数对象来调用,函数对象调用方法后会立刻执行。
-
调用call()和apply()可以将参数中的对象指定为函数执行时的this
-
call()方法可以将实参在对象自会后依次传递
apply()方法需要将实参封装到一个数组中统一传递
function fun(a,b){ console.log("a = " + a); console.log("b = " + b); alert(this); } var obj = { name: "obj", sayName:function(){ alert(this.name); } }; var obj2 = { name: "obj2" } //修改this fun(); //输出window fun.applay(obj); //输出obj obj.sayName.apply(obj2); //输出"obj2" //形参差异 fun.call(obj,2,3); fun.call(obj,[2,3]);
6. this 和 arguments
- 解析器在调用函数时每次都会向函数内部传递两个隐含的参数,分别是this和arguments
this
-
this指向一个对象,这个对象称为函数执行的上下文对象
-
根据函数的调用方式不同,this会指向不同的对象:
- 以函数形式调用时,this永远是window
- 以方法的形式调用时,this是调用方法的对象
- 以构造函数的形式调用时,this是新创建的对象
- 使用call和apply调用时,this是指定的那个对象
arguments
-
在function函数中有一种不需要定义的,但却可以直接用来获取所有参数的变量,我们管他叫做隐形参数arguments
arguments类似于java的可变参数,隐形参数本身是一个类数组
function fun(){ alert(arguments.length); } fun(a,b,c); //就算fun是无参函数,arguments依旧会接收从外界传入的参数并存储,只是不会在函数内使用罢了 function sum(num1,num2){ var result = 0; for(var i = 0;i < arguments.length;i++){ result += arguments[i]; } return result; } alert(sum(100,200,300,400)); //最终输出结果为1000,可以看到即便不按照形参输入arguments也会照常计算,这样很危险