首頁  >  文章  >  web前端  >  詳解this指向,讓你一篇搞懂this、call、apply

詳解this指向,讓你一篇搞懂this、call、apply

coldplay.xixi
coldplay.xixi轉載
2021-03-16 09:56:112495瀏覽

詳解this指向,讓你一篇搞懂this、call、apply

目錄

  • #前言思考題
  • 一、this的指向
  • 二、call和apply
  • 三、模擬實作一個call
  • 四、bind

詳解this指向,讓你一篇搞懂this、call、apply

  • 前言思考題
記得當時找實習的時候,總是會在履歷上加上一句-熟悉Js,例如this指向、call、apply等…

詳解this指向,讓你一篇搞懂this、call、apply

#(免費學習推薦:

javascript影片教學

  • 而每次投遞履歷時我都會經歷以下步驟

面試前,去問度娘-this指向可以分為哪幾種啊~、call和apply的差別是什麼?底氣由0% 猛漲到了50%;面試中,面試官隨便扔上來幾道題,我都可以「堅定的」給出答案,結果總是不盡人意…

    面試後,我會羞愧的刪除掉履歷上的這條。而再之後投遞履歷時我又再加上了這一條…

#思考題下面幾題是我在網路上搜尋出來的熱度較高的問題,如果大佬們可以輕鬆的回答上,並有清晰的思路,不妨直接點個贊吧(畢竟也消耗了不少腦細胞),如果大佬們能在評論處指點一二,就更好了! ! !

填空題:

執行Javascript中的

【 】

函數會建立一個新函數,新函數與被調函數具有相同的函數體,當目標函數被呼叫時this 值指向第一個參數。

詳解this指向,讓你一篇搞懂this、call、apply

問答題:

  • 請你談談改變函數內部this指標的指向函數有哪幾種,他們的差異是什麼?
  • this的指向可以分為哪幾種?
    • 程式碼分析題:
    var name = 'window'var person1 = {
      name: 'person1',
      show1: function () {
        console.log(this.name)
      },
      show2: () => console.log(this.name),
      show3: function () {
        return function () {
          console.log(this.name)
        }
      },
      show4: function () {
        return () => console.log(this.name)
      }}var person2 = { name: 'person2' }person1.show1()person1.show1.call(person2)person1.show2()person1.show2.call(person2)person1.show3()()person1.show3().call(person2)person1.show3.call(person2)()person1.show4()()person1.show4().call(person2)person1.show4.call(person2)()
  • 一、this的指向
  • 百度、Google上輸入「this的指向」關鍵字,大幾千條文章肯定是有的,總不至於為了全方面、無死角的掌握它就要將所有的文章都看一遍吧?所以不如梳理出一個穩固的框架,順著我們的思路來填充它。
心智圖
  • 本節精華:this 總是(非嚴格模式下)指向一個對象,而具體指向哪個物件是在執行時期基於函數的
  • 執行環境
  • 動態綁定的,而非函數被宣告時的環境;
  • 除了不常用的with和eval的情況,具體到實際應用中,this指向大概可以分為四種:

    作為物件的方法呼叫;

    #作為普通函數呼叫;

    建構器呼叫;

    call 或apply呼叫;

    箭頭函數中,this指向函數上層作用域的this;

    ##建構子

    普通函數

    的差別在於

    被呼叫的方式

    A,call(B) => 可以理解成在B的作用域內呼叫了A方法;

    分析

    1、作為物件的方法呼叫

    當函數作為物件的方法被呼叫時,this指向該物件

    var obj = {
        a: 'yuguang',
        getName: function(){
            console.log(this === obj);
            console.log(this.a);
        }};obj.getName(); // true yuguang
    2、作為普通函數調用

    當函數不作為物件的屬性被調用,而是以普通函數的方式,this總是指向全域物件(在瀏覽器中,通常是Window物件)

    window.name = 'yuguang';var getName = function(){
        console.log(this.name);};getName(); // yuguang

    或是下面這段迷惑性的程式碼:

    window.name = '老王'var obj = {
        name: 'yuguang',
        getName: function(){
            console.log(this.name);
        }};var getNew = obj.getName;getNew(); // 老王

    而在ES5的嚴格模式下,this被規定為不會指向全域對象,而是undefined

    3、建構器呼叫

    除了一些內建函數,大部分Js中的函數都可以成為建構器,它們與普通函數沒什麼不同

    建構器###和###普通函數###的差別在於###被呼叫的方式###:###當new運算子呼叫函數時,總是會傳回一個對象,this通常也指向這個物件###
    var MyClass = function(){
        this.name = 'yuguang';}var obj = new MyClass();obj.name; // yuguang
    ###但是,如果顯式的回傳了一個object對象,那麼此運算結果最終會傳回這個物件。 ###
    var MyClass = function () {
        this.name = 1;
        return {
            name: 2
        }}var myClass = new MyClass(); console.log('myClass:', myClass); // { name: 2}
    ###只要建構器不顯示的回傳任何數據,或傳回非物件類型的數據,就不會造成上述問題。 #########4、call或apply呼叫#########跟普通的函數呼叫相比,用call和apply可以動態的改變函數的this###
    var obj1 = {
        name: 1,
        getName: function (num = '') {
            return this.name + num;
        }};var obj2 = {
        name: 2,};// 可以理解成在 obj2的作用域下调用了 obj1.getName()函数console.log(obj1.getName()); // 1console.log(obj1.getName.call(obj2, 2)); // 2 + 2 = 4console.log(obj1.getName.apply(obj2, [2])); // 2 + 2 = 4
    ### ###5.箭頭函數#########箭頭函數不會建立自己的this,它只會從自己的作用域鏈的上一層繼承this。 ###

    因此,在下面的代码中,传递给setInterval的函数内的this与封闭函数中的this值相同:

    this.val = 2;var obj = {
        val: 1,
        getVal: () => {
            console.log(this.val);
        }}obj.getVal(); // 2

    常见的坑

    就像标题一样,有的时候this会指向undefined

    情况一

    var obj = {
        name: '1',
        getName: function (params) {
            console.log(this.name)
        }};obj.getName();var getName2 = obj.getName;getName2();

    这个时候,getName2()作为普通函数被调用时,this指向全局对象——window。

    情况二

    当我们希望自己封装Dom方法,来精简代码时:

    var getDomById = function (id) {
        return document.getElementById(id);};getDomById('p1') //dom节点

    那么我们看看这么写行不行?

    var getDomById = document.getElementByIdgetDomById('p1') // Uncaught TypeError: Illegal invocation(非法调用)

    这是因为:

    • 当我们去调用document对象的方法时,方法内的this指向document
    • 当我们用getId应用document内的方法,再以普通函数的方式调用,函数内容的this就指向了全局对象。

    利用call和apply修正情况二

    document.getElementById = (function (func) {
        return function(){
            return func.call(document, ...arguments)
        }})(document.getElementById) // 利用立即执行函数将document保存在作用域中

    詳解this指向,讓你一篇搞懂this、call、apply

    二、call和apply

    不要因为它的“强大”而对它产生抗拒,了解并熟悉它是我们必须要做的,共勉!

    思维导图

    詳解this指向,讓你一篇搞懂this、call、apply

    1.call和apply区别

    先来看区别,是因为它们几乎没有区别,下文代码实例call和apply都可以轻易的切换。

    当它们被设计出来时要做到的事情一摸一样,唯一的区别就在于传参的格式不一样

    • apply接受两个参数
      • 第一个参数指定了函数体内this对象的指向
      • 第二个参数为一个带下标的参数集合(可以是数组或者类数组)
    • call接受的参数不固定
      • 第一个参数指定了函数体内this对象的指向
      • 第二个参数及以后为函数调用的参数

    因为在所有(非箭头)函数中都可以通过arguments对象在函数中引用函数的参数。此对象包含传递给函数的每个参数,它本身就是一个类数组,我们apply在实际使用中更常见一些。

    call是包装在apply上面的语法糖,如果我们明确的知道参数数量,并且希望展示它们,可以使用call。

    当使用call或者apply的时候,如果我们传入的第一个参数为null,函数体内的this会默认指向宿主对象,在浏览器中则是window

    借用其他对象的方法

    我们可以直接传null来代替任意对象

    Math.max.apply(null, [1, 2, 3, 4, 5])

    2.call和apply能做什么?

    使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数——来时MDN

    • 调用构造函数来实现继承;
    • 调用函数并且指定上下文的 this;
    • 调用函数并且不指定第一个参数;

    1.调用构造函数来实现继承

    通过“借用”的方式来达到继承的效果:

    function Product(name, price) {
    	this.name = name;
    	this.price = price;}function Food(name, price) {
    	Product.call(this, name, price); //
    	this.category = food;}var hotDog = new Food('hotDog', 20);

    2.调用函数并且指定上下文的 this

    此时this被指向了obj

    function showName() {
        console.log(this.id + ':' + this.name);};var obj = {
        id: 1,
        name: 'yuguang'};showName.call(obj)

    3.使用call单纯的调用某个函数

    Math.max.apply(null, [1,2,3,10,4,5]); // 10

    詳解this指向,讓你一篇搞懂this、call、apply

    三、模拟实现一个call

    先来看一下call帮我们需要做什么?

    var foo = {
    	value: 1};function show() {
    	console.log(this.value);};show.call(foo); //1

    就像解方程,要在已知条件中寻找突破哦口:

    • call 使得this的指向变了,指向了foo;
    • show 函数被执行了;
    • 传入的参数应为 this + 参数列表;

    第一版代码

    上面提到的3点,仅仅完成了一点,且传入的参数

    var foo = {
        value: 1};function show() {
        console.log(this.value);};Function.prototype.setCall = function (obj) {
        console.log(this); // 此时this指向show
        obj.func = this; // 将函数变成对象的内部属性
        obj.func(obj.value); // 指定函数
        delete obj.func // 删除函数,当做什么都没发生~}show.setCall(foo);

    第二版代码

    为了解决参数的问题,我们要能获取到参数,并且正确的传入:

    var foo = {
        value: 1};function show(a, b) {
        console.log(this.value);
        console.log(a + b);};Function.prototype.setCall = function (obj) {
        obj.fn = this; // 将函数变成对象的内部属性
        var args = [];
        for(let i = 1; i <p>此时,我们就可以做到,传入多个参数的情况下使用call了,但是如果你仅想用某个方法呢?</p><p><strong>第三版代码</strong></p><pre class="brush:php;toolbar:false">Function.prototype.setCall = function (obj) {
        var obj = obj || window;
        obj.fn = this;
        var args = [];
        for(var i = 1, len = arguments.length; i <p><strong>四、bind</strong></p><blockquote><p>bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用 —— MDN</p></blockquote><p>提到了<strong>call</strong>和<strong>apply</strong>,就绕不开<strong>bind</strong>。我们试着来模拟一个bind方法,以便加深我们的认识:</p><pre class="brush:php;toolbar:false">Function.prototype.bind = function (obj) {
        var _this = this; // 保存调用bind的函数
        var obj = obj || window; // 确定被指向的this,如果obj为空,执行作用域的this就需要顶上喽
        return function(){
            return _this.apply(obj, arguments); // 修正this的指向
        }};var obj = {
        name: 1,
        getName: function(){
            console.log(this.name)
        }};var func = function(){
        console.log(this.name);}.bind(obj);func(); // 1

    这样看上去,返回一个原函数的拷贝,并拥有指定的 this 值,还是挺靠谱的哦~

    写在最后

    JavaScript內功基礎部分第一篇,總結這個系列是受到了冴羽大大的鼓勵和啟發,本系列大約會有15篇文章,都是我們在面試最高頻的,但在工作中常常被忽略的。

    相關免費學習推薦:javascript#(影片)

    #########

    以上是詳解this指向,讓你一篇搞懂this、call、apply的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述:
    本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除