js中call,apply,bind方法的简单总结
一、作用
- call、apply、bind作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this指向
- call、apply、bind是Function.prototype下的方法,都是用于改变函数运行时上下文,最终的返回值是你调用的方法的返回值,若该方法没有返回值,则返回undefined。
二、区别
最主要的区别是call()、apply()方法是立即调用当前函数,而bind()是返回一个改变了this指向的新函数,并不立即调用。
call(),bind()的参数是依次传参,一一对应传递的。
apply() 需要把多个参数放在一个数组中,作为第二个参数传递
三、说明
(一)、call()
用法: call(newThis,arg1,arg2。。。)
call 的第一个参数就是 this 所要指向的那个对象,后面的参数则是函数调用时所需的参数。
1、 newThis包括以下类型:
(1) 传null,undefined或不传, 函数中的this指向window对象。
(2) 传递另一个函数的函数名,函数中的this指向这个函数的引用,则就指向函数体。
(3) 传递字符串、数值或布尔类型等基础类型,函数中的this指向其对应的包装对象,如 String、Number、Boolean等。
(4) 传递一个对象,函数中的this指向这个对象。
2、args是将会传入被绑定函数的参数,被绑定函数的执行时参数顺序为:newThis,args,原参数
列子:
var fan = {
user:"dragon",
fun:function(age,sex){
console.log(this.user); //dragon
console.log(this.user,age,sex) //dragon 男 学习
}
}
var b = fan.fun;
b.call(fan,'男','学习');
b.call(fan); //传递函数名. this指向这个函数的引用
function fan1(){
console.log(this); //输出函数a中的this对象
}
function fan2(){}
var fan3={name:"dragon"};//定义对象fan3
fan1.call(); //window 不传, 函数中的this指向window对象。
fan1.call(null); //window 传null, 函数中的this指向window对象。
fan1.call(undefined); //window 传undefined, 函数中的this指向window对象。
fan1.call(1); //Number Number {1} 传递数值
fan1.call(''); //String String {''} 传递字符串
fan1.call(true); //Boolean Boolean {true} 传递布尔
fan1.call(fan2); //function fan2(){} //传递函数名. this指向这个函数的引用但无引用就指向函数体。
fan1.call(fan3); //Object {name: 'dragon'}
call实现分析:
// 传参默认是指向window
Function.prototype.myCall = function(context = window){
context.fan = this; //为对象添加方法(this指向调用myCall的函数)
let args = [...arguments].slice(1); // 剩余的参数
let res = context.fan(...args); // 调用该方法,该方法this指向context
delete context.fan; //删除添加的方法
return res;
}
还有就是对象继承中的组合继承
//临时中转函数
function obj(o) {
function F() {
}
F.prototype = o;
return new F();
}
//寄生函数
function create(subType, superType){
// var protoType = Object.create(superType.prototype); //创建对象
var protoType = obj(superType.prototype); //创建对象
protoType.constructor = subType; //增强对象
subType.prototype = protoType; //指定对象
}
function Sup(name, age) {
this.name = name;
this.age = age;
}
Sup.prototype.run = function () {
return this.name + " " + this.age + " running..."
}
function Sub(name, age) {
Sup.call(this, name);
this.age = age;
}
create(Sub,Sup); //替代D.prototype=new B();
Sub.prototype.sayAge = function(){
return this.name + " " + this.age + " Sub..."
}
var d= new Sub('dragon',100);
console.log(d.run()); //dragon 100 running...
console.log(d.sayAge()); //dragon 100 Sub...
(二)、apply()
用法;apply(newThis,[arrArg])
apply和call类似,它接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入。
var fan = {
user:"dragon",
fun:function(age,sex){
console.log(this.user); //dragon
console.log(this.user,age,sex) //dragon 男 学习
}
}
var b = fan.fun;
b.apply(fan,['男','学习']);
b.apply(fan); //传递函数名. this指向这个函数的引用
function fan1(){
console.log(this); //输出函数a中的this对象
}
function fan2(){}
var fan3={name:"dragon"};//定义对象fan3
fan1.apply(); //window 不传, 函数中的this指向window对象。
fan1.apply(null); //window 传null, 函数中的this指向window对象。
fan1.apply(undefined); //window 传undefined, 函数中的this指向window对象。
fan1.apply(1); //Number Number {1} 传递数值
fan1.apply(''); //String String {''} 传递字符串
fan1.apply(true); //Boolean Boolean {true} 传递布尔
fan1.apply(fan2); //function fan2(){} //传递函数名. this指向这个函数的引用但无引用就指向函数体。
fan1.apply(fan3); //Object {name: 'dragon'}
apply实现分析:
// 传参默认是指向window
Function.prototype.myApply = function(context = window){
context.fan = this; //为对象添加方法(this指向调用myCall的函数)
let res;
if(arguments[1]){ //判断是否有第二个参数
res = context.fan(...arguments[1]);// 调用该方法,该方法this指向context
}else{
res = context.fan(); // 调用该方法,该方法this指向context
}
delete context.fan; //删除添加的方法
return res;
}
(三)、结合call、apply操作数组或对象
1、找出数组的最大值、最小值
var dragon = [11, 21, 4, 55, 19,88,22,33];
Math.max.apply(null, dragon) // 88 最大值
Math.max.call(null, ...dragon) // 88 最大值
Math.min.apply(null, dragon) // 4 最小值
Math.min.call(null, ...dragon) // 4 最小值
2、转换类似数组的对象
Array.prototype.slice.apply({0: 66,1: 88, length: 2}) // [66,88]
Array.prototype.slice.call({0: 66,1: 88, length: 2}) // [66,88]
Array.prototype.slice.apply({0: 66,1: 88, length: 3}) // [66, 88, undefined]
Array.prototype.slice.call({0: 66,1: 88, length: 3}) // [66, 88, undefined]
Array.prototype.slice.apply({0: 66}) // []
Array.prototype.slice.call({0: 66}) // []
为什么通过 Array.prototype.slice.call(arrayLike) 可以转换类数组为数组?
大概具体是这样的:
Array.prototype.mySlice = function(start=0, end) {
const array = this;
const end = end === undefined ? array.length : end;
const resultArray = [];
if (array.length === 0) return resultArray;
for (let index = start; index < end; index++) {
resultArray.push(array[index]);
}
return resultArray;
}
3、将数组的空元素变为 undefined
Array.apply(null, ['aaa', ,'bbb']); // [ 'aaa', undefined, 'bbb' ]
Array.call(null, ...['aaa', ,'bbb']); // [ 'aaa', undefined, 'bbb' ]
4、合并数组
var arr1=new Array("1","2","3","4","5");
var arr2=new Array("6","7","8","9","10");
Array.prototype.push.apply(arr1,arr2);
console.log(arr1) //['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
(四)bind()
bind()函数会创建一个新的绑定函数,这个绑定函数包装了原函数的对象。调用绑定函数通常会执行包装函数。
绑定函数内部属性:
1 、包装的函数对象
2、在调用包装函数时始终作为this传递的值
3、在对包装函数做任何调用时都会优先用列表元素填充参数列表。
var user="winDragon";
var fan = {
user:"dragon",
fun:function(age,sex){
console.log(this.user); //dragon
console.log(this.user,age,sex)
}
}
var b = fan.fun;
b(); //winDragon 没bind前this指向window
var c = b.bind(fan);
console.log(c()); //dragon bind后this指向对象中的user
b.bind(fan,'男','学习')(); //dragon 男 学习
b.bind(fan,['男','学习'])(); //dragon ['男', '学习'] undefined
bind实现分析:
// 传参默认是指向window
Function.prototype.myBind = function(context = window){
let fan = this; // 调用bind的函数
let args = [...arguments].slice(1); // myBind的参数
let bind = function(){
let args1 = [...arguments].slice(); // bind的参数
return fan.apply(context,args.concat(args1));
}
return bind;
}
四、总结
1、apply 、call、bind 三者都是用来改变函数的 this 对象的指向的;
2、apply、call、bind 三者第一个参数都是 this 要指向的对象;
3、apply、call、bind 三者都可以利用后续参数传参;
4、bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。