函数是什么?
函数用于封装多条代码语句以实现所需功能,便于维护和服用。
我们需要知道立即执行函数,回调函数,匿名函数。
函数声明
函数声明有三种方式,前两种是常用的需要掌握。
用function关键字声明
如果没有返回的内容,则在写代码的时候不关注返回值
没有return:代码执行到大括号
用函数表达式声明
与常规的变量赋值相似,我们需要先创建一个匿名函数,然后赋值给变量函数名。我们知道在JS中如果要想使用一个变量,必须先给这个变量赋值,所以函数表达式也不例外,在调用之前,必须先给它赋值。否则会报错。
使用构造函数声明
变量提升
与var变量相似,会进行提升。
函数内部属性
只有在函数内部才能访问的属性。但this也可以在函数外部进行使用。
arguments
arguments是一个类数组对象,包含着传入函数中的所有参数。arguments主要用途是保存函数参数,但是这个对象还有一个名为callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。
作用:常用于实现实参个数不固定的函数。
下图实例中,只往函数中传入了两个形参,但是调用时却传入了七个实参,这些参数都保存在arguments中,因此不会报错。
callee
callee 属性是 arguments 对象的一个成员,仅当相关函数正在执行时才可用。
callee 属性的初始值就是正被执行的 Function 对象。
作用:callee可以保证外部名称的变化,不会引起内部代码的修改, 降低代码的耦合度 。
// 实现匿名的递归函数
var sum = function (n) {
if (n == 1) {
return 1;
} else {
return n + arguments.callee(n - 1);
}
}
console.log(sum(6));//输出结果:21
*** this ***
this是根随着执行环境变化而变化的,它的指向取决于上下文环境 被谁调用就指向谁 没有明确指出被谁调用就是指向全局global
在方法中,this 表示该方法所属的对象。
如果单独使用,this 表示全局对象。
在函数中,this 表示全局对象。
在事件中,this 表示接收事件的元素。
在显式函数绑定时,我们可以自己决定this的指向
-
1.对象方法中的this
this指向调用它的对象:
this表示person对象,sayName方法所属对象就是person,所以
this.name指向的是person对象里的name属性。
// 谁调用了这个方法 this就指向谁
var person = {
name:"zzy",
age:17,
sayName:function(){
console.log("my name is",this.name);
}
}
person.sayName();
-
2.单独使用this
单独使用 this,则它指向全局对象。
在浏览器中,window 就是该全局对象为 [object Window]:
在node中,指向的是一个{}
var x = this;
-
3.函数中使用this
在函数中,函数的所属者默认绑定到 this 上,属于全局性调用
在浏览器中,window 就是该全局对象为 [object Window]:
在node中,指向的就是global对象
function myFunction() { return this; }
-
事件中的 this
在 HTML 事件句柄中,this 指向了接收事件的 HTML 元素:
<button onclick="this.style.display='none'"> 点我后我就消失了 </button>
显示数据绑定:修改this指向对象
在 JavaScript 中函数也是对象,对象则有方法,apply 和 call 就是函数对象的方法。这两个方法异常强大,他们允许切换函数执行的上下文环境(context),即 this 绑定的对象。
- call
- apply
(详细见函数调用)
函数调用
-
函数名(实参列表);
-
函数名.call(执行环境对象,实参列表);
-
函数名.apply(执行环境对象,实参列表数组);
-
函数名.bind(执行环境对象)(实参列表);
案例:
var obj = {
name: 'taylor',
sayName: function () {
console.log(this.name);
}
}
var b = obj.sayName;
b(); //undefined
obj.sayName(); // taylor
我们无法直接让b() 指向obj中的方法,那么可以使用以下的方法:
1.call(执行环境对象,实参列表);
调用call方法时,第一个参数是要把b添加到的环境,简单来说,就是this要指向的那个对象。
var obj = {
name: 'taylor',
sayName: function () {
console.log(this.name);
}
}
var b = obj.sayName;
b.call(obj); // taylor
在call方法中也可以传递多个参数
var obj = {
name: 'taylor',
sayName: function (a, b) {
console.log(this.name);
console.log(a, b); // 1, 2
}
}
var b = obj.sayName;
b.call(obj, 1, 2); // taylor
2.apply(执行环境对象,实参列表);
与call的用法相似,都可以改变this指向。
不同的是,如果要传递多个参数,则第二个参数必须为一个数组。
用例就不再赘述。
call与apply的总结:
这两个方法中的第一个参数若为null,则this在node环境下指向的是global对象,在HTML中指向的是window对象。
3.bind(执行环境对象)(实参列表);
案例:
可以看到代码没有被打印,这就是bind和call、apply方法的不同,因为实际上bind方法返回的是一个修改过后的函数
我们需要新建一个变量来接收bind修改过后的函数
同样bind也可以有多个参数,并且参数可以执行的时候再次添加,但是要注意的是,参数是按照形参的顺序进行的。
总结:call和apply都是改变上下文中的this并立即执行这个函数,bind方法可以让对应的函数想什么时候调就什么时候调用,并且可以将参数在执行的时候添加,这是它们的区别,根据自己的实际情况来选择使用。
函数应用
*回调函数
作用:回调函数一般都用在耗时操作上面:因为主函数不用等待回调函数执行完,可以接着执行自己的代码。比如ajax请求,比如处理文件等。
回调地狱
常见于异步操作网络请求,如果出现多个网络请求必须同时执行的场景,代码就会变得特别复杂。
回调地狱就是在一个回调函数里面不断触发下一个,下下一个……
解决方法:可以使用promise对象来解决回调地狱的问题(ES6提供的对象)
* 作为返回值
var a = 10
function fn() {
var b = 20
function bar() {
console.log(a + b) //30
}
return bar
}
var x = fn(), // 执行fn() 返回的是bar
b = 200 // 实际上不起作用
x() //执行x,就是执行bar函数
上例中形成闭包。之后再详细介绍