ホームページ  >  記事  >  この点を理解してフロントエンド担当者の7割をキャッチアップした記事

この点を理解してフロントエンド担当者の7割をキャッチアップした記事

青灯夜游
青灯夜游転載
2022-09-06 17:03:132894ブラウズ

この バグ vue2 のこの指摘された問題 でアロー関数を使用したため、同僚が行き詰まり、対応する props# を取得できなくなりました。 ## 。私がそれを紹介したとき、彼はそれを知りませんでした。その後、私はわざとフロントエンド コミュニケーション グループに目を向けました。今のところ、フロントエンド プログラマの少なくとも 70% はまだそれを理解していません。それを共有します。今日はあなたと一緒に thisポイント、何も学んでいないなら、大きな口を立ててください。

1. 場所の呼び出し

    スコープは定義された場所に関連しており、実行される場所とは関係ありません
  • thisポインタは、どこで定義されるかには関係なく、どのように呼び出されるか、そしてどのような形式で呼び出されるかに関係します。
  • this(this) どのように this関数が呼び出されます (覚えておくと便利です)
  • 理解を容易にするために、デフォルトでは厳密モードは有効になっていません

#2. バインディング ルール

上でも紹介しましたが、

this ポインティングは主に呼び方の形式によって決まります。次に、呼び出しルールを紹介します。ルールがなければ何も完成しません。この呼び出しルールを覚えておいてください。難しいことは何もありません。」

    呼び出し元の場所を見つけて、次の 4 つのバインド ルールのどれであるかを判断する必要があります。
  • 次に、これら 4 つのバインド ルールの優先順位も把握しておく必要があります。
  • これら 2 つの点を理解していると、これがどこを指しているのかを簡単に理解できるでしょう。
2.1 デフォルトのバインディング

最も一般的なのは関数です。 used 呼び出しメソッド、呼び出し関数のタイプ: 独立した関数呼び出し

function bar() {
  console.log(this) // window
}

bar は修飾子のない直接呼び出しであるため、デフォルトのバインディングは
    window
  • ## です。 #厳密モー​​ドでは、this
  • ここは
  • 未定義
  • #2.2 暗黙的なバインディング

最も一般的な用語を使用します。 、それは意味します: オブジェクトには特定のメソッドがあり、そのメソッドはこのオブジェクトを通じてアクセスされ、直接呼び出されます (注: アロー関数は特別であり、これについては後で説明します)
const info = {
  fullName: 'ice',
  getName: function() {
    console.log(this.fullName)
  }
}

info.getName() // 'ice'

この関数は

info
    呼び出しが開始され、暗黙的なバインディングが実行されるため、現在の
  • thisinfo となり、値は this.fullName を通じてアクセスされます。は間違いなく Ice
  • 暗黙的な損失が正常です

場合によっては、暗黙的な損失が実行され、暗黙的にバインドされた関数がバインドされたオブジェクトが失われます。つまり、デフォルト バインディングになります。デフォルト バインディングの this

値は、現在いる環境。厳密モードかどうか。

const info = {
  fullName: 'ice',
  getName: function() {
    console.log(this.fullName)
  }
}

const fn = info.getName

fn() //undefined
この場合、暗黙的な損失が行われ、バインドされたオブジェクトが失われることになりますが、なぜこのような問題が発生するのでしょうか?記憶に詳しい人なら簡単に理解できると思います。 ここには直接呼び出しはありませんが、getName に対応するメモリ アドレスは info

を通じて検出され、変数

fn## に割り当てられます。
  • #その後、呼び出しは fn を通じて直接行われました。実際、ここでの本質は、独立した関数呼び出し (window です) です。
  • window
  • fullName 属性を取り出します。この属性は
  • 未定義
  • 暗黙的な損失の高度なである必要があります。最初に理解する必要があるのは、コールバック関数です。実際、これはこのように理解できます。つまり、今は呼び出すのではなく、パラメーターの形式で他の場所に渡し、別の場所で呼び出します。 <pre class="brush:php;toolbar:false">//申明变量关键字必须为var var fullName = 'panpan' const info = {   fullName: 'ice',   getName: function() {     console.log(this.fullName)   } } function bar(fn) {   //fn = info.getName   fn() // panpan } bar(info.getName)</pre>
  • まず、
bar

fn はコールバック関数

    fn = info.getName
  • です。パラメータの受け渡し これは暗黙的な割り当てです。実際、上記の暗黙的な損失と同じ意味を持っています。それらはすべて fn = info.getName 参照を指しており、これはメモリ アドレス です
  • this
  • が失われているため、関数は独立して呼び出されます。デフォルトのバインディング ルールは、this がグローバル window オブジェクト
  • # であるためです。 ##注: 宣言が「What about
  • var」でなければならないのはなぜですか? var で宣言された変数のみがグローバル window
  • オブジェクトに追加されるためです。
  • If let\const
      使用済みの場合は、いいえ、変数を宣言するための 2 つのキーワードを詳しく紹介します
    • しかし、シナリオによっては、暗黙的な損失を望まない場合があります。どうすればよいですか? ディスプレイ バインディングを導入しましょうあなたへの、つまり固定電話です。
    • 2.3 表示バインディング
    ただし、一部のシナリオでは、
  • this
  • の変更は予期せぬものになります。コールバック関数がどのように実行されるかを制御できないため、呼び出し場所に、これが指す予想されるバインディングがあるかどうかを制御する方法がありません。
次の表示バインディングを使用して、この暗黙的な損失の問題を解決できます。

2.3.1 call/apply/bind

js の「すべての」関数には、いくつかの便利な機能があります。これは、そのプロトタイプ チェーンに関連しています。プロトタイプチェーン js を介して継承を偽装して実装する方法をプロトタイプに導入します。このうち call/apply/bind

の 3 つのメソッドは関数プロトタイプチェーン上のメソッドであり、これらは関数プロトタイプチェーンで呼び出すことができます。関数。

2.3.2 call

  • call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
    • 第一个参数为固定绑定的this对象
    • 第二个参数以及二以后的参数,都是作为参数进行传递给所调用的函数
  • 备注
    • 该方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组
var fullName = 'panpan'

const info = {
  fullName: 'ice',
  getName: function(age, height) {
    console.log(this.fullName, age, height)
  }
}

function bar(fn) {
  fn.call(info, 20, 1.88) //ice 20 1.88
}

bar(info.getName)

2.3.3 apply

  • call的方法类似,只是参数列表有所不同
    • 参数
      • call  参数为单个传递
      • apply 参数为数组传递
var fullName = 'panpan'

const info = {
  fullName: 'ice',
  getName: function(age, height) {
    console.log(this.fullName, age, height)
  }
}

function bar(fn) {
  fn.apply(info, [20, 1.88]) //ice 20 1.88
}

bar(info.getName)

2.3.4 bind

  • bindapply/call之间有所不同,bind传入this,则是返回一个this绑定后的函数,调用返回后的函数,就可以拿到期望的this。
  • 参数传递则是
    • 调用bind时,可以传入参数
    • 调用bind返回的参数也可以进行传参
var fullName = 'panpan'

const info = {
  fullName: 'ice',
  getName: function(age, height) {
    console.log(this.fullName, age, height) //ice 20 1.88
  }
}

function bar(fn) {
  let newFn = fn.bind(info, 20)
  newFn(1.88)
}

bar(info.getName)

2.4 new绑定

  谈到new关键字,就不得不谈构造函数,也就是JS中的 "类",后续原型篇章在跟大家继续探讨这个new关键字,首先要明白以下几点,new Fn()的时候发生了什么,有利于我们理解this的指向。

  • 创建了一个空对象

  • 将this指向所创建出来的对象

  • 把这个对象的[[prototype]] 指向了构造函数的prototype属性

  • 执行代码块代码

  • 如果没有明确返回一个非空对象,那么返回的对象就是这个创建出来的对象

function Person(name, age) {
  this.name = name
  this.age = age

}

const p1 = new Person('ice', 20)

console.log(p1) // {name:'ice', age:20}
  • 当我调用new Person()的时候,那个this所指向的其实就是p1对象

3. 绑定优先级

3.1 隐式绑定 > 默认绑定

function bar() {
  console.log(this) //info
}

const info = {
  bar: bar
}

info.bar()
  • 虽然这边比较有些勉强,有些开发者会认为这是默认绑定的规则不能直接的显示谁的优先级高
  • 但是从另外一个角度来看,隐式绑定,的this丢失以后this才会指向widonw或者undefined,变相的可以认为隐式绑定 > 默认绑定

3.2 显示绑定 > 隐式绑定

var fullName = 'global ice'
const info = {
  fullName: 'ice',
  getName: function() {
    console.log(this.fullName) 
  }
}

info.getName.call(this) //global ice
info.getName.apply(this) //global ice
info.getName.bind(this)() //global ice
  • 通过隐式绑定和显示绑定的一起使用很明显 显示绑定 > 隐式绑定

3.3 bind(硬绑定) >  apply/call

function bar() {
  console.log(this) //123
}

const newFn = bar.bind(123)
newFn.call(456)

3.4 new绑定 > bind绑定

首先我们来说一下,为什么是和bind比较,而不能对callapply比较,思考下面代码

const info = {
  height: 1.88
}

function Person(name, age) {
  this.name = name
  this.age = age
}

const p1 = new Person.call('ice', 20)

//报错: Uncaught TypeError: Person.call is not a constructor

new绑定和bind绑定比较

const info = {
  height: 1.88
}

function Person(name, age) {
  this.name = name
  this.age = age
}

const hasBindPerson = Person.bind(info)

const p1 = new hasBindPerson('ice', 20)

console.log(info) //{height: 1.88}
  • 我们通过bindPerson进行了一次劫持,硬绑定了this为info对象
  • new 返回的固定this的函数
  • 但是我们发现 并不能干预this的指向

3.5 总结

new关键字 > bind > apply/call > 隐式绑定 > 默认绑定

4. 箭头函数 (arrow function)

首先箭头函数是ES6新增的语法

const foo = () => {}

4.1 箭头函数this

var fullName = 'global ice'

const info = {
  fullName: 'ice',
  getName: () => {
    console.log(this.fullName)
  }
}

info.getName() //global ice
  • 你会神奇的发现? 为什么不是默认绑定,打印结果为ice
  • 其实这是ES6的新特性,箭头函数不绑定this,它的this是上一层作用域,上一层作用域为window
  • 所以打印的结果是 global ice

4.2 箭头函数的应用场景 进阶

  • 需求: 在getObjName通过this拿到info中的fullName (值为icefullName)
const info = {
  fullName: 'ice',
  getName: function() {
    let _this = this
    return {
      fullName: 'panpan',
      getObjName: function() {
        console.log(this) // obj
        console.log(_this.fullName)
      }
    }
  }
}

const obj = info.getName()
obj.getObjName()
  • 当我调用 info.getName() 返回了一个新对象

  • 当我调用返回对象的getObjName方法时,我想拿到最外层的fullName,我通过,getObjName的this访问,拿到的this却是obj,不是我想要的结果

  • 我需要在调用info.getName() 把this保存下来,info.getName() 是通过隐式调用,所以它内部的this就是info对象

  • getObjName是obj对象,因为也是隐式绑定,this必定是obj对象,绕了一大圈我只是想拿到上层作用域的this而已,恰好箭头函数解决了这一问题

const info = {
  fullName: 'ice',
  getName: function() {
    return {
      fullName: 'panpan',
      getObjName: () => {
        console.log(this.fullName)
      }
    }
  }
}

const obj = info.getName()
obj.getObjName()

5. 总结

5.1 this的四种绑定规则

  • 默认绑定

  • 隐式绑定

  • 显示绑定 apply/call/bind(也称硬绑定)

  • new绑定

5.2 this的优先级 从高到低

  • new绑定

  • bind

  • call/apply

  • 隐式绑定

  • 默认绑定

6. 结语

  当一切都看起来不起作用的时候,我就会像个石匠一样去敲打石头,可能敲100次,石头没有任何反应,但是101次,石头可能就会裂为两半 我知道并不是第101次起了作用,而是前面积累所致。

  大家有疑惑可以在评论区留言 第一时间为大家解答。

(学习视频分享:web前端开发

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。