理解JavaScript中的this、call、apply和bind

一、JavaScript中的this指向

首先我们应该知道,this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,可以简单的理解为,this的最终指向的是那个调用它的对象。

  1. 普通函数调用,此时 this 指向 window。
function a(){
    var name = "Qiu;
    console.log(this.name);   //undefined
    console.log(this);   //Window
}
a();

按照我们上面说的this最终指向的是调用它的对象,这里的函数a实际是被Window对象所点出来的。

  1. 事件绑定,事件处理程序,事件发生时,浏览器帮我们调用这个函数,此函数中的 this表示事件源
<body>
    <button id="btn">box</button>
<script>
    var oBtn = document.getElementById("btn");
    oBtn.onclick = function() {
        console.log(this); 
    }
</script>
</body>

在这里插入图片描述

此时,this指向的是事件源。

  1. 在一个对象中,如果有方法(函数),如果通过这个对象调用方法,方法中的this表示这个对象。
var person = {
    name:"Qiu",
    fn:function(){
        console.log(this.name);  //Qiu
    }
}
person.fn();

这里的this指向的是对象person,因为你调用这个fn是通过person.fn()执行的,那自然指向就是对象person。this的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁。

  1. IIFE中,this表示window
 (function () {
        console.log(this);
    })(); // 在非严格模式下,IIFE中的this表示window
  1. 前四点都是在非严格模式下,在严格模式下,调用一个普通函数,this表示undefined。 IIFE中的this也表示undefined.
  "use strict"; // 启用JS的严格模式
    function f() {
        console.log(this); // undefined
    }
    f();

    (function () {
        console.log(this); // undefined
    })();


我们分析一个例子

<body>
    <button id="btn">box</button>
  <script>
    function f(){
        console.log(this.name);
    }
    let box=document.getElementById("btn");
    box.name="mybox";
    var name="mywindow";
    var obj={
        name:"myobj",
        f:f
    }
    f(); //mywindow
    box.οnclick=f; //点击事件,mybox
    obj.f(); //myobj
 </script>
</body>
 

let一个变量box,通过id获取元素,此时box是一个集合。然后为box添加一个属性,值为mybox。var一个全局变量name=“mywindow”。 定义一个对象,有属性name=myobj和方法f:f。f();处为普通函数调用,此时f()函数中的this指向window。this.name的值为mywindow。obj.f();这里的this指向的是对象obj,因为你调用这个f是通过obj.f()执行的,那自然指向就是对象obi。所以this.name的值为myobj。box.οnclick=f;为点击事件,此时f()函数中的this指向事件源。

<script>
    var fullname = 'John Doe';
    var obj = {
        fullname: 'Colin Ihrig',
        prop: {
            fullname: 'Aurelio De Rosa',
            getFullname: function () {
                return this.fullname;
            }
        }
    };
    console.log(obj.prop.getFullname());
    // 函数的最终调用者 obj.prop 

    var test = obj.prop.getFullname;
    console.log(test());
    // 函数的最终调用者 test()  this-> window

    obj.func = obj.prop.getFullname;
    console.log(obj.func());
    // 函数最终调用者是obj

    var arr = [obj.prop.getFullname, 1, 2];
    arr.fullname = "JiangHao";
    console.log(arr[0]());// 函数最终调用者数组
</script>

二、改变this的指向

我们可以通过

  1. call、apply、bind
  2. 箭头函数

来改变this的指向。
那么我们为什么要去改变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();

我们可以通过call、apply、bind来改进,实现代码的复用。如下:

    var abc = function (){console.log("我是:"+this.name) }
    var obj1 = {name:"wc"}
    var obj2 = {name:"xq"}
    abc.call(obj1); //call
    abc.apply(obj1); //apply
    abc.bind(obj1)(); //bind
这里,改变this指向让abc中的this指向obj1  ,让abc直接执行 。
注意:
1. call和apply改变了函数的this之后执行该函数。
2. bind返回了改变执行上下文之后的一个函数

call、apply与bind的差别
call和apply改变了函数的this上下文后便执行该函数,而bind则是返回改变了上下文后的一个函数。
call、apply的区别
他们俩之间的差别在于参数的区别,call和aplly的第一个参数都是要改变上下文的对象,而call从第二个参数开始以参数列表的形式展现,apply则是把除了改变上下文对象的参数放在一个数组里面作为它的第二个参数。

接下来我们来了解一下call、apply、bind的常见应用场景。

先看个例子:

 function Person(name){
        this.name=name;
    }
    Person.prototype={
        showName:function(){
            console.log(this.name);
        }
    }
    var p=new Person("wc");
    p.showName();//wc

那么

   var animal={name:"xq"}

上面代码中有一个对象字面量,他没有所谓的showName方法,但是我还是想用?怎么办?call、apply、bind可以帮我们干这件事。

p.showName.call(animal);  //xq
p.showName.apply(animal);  //xq
p.showName.bind(animal)();  //xq

大概就是当一个object中没有我们需要的某种方法时,我们可以通过改变this的指向来使用我们需要的方法。
1.求数组中的最大和最小值

var arr=[34,5,6,3,1,-45,666]
console.log(arr);
console.log("MAX:"+Math.max.call(Math,34,5,6,3,1,-45,666));//666
console.log("MAX:"+Math.max.apply(Math,arr));//666

Math是应用在Number类型 的一个内置对象,这里我们通过call、apply方法将Array类型的Math的this指向arr,从而我们可以使用该方法。
2. 将伪数组转化为数组
js中的伪数组(例如通过document.getElementsByTagName获取的元素)具有length属性,并且可以通过0、1、2…下标来访问其中的元素,但是没有Array中的push、pop等方法。我们可以利用call、apply来将其转化为真正的数组这样便可以方便地使用数组方法了。

 let arrayLike={
        0:'wc',
        1:'xq',
        2:'qq',
        length:3
    }
let arr=Array.prototype.slice.call(arrayLike);

此时上面arr便是一个包含arrayLike元素的真正的数组了。
那么怎么知道arr是Object还是Array呢?
3.判断变量类型

    let arrayLike={
        0:'wc',
        1:'xq',
        2:'qq',
        length:3
    }
    let arr=Array.prototype.slice.call(arrayLike);
    console.log(arr);
    console.log(Object.prototype.toString.call(arr));
    console.log(Object.prototype.toString.call(arrayLike));

结果如下:
在这里插入图片描述
4.数据追加
在js中要往数组中添加元素,可以直接用push方法,


  let arr1=[1,2,3]
  let arr2=[4,5,6]
  arr1.push.apply(arr1,arr2);
  console.log(arr1); //[1,2,3,4,5,6]

后续有问题再进行补充。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值