您的位置:首页 > Web前端 > JavaScript

JS(JavaScript)中的this使用方式(call、apply、bind方法的异同)——大总结

2020-07-20 22:39 666 查看

文章目录

一,使用this的五种情况

  1. 如果this出现在普通的函数中,this表示window,如果通过window打点调用一个函数,这个函数中的this也是window;
  2. 事件绑定、事件处理程序、事件发生时、浏览器帮我们调用这个函数,此函数中的this表示事件源;
  3. 在一个对象中,有一个方法(函数),如果通过对象调用了这个方法,那么这个方法中的this就表示这个对象;
  4. 在IIFE(立即调用函数)中,this表示window;
  5. 前四点都是在非严格模式下,在严格模式下,调用一个普通函数,this表示undefined,IIFE中的this也表示undefined;

二,以下实例中包含的五种情况

  • this表示事件源
<button id="box">box</button>-->  // 一个公用的button标签(供一下实例中引用)
<script>
let box = document.getElementById("box");
box.onclick = function () {
console.log(this); // this表示事件源
}
</script>
let box = document.getElementById("box");
function f(){
console.log(this);  // window
return function g(){
console.log(this); // 事件源
}
}
box.onclick = f();
  • this表示window
let box = document.getElementById("box");
function f() {
console.log(this); // window
}
// f();  程序员自己调用一个函数  一个普通函数  函数中的this表示window
box.onclick = f();
function k() {
console.log(this); // window
}
window.k();  // window打点调用
k(); // 程序员自己调用 表示window中的
  • this表示对象
var wc = {
name:"wangcai",  // 叫属性
age:100,   // 叫属性
eat:function () {    // 叫方法(属性)
console.log("eat...")
console.log(this); 表示wc对象
}
} // 一个方法中的this是谁,就看点前面是谁。
wc.eat(); // 调用一个对象上的方法
  • 在非严格模式下,this表示window
(function () {
console.log(this);
})(); // 在非严格模式下,IIFE中的this表示window
  • 当启用严格模式(“use strict”)下
"use strict"; // 启用JS的严格模式
function f() {
console.log(this); //表示undefined
}
f();
(function () {
console.log(this); //表示undefined
})();
  • 非严格模式下的几个实例:
function f() {
console.log(this.name); // 表示window
}
let box = document.getElementById("box");
box.name = "mybox";
var name = "mywindow";
var obj = {
name:"myobj",
f:f
}
f(); // mywindow
box.onclick = f; // 点击得到mybox //调用的是box对象中的属性
obj.f(); // myobj
box.onmousedown = function () {  //onmousedown表示鼠标放button上面就要执行的
f(); // 普通函数调用
}
  • onclick是在鼠标点击弹起之后触发的事件,即一次完整的鼠标点击过程。过程完成瞬间触发函数;
  • onmousedown事件是在鼠标按键按下去的瞬间触发的;
  • 简而言之,也就是说οnclick=onmousedown+onmouseup;
var num = 10;  //60 65
var obj = {
num:20, // num:30
}
obj.fn = (function (num) {
this.num = num*3; //
num++;  //
console.log(num) //21
return function (n) {
this.num += n; // 30
num++;
console.log(num) // 22   23
}
})(obj.num)// 输出21
var fn = obj.fn;
fn(5);// 输出22
obj.fn(10); //输出23  调用obj中的fn属性,其中fn指向function(n)的地址,里面的this.num中的this指向对象obj;
console.log(num,obj.num) //输出(65,30)
console.log(window.num) //输出 65
(function () {
var a = 1;
var obj = {
a:10,  //  要使用这个a 必须是obj.a
f:function () {
a *= 2;  //  a为局部变量a不是对象obj中的;
}
}
obj.f();
alert(obj.a+a)    //  运行结果为 12
})();
window.a = 1;
var obj = {
a:10,
f:function () {
this.a *= 2;
console.log(this.a) //this指对象obj
}
}
obj.f();  //  运行结果为 20
var name = "window";
var Wangcai = {
name:"Wangcai",
show:function () {
console.log(this.name)
},
f:function () {
var fun = this.show;
fun(); // 普通函数调用,对于普通函数调用,this就指向window
}
}
Wangcai.f();  //输出window,
var fullname = "language";
var obj = {
fullname:"javascript",
prop:{
getFullName:function () {
return  this.fullname;
}
}
} // 谁用了getFullName  看 . 前面是什么, . 前面是obj.prop,说明getFullName中的this是obj.prop;然而obj.prop中没有fullname属性;所以输出undefined,因为访问一个对象上没有的属性,得到undefined;
console.log(obj.prop.getFullName()); // 输出 undefined
var fullname = "language";
var obj = {
fullname:"javascript",
prop:{
fullname:"vue",
getFullName:function () {
return  this.fullname;
}
}
}
var t = obj.prop.getFullName;
console.log(t()); //就是简单的函数执行;输出language
let obj = {
fn:(function () {
return function () {
console.log(this)
}
})()
}
obj.fn(); //输出{fn: ƒ}  fn()前面对应obj对象,所以在函数fn中的输出this指对象obj的;
let obj = {
fn:(function () {
return function () {
console.log(this)
}
})()
}
var t = obj.fn;  //t所获取的是function () {     console.log(this)   }
t();  //只是简单的函数调用;

三, call,apply,bind使用的异同

首先说一下这三个方法的用途是用于修改this的指向的; 而且它们三个都可以修改;

  • call的作用(1)可以改变this的指向;(2)让函数直接执行;(3)函数传参时,不是以数组的形式;
  • apply的作用 (1)可以改变this的指向; (2)让函数直接执行;(3)函数传参时,以数组的形式;
  • bind的作用 (1)可以改变this的指向; (2)不让函数执行;

那么问题来了:为什么要修改this指向?
从下面实例中来解释;

//存在两个堆,
var obj1 = {name:"wc",say:function () {console.log("我是:"+this.name) }}
var obj2 = {name:"xq",say:function () {console.log("我是:"+this.name) }}
obj1.say();
obj2.say();
  • 以上代码存在的不足:同样的函数,但是占用了两个堆空间,造成了内存的浪费;

但我们有改进的方法:
(1)改进一

var abc = function (){console.log("我是:"+this.name) }
var obj1 = {name:"wc",say:abc} //此时say属性就指向堆空间;
var obj2 = {name:"xq",say:abc}
console.log(obj1.say  === obj2.say)//输出结果为true,表明两个地址是一样的;
obj1.say();
obj2.say();
abc();

(2)改进二(使用call)

var abc = function (){console.log("我是:"+this.name) }
var obj1 = {name:"wc",say:abc}
var obj2 = {name:"xq",say:abc}
//  call作用: 1)让abc中的this指向obj1   call改变this指向  2)让abc直接执行  调用
abc.call(obj1); // this==>obj1   abc()
abc.call(obj2); //

在调用toString()方法时,我们可以使用call方法去直接调用Object上面的toString方法,写法

var arr3 = [110];
Object.prototype.toString.call(arr3); // arr3直接调用Object的原型对象上面的toString

此时call:(1)call让this指向了Object.prototype,(2)让toString执行;总之,call可以让一个数据去借用一个方法;
利用Object.prototype.toString.call可以非常精确地检测一个数据的数据类型:

console.log(Object.prototype.toString.call(1)); // 此时会把1瞬间包装成对象
console.log(Object.prototype.toString.call("hello")); // 此时"hello"也瞬间包装成对象了
console.log(Object.prototype.toString.call(true))
var a;
console.log(Object.prototype.toString.call(a))
// call  1)改变toString中的this的指向   指向了{}
//       2)让toString执行
console.log(Object.prototype.toString.call({}))
console.log(Object.prototype.toString.call([]))
function f(){}
console.log(Object.prototype.toString.call(f))
let d = new Date()
console.log(Object.prototype.toString.call(d))
let r = new RegExp();
console.log(Object.prototype.toString.call(r))

下面代码可以将一段字符串截取出来,并且变为小写(用于将上面的检测类型给截取下来变为小写并输出):

function getType(abc) {
var rs = Object.prototype.toString.call(abc);
rs = rs.substr(8)
var len = rs.length
return rs.substr(0,len-1).toLowerCase()//变为小写
}
console.log(getType(123));//将数据类型修改输出

总之: call方法的两大作用如下

  1. 修改了this的指向;
  2. 可以让方法直接执行;

相对于call方法来说,apply有什么不一样呢?
可以说apply和call是一模一样的,唯一唯一的不同就是传递参数时候存在不一样;如下:

  • 分别使用call和apply的时候:
function F(a,b) {
return a+b;
}
var obj = {};
F.call(obj); // 让F执行,关键是我们如何给F传递参数
F(1,2); // 我们自己调用F函数  并传递参数
console.log(F.call(obj,4,5)); // 我们使用call来传参的方式;
console.log(F.apply(obj,[4,5])); // 而使用apply来传参的方式;
  • 再如:
var arr = [2,3,3,6,9,10,2,1];
// 获取上面的数组中的最大值   Math类(最好不要叫它为构造器)
// console.log(Math.max(2, 4, 1, 6));
console.log(Math.max.call(arr, 2, 3, 3, 6, 9, 10, 2, 1));//使用call传参
console.log(Math.max.apply(arr, [2,3,3,6,9,10,2,1]));//使用apply传参
console.log(Math.max.apply(arr, arr));//使用apply传参
console.log(Math.max.apply({}, arr));
console.log(Math.max.apply("", arr));
console.log(Math.max.apply(null, arr));
console.log(Math.max.apply("abc", arr));
//上面表明如使用apply方法的时候,不过第一个参数写啥,只要把实参传过去就OK,如传一个数值的话使用apply效果会好点;

对于bind方法来说,与上面两种方法不同的地方在于;

call 改变了this指向,并让函数执行;传递参数一个个传递;
apply 改变了this指向,并让函数执行;传送参数必须是数组;
bind 改变了this指向,但是函数不执行;

function F(a,b) {
console.log("F...")
return a+b;
}
var obj = {};
let k = F.bind(obj);//bind改变的知识this的指向,而函数不执行,要想执行还需要函数调用并传参;
console.log(k(1, 2));
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: