学习菜鸟教程JS函数,写下这篇笔记。
目录
一.JS函数定义的三种方式
1.使用关键字function定义函数
function myfunction(a,b){
return a*b;
}
2.通过一个表达式定义函数
var x = function(a,b){return a*b};
var y = x(3,4);
//y的结果为12
该中方法中的函数被称为匿名函数 ,没有函数名称,通过使用变量来调用。
3. 函数构造器Function()定义函数(不常用)
var myFunction = new Function("a","b","return a*b");
var x = myFunction(3,4);
//x结果为12
二.自调用函数
(function myFunction(){
var x = "Hello!"; //实际上该函数是一个匿名自我调用的函数
})();
//通过在函数表达式后添加(),使得其成为自调用函数
//当代码执行到自调用函数时,自调用函数将会直接执行,不
//再需要调用函数的方法等操作,与普通代码块无异。
三.箭头函数
箭头表达式的语法比普通函数表达式更简洁。
ES6新增的箭头函数。
//多参数类型
(x,y,z)=>{return x*y*z};
//单参数类型
x=>{return x};
//无参数类型
()=>{return "hello!"};
注:
1.我们在使用箭头函数时,箭头函数会默认关键字this绑定外层this的值,因此,箭头函数中this的值与箭头函数外层this值是相等的。
2.箭头函数不能提升,需要在使用前完成定义。
3.使用const比使用var更安全,因为函数表达式始终是一个常量。
四.函数提升(Hoisting)
提升(Hoisting)是JS默认将当前代码块的作用域提升到前面的行为。
提升(Hoisting)应用在变量和函数的声明。
myFunction(5);
function myFunction(y){
return y;
}
//不会报错,y的值为5
五.函数是对象
在JS中使用操作符typeof判断函数类型将会返回“function”,但是在JS中也可以将函数看做为一个对象。因为JS函数具有属性和方法。
1.arguments.length属性会返回函数调用过程中接收到的参数个数。
function myFunction(a,b){
return arguments.length;
}
//返回2
2.toString()方法将函数作为一个字符串返回。
function myFunction(a,b){
return a*b;
}
var x = myFunction.toString();
//x的值为"return a*b"
arguments对象
JS函数中有一个内置的arguments对象,arguments对象包含了函数参数的数组。
//返回最大值的函数
function myFunctin(){
var i,max = arguments[0];
if(arguments.length < 2) return max;
for(i = 0; i < arguments.length; i++){
if(arguments[i] > max){
max = arguments[i];
}
}
return max;
}
六.函数参数
JS中的函数参数分为函数显式参数与隐式参数。
显式参数在函数定义时列出。
隐式参数是函数调用时传递给函数真正的值。
function myFunction(a,b){
return a*b; //a,b为显式参数
} //x,y为隐式参数
var x=1,y=2;
myFunction(x,y);
参数规则
1.JS函数定义显式参数时没有指定数据类型。
2.JS函数对隐式参数没有进行类型检测。
3.JS函数对隐式参数的个数没有进行检测。
默认参数
在ES5中,如果在调用函数时,未定义隐式参数,则参数会默认设置为:undefined。
function myFunction(x,y){
y = y||0;
return x*y;
}
myFunction(3);
//返回的值为0,y为undefined或null;
但在一般情况下,都会为参数设置一个默认值(初始值)。
function myFunction(x){
if(x === undefined){
x = 0;
}
}
在ES6下,函数会判断函数参数是否有默认值。
function myFunction(x,y = 10){
return x + y;
}
myFunction(0,2); //返回2
myFunction(5); //返回15
注:==与===的区别:
使用==时,如果value1与value2的数据类型不同,会将两者转化为相同的数据类型后再进行值的比较,值相同则true,反之则false。
使用===时,表示在严格模式"use strict"下的相等,如果value1与value2的类型不相同,则false,在数据类型相等且值相等的情况下,则为true。
七.函数调用的四种类型
函数调用类型的不同与关键字this有一定联系,在一般情况下,this会指向函数执行时当前的对象。
1.函数直接调用(未被对象调用)
function myFunction(x,y){
return x*y;
}
myFunction(2,10);
//结果为20;
该函数不属于任何对象。但在JS中它始终是默认全局对象的函数。
在HTML中默认的全局对象是HTML页面本身,所以它是HTML页面对象的函数。
在浏览器页面中的全局对象是浏览器窗口(window对象)。因此该函数会自动成为window对象的函数。
即myFunction(2,10)和window.myFunction(2,10)是等同的。
注:由于全局变量,方法或函数容易造成命名冲突,产生bug,因此window.myFunction(2,10)不被推荐使用。
全局对象
在函数没有被对象调用时,this的值会自动变为全局对象。
在web浏览器中,全局对象是浏览器窗口(window对象)。
function myFunction() {
return this;
}
myFunction(); // 返回 window 对象
2.函数作为方法调用
在JS中,匿名函数常作为对象的一个方法。
var myObject = {
firstName:"John", //两个属性firstName,lastName
lastName: "Doe", //一个方法fullName
fullName: function () {
return this.firstName + " " + this.lastName;
}
}
myObject.fullName(); // 返回 "John Doe"
fullName方法是一个函数,函数属于myObject对象,这里的this指向myObject对象,所以this为Object对象。
3.使用构造函数调用函数
//构造函数:
function myFunction(arg1, arg2) {
this.firstName = arg1;
this.lastName = arg2;
return this;
}
var x = new myFunction("John","Doe");
x.firstName; //x.firstName的值为John
document.write(x); //输出 object 对象
函数在调用前使用了关键字new,则是调用了构造函数。
前面提到在JS中函数是对象,这里可以理解为新建了一个myFunction类型的x对象。
构造函数的调用会创建一个新的对象,该对象会继承构造函数的属性和方法。
注:构造函数中关键字this没有任何指向的对象,在函数调用实例化对象时,this指向了x对象。
4.函数作为函数方法调用
在JS中,函数也是对象,有属性和方法。
call()和apply()是函数的两个方法。两个方法在调用函数时,第一个参数必须是对象本身。
//call()方法
function myFunction(a, b) {
return a * b;
}
myObject = myFunction.call(myObject, 10, 2); // 返回 20
//apply()方法,传入数组
function myFunction(a, b) {
return a * b;
}
myArray = [10, 2];
myObject = myFunction.apply(myObject, myArray); // 返回 20
在JS严格模式(strict mode)下,在使用这两种方法时,第一个参数会变为this的值(如:window,object);在非严格模式下,如果第一个参数的值为null或undefined,this的值将自动使用全局对象替代。
八.闭包
在JS中,全局变量与局部变量常存在。
全局变量可以用于页面上的所有脚本。
函数内部声明的变量则是局部变量,局部变量只能作用于定义它的函数,对于脚本和其他函数是不可用的。
注:变量声明时如果不使用 var 关键字,那么它就是一个全局变量,即便它在函数内定义。
现存在一个计数器困境。
使用全局变量设置计数器递增。
var a = 0;
function add(){
return a += 1;
}
add(); //1
add(); //2
add(); //3
计数器确实在递增,但由于a是全局变量,页面的所有脚本都可以改动它,那么计数器将毫无意义。
使用局部变量设置计数器递增。
function add() {
var a = 0;
return a += 1;
}
add(); //1
add(); //1
add(); //1
毫无疑问,使用局部变量设置计数器递增也是失败的。
内嵌函数
在JS中,所有函数都能够访问它们上一层的作用域。也就是说内嵌函数可以访问上一层函数的变量。
function add1(){
var a = 0;
function add2() {a += 1;}
add2();
return a; //a为1
}
add1(); //1
add1(); //1
add1(); //1
这样就完美解决了全局计数器中所有脚本会改变变量a的问题。
看到这儿,可能存在这样的疑问:局部计数器出现的问题与内嵌计数器出现的问题不是一样的吗?其实,内嵌计数器优化了局部计数器中的问题。虽然两者结果都只能是1,但是只要让内嵌计数器中的变量a只执行一次就行了。
这里需要用到前面提到的自调用函数,自调用函数会自动执行,且执行一次。
var add = (function(){
var a = 0;
return function() {return a += 1;}
})();
add(); //1
add(); //2
add(); //3
自调用函数执行后,a==0,返回了一个方法;即var add = function(){return a +=1;},这是函数定义类型中的第二类:通过一个表达式定义函数。然后直接调用函数add(),即可实现自增。
这个就是闭包,其中变量a可称为私有变量。计数器受匿名函数的作用域保护,只能通过调用add函数修改。
闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。直观的说就是形成一个不销毁的栈环境。