关于JavaScript中函数的学习:
MDN
阮一峰老师的教程
关于函数的定义:
阮一峰老师:函数是一段可以反复调用的代码块。函数还能接受输入的参数,不同的参数会返回不同的值。
MDN:Function 构造函数 创建一个新的Function对象。 在 JavaScript 中, 每个函数实际上都是一个Function对象。
函数的声明方式和name
函数的声明方式有目前来说有5种,每一个函数都有他的一个name属性(包括匿名函数),要注意。
- 具名函数
function fn(x,y) {
console.log(x + y)
}
fn.name //"fn"
- 匿名函数
var fn
f = function(x,y){
console.log(x + y)
}
f.name //"f"
- 具名函数赋值
var fn
fn = function f(x,y) {
console.log(x + y)
}
fn.name //"f"
f.name //f is not defined
- window.Function
var f = new Function('x','y','return x+y')
f.name // "anonymous"
- 箭头函数
var fn = (x,y) => {return x + y}
f.name //"fn"
现在最常用的就是具名函数和箭头函数。
函数的调用和call()
在声明了一个函数之后,我们就可以调用这个函数了。
但是,我们在调用一个函数的时候,究竟发生了什么,搞清楚其中的机制,可以帮助我们更深入地了解函数。
现梳理一下我的关于函数调用的思路:
首先我们声明了一个函数:
var fn = function(x,y){
retrun x + y
}
因为Function也是属于一种对象,所以这个变量就有了一个地址A,这个地址A就指向堆内存中的对象B,对象B中就存放着我们的函数参数与函数体。
如图所示:
在这个对象B中的__proto__指向了Function.prototype,在Function.prototype中有一个特殊的call()属性,这个方法对我们“调用函数”的这个动作来说至关重要。
每次调用函数时,其实就是使用这个call()属性来执行函数体中的内容。
由于JavaScript这门语言的历史遗留问题,fn.call()才是函数真正的调用方法。
注意
fn(1,2)等价于fn.call(undefined,1,2)
fn(1,2)相当于一种语法糖,但是call()方法能让学习者更好地理解关于函数中的this和arguments概念,所以建议在学习过程中函数调用使用call(),直到掌握this为止。
函数中的this和arguments
函数中的 this指的到底是什么?
先下结论:this指的就是call()中的第一个参数,第一个参数往后都是arguments。
对此,我的理解是,他指的是一种域,一种范围(可适当理解为函数的作用域),结合call()来看的话就很清晰明了了。
且看,我们声明了一个函数:
var fn = function(x,y){
console.log('x:'+x,'y:'+y)
console.log(this)
}
fn(1,2)
fn.call(undefined,1,2)
两者的运行结果都是:
两者的this都是一个window全局对象,我们来转变一下:
var a = 3
var obj = {
a: 4
}
var fn = function(x,y){
console.log('x:'+x,'y:'+y)
console.log(this.a)
}
fn(1,2)
fn.call(obj,1,2)
运行结果如下:
我们可以看到,因为我们在代码前面声明了一个赋值为3的全局对象a,第一个fn的调用中的this没有改变,依然是window全局对象,所以函数fn中的this.a找的是全局作用域中的a,也就是打印出了3。
我们还在代码前面声明了一个对象,其中包含了一个key a
,key的值为 4
,第二个fn调用的时候,我们把对象obj当成参数传给了fn,这个对象 obj就变成了fn的 this!!所以代码运行时,函数fn中的this.a找的就是对象obj中的a。
因为正常的函数调用fn()简化了传入this的一步,而fn.call()则需要把this作为参数传入,所以使用call()来学习this 是有好处的。
注意
普通模式与严格模式
在普通模式和严格模式下,this表现会有所不同:
// 严格模式
var fn = function(x,y){
'use strict'
console.log('x:'+x,'y:'+y)
console.log(this)
}
fn.call(undefined,1,2)
fn.call(1,1,2)
// 普通模式
var fn = function(x,y){
console.log('x:'+x,'y:'+y)
console.log(this)
}
fn.call(undefined,1,2)
fn.call(1,1,2)
在普通模式下,如果this是undefined,浏览器会自动把this变成Window;如果是其他的值,则会打出一个对象(比如说1,则会打出一个Number 1的对象);
严格模式下,如果this是undefined,他就是undefined,如果是其他的值,则会打出这个值本身。
arguments
arguments指的就是函数的参数。
使用arguments
可以打出一个伪数组,伪数组就是像数组,但是__proto__中没有Array.prototype的对象。
// 普通模式
var fn = function(x,y){
console.log('x:'+x,'y:'+y)
console.log(arguments)
}
fn.call(undefined,1,2)
下一篇博文会接着总结一下函数中的作用域和闭包。