函数定义的三种方法,函数调用的四种模式

函数进阶

定义函数的三种方式

函数声明

1
2
3
4
fn();// 函数声明可以先调用,在声明
function fn(){
console.log("这是函数声明")
}

函数表达式

1
2
3
4
var fn = function() {
console.log("这是函数表达式");
}
fn();//函数表达式必须先声明,再调用

构造函数Function

1
2
3
4
5
6
7
8
9
10
11
12
//函数也是对象,可以使用Function构造函数new出来
//相当于var fn = function(){}
var fn = new Function();

//语法:new Function(arg1,arg2,arg3..,body);
// 1. 所有的参数都是字符串类型。
// 2. 前面可以定义任意多个形参,最后一个参数是代码体。
var fn = new Function("alert(1111)");
fn();

var fn1 = new Function("a1", "a2", "alert(a1+a2)");
fn1(1,2);

eval函数

eval的可以和new Function一样,执行字符串代码

注意:eval函数的功能非常的强大,但是实际使用的情况并不多。

  • eval形式的代码难以阅读

  • eval形式的代码无法打断点,因为本质还是还是一个字符串

  • 在浏览器端执行任意的 JavaScript会带来潜在的安全风险,恶意的JavaScript代码可能会破坏应用,特别是在用它执行用户输入数据的情况下。否则,可能会有恶意用户输入威胁你的站点或应用程序安全的代码(即所谓的代码注入)

    了解: xss攻击

函数的四种调用模式

根据函数内部this的指向不同,可以将函数的调用模式分成4种

  1. 函数调用模式
  2. 方法调用模式
  3. 构造函数调用模式
  4. 上下文调用模式(借用方法模式)
1
2
函数:当一个函数不是一个对象的属性时,称之为函数。
方法:当一个函数被保存为对象的一个属性时,称之为方法。

函数调用模式

如果一个函数不是一个对象的属性时,就是被当做一个函数来进行调用的。此时this指向了window

1
2
3
4
function fn(){
console.log(this);//指向window
}
fn();

方法调用模式

当一个函数被保存为对象的一个属性时,称之为一个方法。当一个方法被调用时,this被绑定到当前对象上。

1
2
3
4
5
6
var obj = {
sayHi:function(){
console.log(this);// 在方法调用模式中,this指向调用当前方法的对象。
}
}
obj.sayHi();

构造函数调用模式

如果函数是通过new关键字进行调用的,此时this被绑定到创建出来的新对象上。

1
2
3
4
5
function Person(){
console.log(this);
}
Person();//this指向什么?
var p = new Person();//this指向什么?

总结:分析this的问题,主要就是区分函数的调用模式,看函数是怎么被调用的。

几种特殊的this指向

  • 定时器中的this指向了window,因为定时器的function最终是由window来调用的。**可以使用箭头函数来避免这一问题 **
  • 事件中的this指向的是当前的元素,在事件触发的时候,浏览器让当前元素调用了function。多数情况不能使用箭头函数

上下文调用模式

上下文调用模式也叫方法借用模式,分为apply与call

使用方法: 函数.call() 或者 函数.apply()

call方法

call方法可以调用一个函数,并且可以指定这个函数的this指向

1
2
3
4
5
// 所有的函数都可以使用call进行调用
// 参数1:指定函数的this,如果不传,则this指向window
// 其余参数:和函数的参数列表一模一样。
// 说白了,call方法也可以和()一样,进行函数调用,call方法的第一个参数可以指定函数内部的this指向。
fn.call(thisArg, arg1, arg2, arg2);
  • 借用对象的方法

伪数组与数组

伪数组也叫类数组

  1. 伪数组其实就是一个对象,但是跟数组一样,伪数组也会有length属性,也有0,1,2,3等属性。
  2. 伪数组并没有数组的方法,不能使用push/pop等方法
  3. 伪数组可以跟数组一样进行遍历,通过下标操作。
  4. 常见的伪数组:argumentsdocument.getElementsByTagName的返回值jQuery对象
1
2
3
4
5
6
7
var arrayLike = {
0:"张三",
1:"李四",
2:"王五",
length:3
}
// 伪数组可以和数组一样进行遍历
  • 伪数组借用数组的方法
1
Array.prototype.push.call(arrLike, "赵六");
  • 将伪数组转换成真数组
1
var arr = Array.prototype.slice.call(arrLike);

apply方法

apply()方法的作用和 call()方法类似,只有一个区别,就是apply()方法接受的是一个包含多个参数的数组。而call()方法接受的是若干个参数的列表

call和apply的使用场景:

  • 如果参数比较少,使用call会更加简洁
  • 如果参数存放在数组中,此时需要使用apply

bind方法

**bind()**方法创建一个新的函数, 可以绑定新的函数的this指向

1
2
3
4
// 语法:函数.bind(thisArg)
// 返回值:新的函数
// 参数:新函数的this指向,当绑定了新函数的this指向后,无论使用何种调用模式,this都不会改变。
var newFn = fn.bind(window);