js call和apply详解

call:改变调用者的this指向,this指向参数一

eg:

b中的this指向了a,且b执行。

b.call( a, param1,param2,param3··· ··· ) 
let fn = function() {console.log(this.name)}
let obj = {
    name: 'lxc'
}
fn.call(obj) // lxc

tips: 

fn.call : 当前实例(函数Fn)通过原型链的查找机制,找到 Function.prototype上的call方法 => funciton call(){ [native cade] }

几行代码实现call原理:

Function.prototype.myCall = function(o) {
  o.key = this;
  let r = o.key(...[...arguments].slice(1))
  delete o.key
  return r
}
// 可以这么理解:相当于在obj对象上执行a方法
// [...arguments].slice(1) 截取从参数2往后的参数,组成一个数组
// (...[]) 把数组再展开
let obj = {}
a.myCall(obj, 1,2,3)

 

apply:定义同上

eg:b.apply( a, [ param1,param2,param3··· ··· ] ) //即a对象调用b对象的方法。apply后边的参数是数组。

它们的共同点:

都可以用来代替另一个对象调用其方法,将一个函数的上下文从初始的上下文改变为thisObj指定的新对象,说白了就是改变函数的运行环境。

不同点:

参数列表不同

简单的继承:

function NameFn(name){
   this.name = name;
   this.showName = function(){
     alert(this.name)
   }
}
function Inherit(){
   NameFn.apply(this,["小辰"])
}
var a = new Inherit();
a.showName()//小辰

上边代码:在构造函数Inherit里边,用apply把this指向NameFn,也就是在Inhert里边调用NameFn里边的方法。

多重继承:

function A(name){
  this.name = function(){
    console.log(name);
  }
}
function B(age){
  this.age = function(){
    console.log(age);
  }
}
function C(){
  A.call(this,"星辰");
  B.call(this,"20");
}
var d = new C();
d.name() //星辰
d.age() //20

参数:

如果参数为空、null、undefined,则默认传入全局对象;

var name = "小辰";
var obj = {
    name:"大辰"
}
function a(){
	return this.name;
}
var test = a.call() // 等于 a.call(this)
var test1 = a.call(obj)
console.log(test);//小辰
console.log(test1) //大辰

一个经常看到的例子:

var myArr = [1,2,3,4,5]

var maxNumber = Math.max.apply(null,myArr) // 等于Math.max.apply(this,myArr)
console.log(maxNumber)//5

求数组最大值,Math.max方法的参数不支持数组形式,而用aplly可实现传递数组参数,之所以用apply而不用call,call的参数语法规定不可以传数组;参数为null的意思是:没有对象去调用这个方法,我只需要用这个方法帮我运算,得到返回的结果就行,所以直接传递了一个null过去。

实现数组合并:

var arr1 = [1,2,3];
var arr2 = [4,5,6];
// 用数组push方法向数组arr1插入 4  5  6
var arr = [].push.apply(arr1,arr2);
console.log(arr);//6   push返回的push结束时数组的长度
console.log(arr1)//[1,2,3,4,5,6]

利用apply让数组的空元素变成undefined,当我们用forEach来遍历数组时,如果数组有空元素,forEach会忽略空元素,如果是undefined,forEach会遍历出来,最后结果不同。

var arr1 = [1, ,3];
Array.apply(null,arr1).forEach((ele,index,self)=>{
	console.log(ele);//1  undefined  3
})

bind跟call和apply一样,把函数内部的this绑定到指定的对象中去。

var age = 10;
var obj = {
	age:20,
	showAge:function(){
		console.log(this.age);
	}
} 
var getAge = obj.showAge;
getAge();//10

上边代码由于getAge执行相当于window.getAge(),所以this指向全局对象window,打印10,可以通过bind把this绑定到对象obj中去,让this指向obj;

var age = 10;
var obj = {
	age:20,
	showAge:function(){
		console.log(this.age);
	}
} 
var getAge = obj.showAge.bind(obj);
getAge();//20

 

补充:

小例子:

1、call执行原理:

function fn() {
    console.log(this.name)
}
const obj = {
    name: 'lxc'
}
fn.call(obj)
// 解释
/**
    第一步永远都是最后的call先执行
* 1、call执行,然后call中的this
* 2、关键字(这里的this关键字就是前边的fn) 修改为 call第一个参数 obj;
* 3、fn执行,实际上就是call中的this执行,因为是fn调用的call,所以call中的this为fn;
* 4、同时把第二个以及以后接受的参数传递给函数fn 。。。
*
* */

2、下边执行输出结果:

function fn1() {
    console.log('1')
}
function fn2() {
    console.log('2')
}
// call执行,call中this(fn1)指向fn2,fn1执行,结果输出1
const a = fn1.call(fn2) // 1
// 最后一个call先执行,call中this(也就是fn1.call)变为fn2,接着fn1.call执行,实际上,执行
// 的也是call,里边this是fn2,没传参数,所以this(fn2)中的this为undefined,最后执行的是call中的
// this,也就是fn2执行 
const b = fn1.call.call(fn2) // 2
// call先执行,Function.prototype中this变为fn1,Function.prototype执行 -> ƒ () { [native code] }
// 空函数执行还是一个空函数
const c = Function.prototype.call(fn1)
// call先执行,把Function.prototype.call 中的this变为fn1,接着Function.prototype.call执行,
// 实际是Function.prototype.call中的call执行,但是此时里边的this为fn1了,所以是fn1执行。
const d = Function.prototype.call.call(fn1) // 1

3、call细节:

非严格模式下,call中的第一个参数传的是null、undefined 或 不传值,this指向window;

严格模式下,传什么是什么,包括null和undefined,不传值,this为undefined。

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值