JS对象类型-函数篇-函数概述
定义函数
定义函数的方式有三种:函数声明语句、函数表达式和Function构造函数
函数声明语句
function functionName([arg1 [,arg2 [...,argn]]]) {
statemant
}
functionName指要声明函数的名称(标识符),圆括号中是参数列表,参数之间用逗号分隔。当调用函数时,这些形参(参数列表)会被替换成实参(传入的参数)。
【声明提升】:函数声明语句定义的函数有个特点,就是函数体和函数名称会提升。
foo()
function foo(){
console.log(1)
}
// 1
由于函数声明提升,上面的函数正常执行,相当于下面代码:
function foo(){
console.log(1)
}
foo()
【重复】:如果两个函数重名了,那么后声明的函数会覆盖前声明的函数。
function b(){
console.log(1)
}
function b(){
console.log(2)
}
b() // 2
// 示例1
var a;
function a(){
console.log(1)
}
a() // 1
// 示例2
var a = 2;
function a(){
console.log(1)
}
console.log(a) // 2
// 示例3
var a = 2;
var a = 3;
console.log(a) // 3
// 示例4
var a = 2;
var a;
console.log(a) // 2
个人理解:如果变量只声明未初始化,则它的权重很低,重名函数或者已赋值重名变量都可以覆盖它。如果变量初始化了,那么它和重名的函数或变量权重一样,后面的会覆盖前面的。
【删除】:函数声明语句创建的变量无法删除
function a(){
console.log(1)
}
delete a
a() // 1
函数表达式
var funcName = function([arg1 [,arg2 [...,argn]]]){
statement;
}
var funcName = function functionName([arg1 [,arg2 [...,argn]]]){
statement;
}
函数表达式定义的函数,函数名字是可选的。如果没有函数名字就是匿名函数(也叫拉姆达函数)。
var a = function(){
console.log(1)
}
a() // 1
当函数有名字时,函数的名字会成为一个函数内部的局部变量,这个名字相当于函数对象的形参。
var a = function foo(){
return foo
}
console.log(a) // foo(){ return foo }
console.log(a()) // foo(){ return foo }
console.log(a()()) // foo(){ return foo }
Function构造函数
var functionName = new Function(['arg1' [,'arg2' [...,'argn']]],'statement;');
类似于函数表达式,但它只能创建匿名函数。不推荐使用这种方式定义函数,因为它会解析两次代码,影响性能。
var sum = new Function('num1','num2','return num1 + num2');
//等价于
var sum = function(num1,num2){
return num1+num2;
}
函数返回值
函数中的return语句用来定义函数的返回值。
return expression;
如果函数体中没有定义return语句,或者return语句没有expression,函数返回undefined。
var a = function(){
return;
}
console.log(a()) // undefined
如果函数调用时在前面加了new关键字,且返回值不是一个对象,则返回this。
// 示例1
function foo(){
this.name = 'hello'
return 1;
}
var test = new foo()
console.log(test) // {name: 'hello'}
// 示例2
function foo(){
this.name = 'hello'
return {a: 1};
}
var test = new foo()
console.log(test) // {a: 1}
函数调用
函数调用有四种方式:函数调用模式、方法调用模式、构造器调用模式和间接调用模式
函数调用模式
函数调用模式在非严格模式下,this指向window对象。严格模式下this是undefined。
// 示例1
function add(x,y){
console.log(this);//window
}
add();
// 示例2
function add(x,y){
'use strict';
console.log(this);//undefined
}
add();
可以根据this值判断当前是否是严格模式
var isStrict = (function(){return !this;}());
由于this指向全局对象,所以全局属性可能被重写
var a = 1;
function b() {
this.a = 2
}
b()
console.log(a, this.a) // 2 2
方法调用模式
当一个函数作为对象的属性时,这个函数就是该对象的方法。方法被调用时,this绑定到该对象,所以可以通过this修改对象的属性和方法。注意是方法调用时this才绑定到该对象。
var o = {
a: 1,
b: function(){
return this
},
c: function(){
this.a = 2
}
}
console.log(o.b().a) // 1
o.c()
console.log(o.b().a) // 2
关键字this没有作用域限制,所以嵌套的函数不会从调用他的函数中继承this。如果嵌套函数作为方法调用,则this执行调用他的函数。如果嵌套函数作为函数调用,则this指向window(非严格模式)对象或是undefined(严格模式)
// 示例1
var o = {
a: function(){
// 嵌套函数
function b(){
return this
}
return b()
}
}
console.log(o.a()) // window
// 示例2
'use strict'
var o = {
a: function(){
// 嵌套函数
function b(){
return this
}
return b()
}
}
console.log(o.a()) // undefined
如果想要把嵌套函数的this绑定到外部函数,可以把this保存一个临时变量。
var o = {
a: function(){
var self = this
// 嵌套函数
function b(){
return self
}
return b()
}
}
console.log(o.a() === o) // true
构造函数调用模式
如果函数或者方法使用new关键字进行调用,就成为了构造函数调用。
// 示例1
function fn(x) {
this.a = x
}
var test = new fn('good')
console.log(test.a) // 'good'
// 示例2 没有形参的构造函数可以省略圆括号
function fn() {
this.a = 'good'
}
var test = new fn
console.log(test.a) // 'good'
当使用构造函数方式调用对象的方法时,对象中的this指向新对象的上下文。
var o = {
name: 'good',
a: function(){
return this.name;
}
}
var obj = new o.a();
// this指向obj而不是o,obj没有name属性,所以结果是空对象
console.log(obj, obj === o) // {} false
构造函数通常不适用return语句,当构造函数执行完毕后,会把新对象作为调用结果返回。
如果构造函数使用return语句但是没有指定返回值,或者返回一个原始值,那么返回值会被忽略,会把新对象作为调用结果返回。
如果构造函数使用return语句返回一个对象,那么会把这个对象作为调用结果返回。
// 示例1
function fn(){
this.a = 1
}
var test = new fn()
console.log(test) // {a: 1}
// 示例2
function fn(){
this.a = 1
return 'hello'
}
var test = new fn()
console.log(test) // {a: 1}
// 示例3
var obj = {name: 'hello'}
function fn(){
this.a = 1
return obj
}
var test = new fn()
console.log(test) // {name: 'hello'}
间接调用模式
JS中的函数也是对象,函数对象中的方法可以通过call()
和apply()
间接调用,这两个方法可以显式的改变this指向,所以任何函数都可以作为任何对象的方法来调用,即使这个函数不是该对象的方法。call()方法和apply()方法有些差别: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