call()、apply()和bind()用法、区别和场景使用
先简单了解下:
例一:
var name = "小王";
var age = 17;
var obj = {
name: "小张",
objAge: this.age,
myFun: function() {
console.log(this.name + "年龄" + this.age);
}
}
console.log(obj.objAge); // 17
console.log(obj.myFun()); // 小张年龄 undefined
例二:
var fav = "盲僧"
function shows(){
console.log(this.fav);
}
console.log(shows()); // 盲僧
比较一下这两者 this 的差别,第一个打印里面的 this 指向 obj,第二个全局声明的 shows() 函数 this 是 window ;
- call()、apply()、bind() 都是用来重定义 this 这个对象的!
var name = "小王";
var age = 17;
var obj = {
name: "小张",
objAge: this.age,
myFun: function() {
console.log(this.name + "年龄" + this.age);
}
}
var db = {
name: "德玛",
age: 99
}
console.log(obj.myFun.call(db)); // 德玛年龄 99
console.log(obj.myFun.apply(db)); // 德玛年龄 99
console.log(obj.myFun.bind(db)()); // 德玛年龄 99
以上出了:
- 如果call()和apply()的第一个参数是null或者undefined,那么this的指向就是全局变量,在浏览器里就是window对象。
- bind 方法后面多了个 () 外 ,结果返回都一致!
由此得出结论,bind 返回的是一个新的函数,你必须调用它才会被执行。
2,对比call 、bind 、 apply 传参情况下
var name = "小王";
var age = 17;
var obj = {
name: "小张",
objAge: this.age,
myFun: function(fm,t) {
console.log(this.name + "年龄" + this.age, "来自" + fm + "去往" + t);
}
}
var db = {
name: "德玛",
age: 99
}
obj.myFun.call(db,'成都','上海'); // 德玛 年龄 99 来自 成都去往上海
obj.myFun.apply(db,['成都','上海']); // 德玛 年龄 99 来自 成都去往上海
obj.myFun.bind(db,'成都','上海')(); // 德玛 年龄 99 来自 成都去往上海
obj.myFun.bind(db,['成都','上海'])(); // 德玛 年龄 99 来自 成都, 上海去往 undefined
微妙的差距!
从上面四个结果不难看出:
call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象,第二个参数差别就来了:
call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔,直接放到后面 obj.myFun.call(db,'成都', ... ,'string' )。
apply 的所有参数都必须放在一个数组里面传进去 obj.myFun.apply(db,['成都', ..., 'string' ])。
bind 除了返回是函数以外,它 的参数和 call 一样。
当然,三者的参数不限定是 string 类型,允许是各种类型,包括函数 、 object 等等!
- 总结:
- 每个函数都包含两个非继承而来的方法:call()方法和apply()方法。
- 相同点:这两个方法的作用是一样的。
都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。
一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向。 - 不同点:接收参数的方式不同。
apply()方法 接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。
语法:apply([thisObj [,argArray] ]);,调用一个对象的一个方法,2另一个对象替换当前对象。
说明:如果argArray不是一个有效数组或不是arguments对象,那么将导致一个
TypeError,如果没有提供argArray和thisObj任何一个参数,那么Global对象将用作thisObj。
call()方法 第一个参数和apply()方法的一样,但是传递给函数的参数必须列举出来。
语法:call([thisObject[,arg1 [,arg2 [,...,argn]]]]);,应用某一对象的一个方法,用另一个对象替换当前对象。
说明: call方法可以用来代替另一个对象调用一个方法,call方法可以将一个函数的对象上下文从初始的上下文改变为thisObj指定的新对象,如果没有提供thisObj参数,那么Global对象被用于thisObj。
call():
call方法既可以调用函数,又可以改变函数内的this指向。
var obj = {
name: 'andy'
}
function fn(a, b) {
console.log(this);
console.log(a+b)
};
fn(1,2)// 此时的this指向的是window 运行结果为3
fn.call(obj,1,2)//此时的this指向的是对象obj,参数使用逗号隔开,运行结果为3
应用场景: 经常做继承,参考下面这篇讲原型链的末尾部分:https://blog.csdn.net/caipital/article/details/108396438
function f1(a,b){
console.log(this) //输出f2
console.log(a+b) //输出3
}
function f2 (a,b) {
console.log(this)
console.log(a-b)
}
f1.call(f2,1,2) //打印输出f2 和 3
利用call()判断数据类型(在判断数据类形式使用typeof,一般不是太准确的,我们可以使用Object.prototype.toString.call()方法来判断一个数据的数据类型
console.log(Object.prototype.toString.call("sun")) // [Object String] 返回值都是字符串类型
console.log(Object.prototype.toString.call(18)) // [object Number]
console.log(Object.prototype.toString.call(false)) // [object Boolean]
console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
console.log(Object.prototype.toString.call(null)) // [object Null]
console.log(Object.prototype.toString.call(function(){})) // [object Function]
console.log(Object.prototype.toString.call([])) // [object Array]
console.log(Object.prototype.toString.call({})) // [object Object]
// 封装
function getType(para){
var obj = Object.prototype.toString.call(para); //区分对象类型 确定当前的数据的类型
var sub = obj.substr(8);
// stringObject.substr(start,length) start 要抽取的子符串的起始下标,
// length 截取的长度,如果不写则表示从start开始截取到最后 ,stringObject表示某一字符串
var len = sub.length;
var sub = sub.substr(0,len-1)
var change = sub.toLowerCase(sub) //转换成小写
return change ;
}
console.log(getType("sun")); //string
翻转字符串
//思路:将字符串转化为数组,借用数组中的reverse,将字符串翻转过来
var str = "abcdefg";
console.log(Array.prototype.reverse.call(str)); //此时会报错误,即引用类型错误,就是说只有数组才能使用reverse这个方法;(错误写法)
//方法一:这种方法内有使用call()
var arr = Array.from(str).reverse().join("") //将字符串转化为数组,在进行翻转,然后在进行拼接
console.log(arr) //gfedcba
console.log(typeof arr) //string
//方法二:
var rs = Array.prototype.reverse.call(str.split("")).join("");
//splice(start,length)方法用于把一个字符串分割成字符串数组,start 表示从指定的地方分割字符串 length表示分割的长度。
//返回一个一个字符串数组 如果把空字符串 ("") 用为参数那么字符串中的每个字符之间都会被分割
console.log(rs); //gfedcba
console.log(typeof arr) //string
apply()
call方法既可以调用函数,又可以改变函数内的this指向。与call()的区别是函数调用时参数是数组。
function fn(a,b){
console.log(this)
console.log(a+b)
}
let obj = {
name:'zy'
}
fn.apply(obj,[1,2]) //此时的this指向的是对象obj,参数传入了一个数组,运行结果为3
应用场景:
- 数组运用到Math的API中,如:Math.max,Math.min等,
- Array.prototype.push 可以实现两个数组合并等
Math的API:
let a = Math.max(9,21,33);
console.log(a) //33
let arr = [9,21,33];
let res1 = Math.max(...arr)
let res2 = Math.max.apply(null,arr);
let res3 = Math.max.apply(Math,arr); // 推荐
let res4 = Math.max.apply(Function,arr);
console.log(res1) //33
console.log(res2) //33
console.log(res3) //33
console.log(res4) //33
数组合并:
vararr1=new Array("1","2","3");
vararr2=new Array("4","5","6");
Array.prototype.push.apply(arr1,arr2);
bind()
bind()改变函数this指向,但不调用函数,会生成一个新的函数
function fn(a,b){
console.log(this)
console.log(a+b)
}
let obj = {
name:'zy'
}
let new_fn1 = fn.bind(obj)
new_fn1(2,3) //this指向obj 打印输出5
let new_fn2 = fn.bind(obj,1,2)
new_fn2() //this指向obj 打印输出3
应用场景:当你想改变函数的this指向同时又不想立即执行函数的时候。
例子:要求:给三个按钮绑定点击事件,点击之后按钮变成不可点的状态,1秒之后恢复,变成可点击的状态。
方案一 :let 块级作用域
for(let i =0;i<btns.length;i++) {
btns[i].onclick = function() {
//表达式函数this指向绑定事件的对象
this.disabled = true;
setTimeout(function(){
btns[i].disabled = false;
},1000)
}
}
方案二:使用自调用函数(闭包思想)
闭包(closure):指有权访问另一个函数作用域中变量的函数。
for (var i = 0; i < btns.length; i++) {
(function (i) {
btns[i].onclick = function () {
//表达式函数this指向绑定事件的对象
this.disabled = true;
setTimeout(function () {
console.log(this) //Window
btns[i].disabled = false;
}, 1000)
}
})(i)
}
方案三: var that = this,储存this(闭包)
for(var i =0;i<btns.length;i++) {
btns[i].onclick = function() {
//表达式函数this指向绑定事件的对象
this.disabled = true;
var that = this;
setTimeout(function(){
that.disabled = false;
},1000)
}
}
方案四:使用 bind()改变this指向
for(var i =0;i<btns.length;i++) {
btns[i].onclick = function() {
//表达式函数this指向绑定事件的对象
this.disabled = true;
setTimeout(function(){
this.disabled = false;
}.bind(this),1000)
}
}
参考地址:
https://www.runoob.com/w3cnote/js-call-apply-bind.html
https://www.cnblogs.com/phoebeyue/p/9216514.html
https://blog.csdn.net/caipital/article/details/108437024