js函数、函数参数全解(上)

  • js函数、函数参数全解(上)已关闭评论
  • 564 views
  • A+
所属分类:JS、JQuery 编程技术
【腾讯云】11.11 云上盛惠,云产品限时抢购,1核2G云服务器首年88元

函数定义

一、3种函数定义方式

【1】函数声明语句
使用function关键字,后跟一组参数以及函数体

function funcname([arg1 [,arg2 [...,argn]]]){
    statement;
}

【2】函数定义表达式

以表达式方式定义的函数,函数的名称是可选的

var functionName = function([arg1 [,arg2 [...,argn]]]){
    statement;
}

var functionName = function funcName([arg1 [,arg2 [...,argn]]]){
    statement;
}

 

匿名函数(anonymous function)也叫拉姆达函数,是function关键字后面没有标识符的函数

 

通常而言,以表达式方式定义函数时都不需要名称,这会让定义它们的代码更加紧凑。函数定义表达式特别适合用来定义那些只会使用一次的函数

var tensquared = (function(x) {return x*x;}(10));   //定义同时进行调用

 

而一个函数定义表达式包含名称,函数的局部作用域将会包含一个绑定到函数对象的名称。实际上,函数的名称将成为函数内部的一个局部变量

var test = function fn(){
   return fn;
}
console.log(test);//fn(){return fn;}
console.log(test());//fn(){return fn;}
console.log(test()());//fn(){return fn;}

 

个人理解,对于具名的函数表达式来说,函数名称相当于函数对象的形参,只能在函数内部使用;而变量名称相当于函数对象的实参,在函数内部和函数外部都可以使用

var test = function fn(){
   return fn === test;
}
console.log(test());//true
console.log(test === fn);//ReferenceError: fn is not defined

 

函数定义了一个非标准的name属性,通过这个属性可以访问到给定函数指定的名字,这个属性的值永远等于跟在function关键字后面的标识符,匿名函数的name属性为空

//IE11-浏览器无效,均输出undefined
//chrome在处理匿名函数的name属性时有问题,会显示函数表达式的名字
function fn(){};
console.log(fn.name);//'fn'
var fn = function(){};
console.log(fn.name);//'',在chrome浏览器中会显示'fn'
var fn = function abc(){};
console.log(fn.name);//'abc'

【3】Function构造函数

 

Function构造函数接收任意数量的参数,但最后一个参数始终都被看成是函数体,而前面的参数则枚举出了新函数的参数

var functionName = new Function(['arg1' [,'arg2' [...,'argn']]],'statement;');

[注意]Function构造函数无法指定函数名称,它创建的是一个匿名函数。

 

从技术上讲,这是一个函数表达式。但不推荐使用,因为这种语法会导致解析两次代码。第一次是解析常规javascript代码,第二次解析传入构造函数中的字符串,影响性能。

var sum = new Function('num1','num2','return num1 + num2');
//等价于
var sum = function(num1,num2){
    return num1+num2;
}

Function()构造函数创建的函数,其函数体的编译总是会在全局作用域中执行。于是,Function()构造函数类似于在全局作用域中执行的eval()

var test = 0;
function fn(){
    var test = 1;
    return new Function('return test');
}
console.log(fn()());//0

[注意]并不是所有的函数都可以成为构造函数

var o = new Math.min();//Uncaught TypeError: Math.min is not a constructor

二、函数声明顺序

函数声明,相对于变量会优先加载。所以不用担心函数声明在调用前还是调用后。

调用函数时会先在本机活动对象中查询,即当前js文件中查询,如果没有才会向上查询,所以若在两个js文件中定义相同函数名,这两个js文件内部调用各自的函数,其他js文件中调用最后声明的函数。

三、重复

变量的重复声明是无用的,不会覆盖之前同一作用域声明的变量,但函数的重复声明会覆盖前面的声明的同名函数或同名变量。

//变量的重复声明无用
var a = 1;
var a;
console.log(a);//1
//覆盖同名变量
var a;
function a(){
    console.log(1);
}
a();//1
//覆盖同名函数
a();//2
function a(){
    console.log(1);
}
function a(){
    console.log(2);
}

四、删除

函数声明语句创建的变量无法删除,这一点和变量声明一样。

function foo(){
    console.log(1);
}
delete foo;//false
console.log(foo());//1

函数返回值

所有函数都有返回值,没有return语句时,默认返回内容为undefined,和其他面向对象的编程语言一样,return语句不会阻止finally子句的执行。

function testFinnally(){
    try{
        return 2;
    }catch(error){
        return 1;
    }finally{
        return 0;
    }
}
testFinnally();//0

如果函数调用时在前面加上了new前缀,且返回值不是一个对象,则返回this(该新对象)。

function fn(){
    this.a = 2;
    return 1;
}
var test = new fn();
console.log(test);//{a:2}
console.log(test.constructor);//fn(){this.a = 2;return 1;}

如果返回值是一个对象,则返回该对象。

、
function fn(){
    this.a = 2;
    return {a:1};
}
var test = new fn();
console.log(test);//{a:1}
console.log(test.constructor);//Object() { [native code] }

函数调用

javascript一共有4种调用模式:函数调用模式、方法调用模式、构造器调用模式和间接调用模式。

【1】函数调用模式

当一个函数并非一个对象的属性时,那么它就是被当做一个函数来调用的。对于普通的函数调用来说,函数的返回值就是调用表达式的值。

function add(x,y){
    return x+y;
}
var sum = add(3,4);
console.log(sum)//7

使用函数调用模式调用函数时,非严格模式下,this被绑定到全局对象;在严格模式下,this是undefined

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绑定到全局对象,所以会发生全局属性被重写的现象

var a = 0;
function fn(){
    this.a = 1;
}
fn();
console.log(this,this.a,a);//window 1 1

【2】方法调用模式

当一个函数被保存为对象的一个属性时,我们称它为一个方法。当一个方法被调用时,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

任何函数只要作为方法调用实际上都会传入一个隐式的实参——这个实参是一个对象,方法调用的母体就是这个对象,通常来讲,基于那个对象的方法可以执行多种操作,方法调用的语法已经很清晰地表明了函数将基于一个对象进行操作

rect.setSize(width,height);
setRectSize(rect,width,height);

假设上面两行代码的功能完全一样,它们都作用于一个假定的对象rect。可以看出,第一行的方法调用语法非常清晰地表明这个函数执行的载体是rect对象,函数中的所有操作都将基于这个对象

和变量不同,关键字this没有作用域的限制,嵌套的函数不会从调用它的函数中继承this。如果嵌套函数作为方法调用,其this的值指向调用它的对象。如果嵌套函数作为函数调用,其this值不是全局对象(非严格模式下)就是undefined(严格模式下)

var o = {
    m: function(){
         function n(){
             return this;
         }
         return n();
    }
}
console.log(o.m());//window
var o = {
    m: function(){
         function n(){
             'use strict';
             return this;
         }
         return n();
    }
}
console.log(o.m());//undefined

 

如果想访问这个外部函数的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

【3】构造函数调用模式

如果函数或者方法调用之前带有关键字new,它就构成构造函数调用

function fn(){
    this.a = 1;
};
var obj = new fn();
console.log(obj.a);//1

如果构造函数调用在圆括号内包含一组实参列表,先计算这些实参表达式,然后传入函数内

function fn(x){
    this.a = x;
};
var obj = new fn(2);
console.log(obj.a);//2

如果构造函数没有形参,javascript构造函数调用的语法是允许省略实参列表和圆括号的。凡是没有形参的构造函数调用都可以省略圆括号

var o = new Object();
//等价于
var o = new Object;

[注意]尽管构造函数看起来像一个方法调用,它依然会使用这个新对象作为调用上下文。也就是说,在表达式new o.m()中,调用上下文并不是o

var o = {
    m: function(){
        return this;
    }
}
var obj = new o.m();
console.log(obj,obj === o);//{} false
console.log(obj.constructor === o.m);//true

构造函数通常不使用return关键字,它们通常初始化新对象,当构造函数的函数体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计算结果就是这个新对象的值

function fn(){
    this.a = 2;
}
var test = new fn();
console.log(test);//{a:2}

如果构造函数使用return语句但没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果

function fn(){
    this.a = 2;
    return;
}
var test = new fn();
console.log(test);//{a:2}

如果构造函数显式地使用return语句返回一个对象,那么调用表达式的值就是这个对象

var obj = {a:1};
function fn(){
    this.a = 2;
    return obj;
}
var test = new fn();
console.log(test);//{a:1}

【4】间接调用模式

javascript中函数也是对象,函数对象也可以包含方法。call()和apply()方法可以用来间接地调用函数。

这两个方法都允许显式指定调用所需的this值,也就是说,任何函数可以作为任何对象的方法来调用,哪怕这个函数不是那个对象的方法。两个方法都可以指定调用的实参。call()方法使用它自有的实参列表作为函数的实参,apply()方法则要求以数组的形式传入参数。

var obj = {};
function sum(x,y){
    return x+y;
}
console.log(sum.call(obj,1,2));//3
console.log(sum.apply(obj,[1,2]));//3
  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin