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。