函数的理解和使用
函数的分类
普通函数
// 声明式创建函数
function f1(){
console.log("welcome to my blog");
}
// 声明式创建函数的执行
f1(); // welcome to my blog
// 赋值式创建函数
var f2 = function(){
console.log("thumbs up");
}
// 赋值式创建函数的执行
f2(); // thumbs up
构造函数
- 一般使用new关键字来调用构造函数
var obj = new Object();
递归函数
钩子函数
箭头函数
尾调函数
匿名函数
- 在使用匿名函数之前,必须先进行赋值操作,否则将会引起错误。
作为变量的“值”存在
var fn = function(){
console.log("collection");
}
fn(); // collection
作为“值”存在,fn确实是函数名,但是它的值function(){}确实也是一个无名函数的形式。
作为事件处理函数执行
document.onclick = function(){
alert("comments"); // 点击页面的时候弹出comments
}
当有绑定的事件类型被触发时,执行这里的代码
作为函数的参数使用(回调函数)
function fn(cb){
cb(); // 用cb来接收,并且执行,打印 subscribe
}
fn(function(){ // 执行fn的时候传入一个为函数的参数
console.log("subscribe");
});
当一个函数的参数又是一个函数时,这个作为参数的函数叫回调函数
作为函数的返回值使用
var fn(){
return function(){
console.log("thanks for watching");
}
}
var f = fn(); // 执行fn的时候得到了返回值,这个返回值就是无名函数
f(); // 执行无名函数,打印 thanks for watching
在一个函数内部,嵌套了另一个函数,此时就形成了闭包
作为匿名函数的函数体
;(function (){
console.log("thumbs up");
})(); // 匿名函数是自动执行的,打印 thumbs up
在匿名函数之前的语句必须加上";",js中不能识别换行
函数的定义
函数声明
- 结构
function 函数名(形参){
方法体
return 返回值
}
- 在JS中函数的声明会被提升,即在运行前函数会先被声明,所以可以在代码中可以在函数声明之前调用函数而不会报错。
- 示例
add(1,2);
function add(a,b){
var total = a + b;
return total;
}
var aaa=new add();
函数表达式
- 结构
var 函数名 = function(){};
- 示例
add();
var add = function(a,b){
return a+b
};
函数的真实执行顺序
var add;
add();
add = function(a,b){return a+b};
- 理解
var声明的变量会被提升,此时的函数视为赋值给add,所以不会被提升,add这时候的值是undefined,所以执行undefined();会报错。
函数的属性和对象
arguments
- 有传入函数中所有的参数,相当于可以传递不定长参数,主要用途是保存函数参数
- 示例
function test(){
for(i = 0;i < arguments.length;i++){
console.log(arguments[i]);
}
}
test("Tom","jerry","asl");//控制台输出:Tom jerry asl
test("Tom","jerry");//控制台输出:Tom jerry
callee
- 该属性是一个指针,指向拥有这个arguments对象的函数。
- 示例
function box(num) {
if (num <= 1) {
return 1;
} else {
return num * box(num-1); //一个简单的的递归
}
}
对于阶乘函数一般要用到递归算法,所以函数内部一定会调用自身;如果函数名不改变是没有问题的,但一旦改变函数名,内部的自身调用需要逐一修改。
function box(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num-1);//使用callee来执行自身
}
}
this
- this引用的是函数据以执行操作的对象,或者说函数调用语句所处的那个作用域;当在全局作用域中调用函数时,this对象引用的就是window。
- 示例
function add(x,y){
console.log(this);//window
}
add();
function add(x,y){
'use strict';
console.log(this);//undefined
}
add();//window
'this’可以用来判断当前是否是严格模式
var strict = (function(){
return !this;
}());
- 方法可以使用this访问自己所属的对象,所以它能从对象中取值或对对象进行修改。this到对象的绑定发生在调用的时候。通过this可取得它们所属对象的上下文的方法称为公共方法
var o = {
a: 1,
m: function(){
return this;
},
n: function(){
this.a = 2;
}
};
console.log(o.m().a);//1
o.n();
console.log(o.m().a);//2
- 因为函数调用模式的函数中的this绑定到全局对象,所以会发生全局属性被重写的现象
var a = 0;
function fn(){
this.a = 1;
}
fn();
console.log(this,this.a,a);//window 1 1
length
- length属性表示函数希望接收的命名参数的个数。
- 示例
function box(name, age) {
alert(name + age);
}
alert(box.length); //2
prototype(原型)
- prototype下有两个方法:apply()和call(),每个函数都包含这两个非继承而来的方法。这两个方法的用途都在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
- 示例
function box(num1, num2) {
return num1 + num2; //原函数
}
function sayBox(num1, num2) {
return box.apply(this, [num1, num2]); //this表示作用域,这里是window
} //[]表示box所需要的参数
function sayBox2(num1, num2) {
return box.apply(this, arguments); //arguments对象表示box所需要的参数
}
alert(sayBox(10,10)); //20
alert(sayBox2(10,10)); //20
- call()方法于apply()方法相同,他们的区别仅仅在于接收参数的方式不同。对于call()方法而言,第一个参数是作用域,没有变化,变化只是其余的参数都是直接传递给函数的。
function box(num1, num2) {
return num1 + num2;
}
function callBox(num1, num2) {
return box.call(this, num1, num2); //和apply区别在于后面的传参
}
alert(callBox(10,10));
传递参数并不是apply()和call()方法真正的用武之地;它们经常使用的地方是能够扩展函数赖以运行的作用域。
var color = '红色的'; //或者window.color = '红色的';也行
var box = {
color : '蓝色的'
};
function sayColor() {
alert(this.color);
}
sayColor(); //作用域在window
sayColor.call(this); //作用域在window
sayColor.call(window); //作用域在window
sayColor.call(box); //作用域在box,对象冒充
使用call()或者apply()来扩充作用域的最大好处,就是对象不需要与方法发生任何耦合关系(耦合,就是互相关联的意思,扩展和维护会发生连锁反应)。也就是说,box对象和sayColor()方法之间不会有多余的关联操作,比如 box.sayColor = sayColor;
函数的调用
函数调用
- 普通的函数调用来说,函数的返回值就是调用表达式的值
- 当一个函数并非一个对象的属性时,那么它就是被当做一个函数来调用的
function add(x,y){
return x+y;
}
var sum = add(3,4);
console.log(sum)//7
-
非严格模式下,this被绑定到全局对象;在严格模式下,this是undefined
-
this绑定到全局对象,所以会发生全局属性被重写的现象
方法调用
- 当一个函数被保存为对象的一个属性时,我们称它为一个方法。当一个方法被调用时,this被绑定到该对象。如果调用表达式包含一个提取属性的动作,那么它就是被当做一个方法来调用
var o = {
m: function(){
console.log(1);
}
};
o.m();//1
- 方法可以使用this访问自己所属的对象,所以它能从对象中取值或对对象进行修改。this到对象的绑定发生在调用的时候。通过this可取得它们所属对象的上下文的方法称为公共方法
var o = {
a: 1,
m: function(){
return this;
},
n: function(){
this.a = 2;
}
};
console.log(o.m().a);//1
o.n();
console.log(o.m().a);//2
- 如果想访问这个外部函数的this值,需要将this的值保存在一个变量里,这个变量和内部函数都同在一个作用域内。通常使用变量self或that来保存this
var o = {
m: function(){
var self = this;
console.log(this === o);//true
function n(){
console.log(this === o);//false
console.log(self === o);//true
return self;
}
return n();
}
}
console.log(o.m() === o);//true
构造器调用
- 如果函数或者方法调用之前带有关键字new,它就构成构造函数调用
function fn(){
this.a = 1;
};
var obj = new fn();
console.log(obj.a);//1
- 如果构造函数调用在圆括号内包含一组实参列表,先计算这些实参表达式,然后传入函数内
- 如果构造函数没有形参,javascript构造函数调用的语法是允许省略实参列表和圆括号的。凡是没有形参的构造函数调用都可以省略圆括号
- [注意]尽管构造函数看起来像一个方法调用,它依然会使用这个新对象作为调用上下文。也就是说,在表达式new o.m()中,调用上下文并不是o
- 构造函数通常不使用return关键字,它们通常初始化新对象,当构造函数的函数体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计算结果就是这个新对象的值
- 如果构造函数使用return语句但没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果
- 如果构造函数显式地使用return语句返回一个对象,那么调用表达式的值就是这个对象
间接调用
函数的注意点
1、函数是一种封装,要使用它需要调用,本身不会自动执行;
2、函数的定义顺序和调用顺序无关,函数优先于所有代码执行;
3、函数可以有参数也可以没有。函数的参数分为形参(函数定义时的参数)、实参(函数调用时的参数);函数的参数理论上有无限个,每个参数之间用逗号隔开,参数的类型不限。
4、函数可以有返回值也可以没有;函数的返回值可以用 (return +返回值;)返回;例如要返回变量a,可以:return a; 返回a变量;return语句不仅有返回结果的作用,还有结束函数的作用;注意,一个函数的返回值只有一个;finally语句除外
5、在JavaScript中没有重载函数。也就是说函数如果重名会被覆盖。
6、函数不能作比较。
7、函数可以作为参数、返回值。