函数定义
函数使用 function 关键字来定义,有 函数定义表达式
和 函数声明语句
两种方式。
函数定义表达式
var fun = function () {
// some code ...
}
函数声明语句
function fun () {
// some code ...
}
// 本质:声明了一个变量 fun,并把函数对象赋值给 fun。
如果函数定义表达式中存在函数名称标识符,那么该标识符只存在于函数体内中,并指代该函数对象本身。
var fun = function f () {
console.log(f);
}
fun();
console.log(f);
如果一个函数定义表达式包含名称,那么该函数的局部作用域将会包含一个绑定到函数对象的名称。
函数声明语句会被提前到外部脚本或外部函数作用域的顶部。(“变量提升”)
fun();
function fun () {
console.log('fun:我可以在定义之前被调用...');
}
// 相当于
function fun () {
console.log('fun:我可以在定义之前被调用...');
}
fun();
上面代码中,变量
fun
用function
命令声明,会发生变量提升,即脚本开始运行时,函数声明语句就已经存在了。所以可以在被定义之前所调用。
函数定义表达式虽然也存在变量提升,但无法被提前调用。
fun();
var fun = function () {
console.log('fun:我不可以在定义之前被调用...');
}
// 相当于
var fun;
fun(); // 此时 fun 是 undefined,并不是一个函数。
fun = function () {
console.log('fun:我不可以在定义之前被调用...');
}
上面代码中,变量
fun
用var
命令声明,虽然会发生变量提升,即脚本开始运行时,变量fun
已存在,但是此时还不是一个函数,所以会报错。
函数中的 return
语句。
function fun1 () {
return '123';
}
function fun2 () {
return;
}
function fun3 (x, y) {
let result = x + y;
}
console.log(fun2());
console.log(fun3(1, 2));
如果
return
语句有与之相关的表达式,会返回表达式的值给调用者。如果没有与之相关的表达式,则返回
undefined
值。如果一个函数不包含
return
语句,那会返回undefined
值。
函数的调用
有4种方式来调用JS函数:
+ 作为函数
+ 作为方法
+ 作为构造函数
+ 通过它们的 call() 与 apply() 方法间接调用
作为函数调用
function fun (x, y) {
return x + y;
}
fun(1, 2);
根据 ECMAScript 3 和非严格的 ECMAScript 5 对函数调用的规定,调用上下文(
this
的值)是全局对象。然而,在严格模式下,调用上下文则是undefined
。
可以通过下列方法判断当前是否严格模式。
var strict = (function() { return !this })();
作为方法调用
// 如果一个函数 fun 是一个对象 obj 的方法,则通过如下方式调用
obj.fun();
此时,
fun
函数被当做一个方法,而不是作为一个普通函数来调用。
在方法调用表达式里,对象 obj
成为函数的调用上下文,函数体可以使用关键字 this
引用该对象。
var obj = {
add: function () {
console.log(this)
}
}
obj.add();
上面代码中,方法
add
体内的this
指向的是对象obj
的本身。任何函数只要作为方法调用实际上都会传入一个隐式的实参—这个实参是一个对象,方法调用的母体(如
obj
)就是这个对象。
作为构造函数调用
如果函数或方法调用之前带有关键字 new
,它就构成构造函数调用。构造函数调用和普通的函数调用以及方法调用在实参处理、调用上下文和返回值方面都有不同。
实参处理:
如果构造函数有实参,先计算实参表达式,然后传入函数内,这和普通函数调用时一致的。但如果构造函数没有形参,调用时都可以省略圆括号,如下,下面两行代码是等价的:
var obj1 = new Object();
var obj2 = new Object;
console.log(obj1);
console.log(obj2);
调用上下文:
构造函数调用创建一个新的空对象,这个对象继承自构造函数的
prototype
属性。并将这个对象用作上下文,因此构造函数可以使用this
关键字来引用这个新创建的对象。
// 情况一
function Obj() {
this.name = 'cez',
this.fun = function() {
console.log(this);
}
}
var obj = new Obj();
obj.fun();
// 情况二
var o = {
Obj: function () {
this.name = 'zlz';
this.getThis = function () {
console.log(this);
console.log(this == o);
}
}
}
var obj = new o.Obj();
obj.getThis();
上面代码中,构造函数体内的
this
指向的是新创建的对象。也就是说,在表达式new o.Obj()
中,调用上下文并不是o
。
返回值:
构造函数通常不使用
return
关键字,它们通常初始化新对象,当构造函数的函数体执行完毕时,它会显示返回。如果构造函数显示的使用return
语句返回一个对象,那么调用表达式的值就是这个对象。如果构造函数使用return
语句但没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果。
// 没有 return 语句
function Obj() {
this.name = 'cez';
}
var obj = new Obj();
console.log(obj);
// return 语句返回一个对象
function Obj() {
this.name = 'cez';
return { name: 'zlz' };
}
var obj = new Obj();
console.log(obj);
// return 语句没有返回值
function Obj() {
this.name = 'cez';
return;
}
var obj = new Obj();
console.log(obj);
// return 语句返回一个原始值
function Obj() {
this.name = 'cez';
return 'zlz';
}
var obj = new Obj();
console.log(obj);
没有 return 语句:
return 语句返回一个对象:
return 语句没有返回值:
return 语句返回一个原始值:
间接调用
JS 中的函数也是对象,函数对象也可以包含方法。其中的两个方法
call()
和apply()
可以用来间接地调用函数。两个方法都允许指定调用所需的this
值,也就是说,任何函数可以作为任何对象的方法来调用,哪怕这个函数不是那个对象的方法。
var obj = {
name: 'cez'
}
function fun() {
console.log(this);
}
fun();
fun.call(obj);
fun.apply(obj);
上面代码中,正常情况下,函数体内的
this
指向的是全局对象(Window),在使用call
和apply
方法时可以显示的指定this
的值。
函数可以作为任何对象的方法来调用,哪怕这个函数不是那个对象的方法。
var obj = {
name: 'cez'
}
function fun() {
console.log(this);
}
fun();
fun.call(obj);
fun.apply(obj);
上面代码中,正常情况下,函数体内的
this
指向的是全局对象(Window),在使用call
和apply
方法时可以显示的指定this
的值。