函数调用模式
函数有 4 种调用模式:
- 方法调用模式(对象)
- 函数调用模式(window 对象)
- 构造器调用模式(new)
- 上下文模式
了解函数调用模式的不同,对于我们理解函数内部 this 指向有着重要的作用
方法调用模式与函数调用模式
demo1:
var age = 38
var obj = {
age: 18,
getAge: function() {
return this.age
}
}
console.log(obj.getAge()) //18 方法调用模式
var getAge = obj.getAge
console.log(getAge()) //38 函数调用模式
demo2:
var age = 38
var obj = {
age: 18,
getAge: function() {
//方法调用 this指向调用对象
console(this.age)
function foo() {
console.log(this.age)
}
foo() //函数调用,this指向window对象(非严格模式)
}
}
obj.getAge() // 18 38
demo3:
var length = 10
function fn() {
console.log(this.length)
}
var obj = {
length: 5,
method: function(fn) {
fn() //函数调用 10
arguments[0]() // 方法调用 arguments[0] => arguments.fn this 指向 arguments length为2
}
}
obj.method(fn, 123) //10 2
相当于 arguments 调用了 fn 返回 arguments 的长度
arguments 是一个包含很多属性的对象,键名为索引,键值为传进来的参数值
获取对象的属性值有两种方法:点方法与中括号法 obj.name === obj['name']
arguments[0] === arguments.0(并不能这样写)
调用 arguments.0()==arguments[0]() 实际上是方法调用
理解不了看下面:
var obj = { abc: fn }
obj.abc() //方法调用
obj['abc']() //方法调用
构造器调用模式
demo4:
function Person(name, age) {
this.name = name
this.age = age
}
var p = new Person('zzw', '18')
new 关键字就做了一点微小的工作
var obj={};
obj.\_\_proto\_\_=Person.prototype;
Person.call(obj);//通过call方法强行将构造函数内部的this指向了obj
上下文模式
首先来看代码:
var p1 = {
name: '赵志文',
age: '18'
}
var name = '周娇娇'
function getName() {
console.log(this.name, this)
}
getName() //周娇娇 window //函数调用,this指向window(非严格模式)
getName.call(p1) //赵志文 Object //改变了getName内部的this指向,指向p1
getName.apply(p1) //同上
相信各位读者通过上例能够很轻易的知道 call 与 apply 的作用:改变调用函数内部的 this 指向.
使用 call 与 apply,可以修改函数调用上下文,也就是 this 的值,这两个方法都是定义在 Function.prototype 中,所有函数都可以调用
Function.prototype.apply/call
MDN 文档中写道:"apply()方法在指定 this 值和参数(参数以数组或类数组形式存在)的情况下调用某个函数."
apply()方法与 call()方法仅有的区别:call()方法接收的是一个参数列表,而 apply()方法接收的是一个包含多个参数的数组(或类数组对象);
语法:
fun.apply(thisArg [,argsArray]);
fun.call(thisArg,para1,para2,...);
例子
var name = 'zzw'
function test(a, b) {
console.log(this.name + '吃了' + (a + b) + '个西瓜')
}
test(1, 2) // 函数调用模式 非严格模式下指向window zzw吃了3个西瓜
var obj = {
name: 'zjj'
}
var obj1 = {
name: 'ruizhi'
}
test.call(obj, 4, 5) // 改变this指向为obj同时传参 zjj吃了9个西瓜
test.apply(obj1, [100, 99]) // 改变this指向为obj同时传参 ruizhi吃了199个西瓜
举例
- 求一个数组中的最大值
js 中 Math 对象有个方法 max,求出参数中的最大值
但是往往给定的是数组 ,怎么办捏
使用 apply,这里就不改变 this 了,单纯数组转参数
知识点:apply 与 call 第一个参数为 null 时,单纯调用函数,着眼于参数类型(例如给定数组,通过 apply 数组转参数列表)
var arr = [1, 20, 157, 76, 84]
var max = Math.max.apply(null, arr)
console.log(max)
- 将传入函数的参数打印出来,用'-'连接
法一:自己的傻瓜拼接字符串法
function demo4() {
var res = ''
for (var j = 0; j < arguments.length; j++) {
if (j < arguments.length - 1) {
res += arguments[j] + '-'
} else {
res += arguments[j]
}
}
console.log(res)
}
demo4(1, 2, 3, 'abc', 'zzw') //1-2-3-abc-zzw
法二:可以重新创建一个数组容纳参数,调用 join 方法
function demo4() {
var arr = []
for (var i = arguments.length - 1; i >= 0; i--) {
arr.unshift(arguments[i])
}
arr = arr.join('-')
console.log(arr)
}
demo4(1, 2, 3, 'abc', 'zzw') //1-2-3-abc-zzw
法三:直接借用 join 方法,改变 join 的内部 this 指向
function demo4() {
//return arguments.join("-");//报错 伪数组并非数组 不可以使用join
var str = Array.prototype.join.call(arguments, '-') //改变join内部的this
console.log(str)
//或写成 var str = [].join.call(arguments,"-");
}
demo4(1, 2, 3, 'abc', 'zzw') //1-2-3-abc-zzw
this 与构造函数
在学习构造函数的过程中肯定会思考构造函数中的 this 到底是什么呢?
function Person(name, job) {
//默认隐含的操作,将new创建的对象赋值给this
this.name = name
this.job = job
this.sayHello = function() {
console.log('hello')
}
}
var winter = new Person('winter', 'coding')
构造函数的执行过程:
- 使用 new 关键字创建对象;
- 调用构造函数,将新创建出来的对象赋值给对象函数内部的 this;//如何做到?
- 在构造函数内部使用 this 为新创建的对象新增成员;
- 默认返回新创建的对象,普通函数如果不写返回语句,会返回 undefined.
new 关键字就做了一点微小的工作
function new(F){
return function () {
var args = arguments;
var obj = {}
obj.__proto__ = F.prototype
F.call(obj,args)
return obj
}
}
此处也是 call 与 apply 的一个典型用法.