JavaScriptの中のthis(FCC成都全スタック大会賀老【this in js】テーマ記録)

7355 ワード

前言
成都FCC全スタック大会のお祝い「This In JS」のテーマ記録を共有しています.Javascriptの中でthisと他の言語のthisは違っています.私たちは環境や文法、呼び方などによって、いつも様々な奇妙な方向を指しているかもしれません.
本文
function f() { this }
function f() { 'use strict'; this }
const f = () => this
まずこのような簡単な例を見てみます.この例はみんなJSや面接の時に使ったことがあると信じています.私たちは違った文法環境の下で、異なったモードの下で、thisは違う値かもしれません.同時に、thisの取得値もコードコール方式に依存している.
f()
obj.f()
new f()
関数コールですか?オブジェクトの属性呼び出し?またはコンストラクタの形式で呼び出しますか?同時にJSは、私たちがよく使うcall apply bindのように、thisの指向を変える独特の方法もあります.
f.call(obj)
f.call(null)
f.call(42)
f.call()
もしcallからnullが入ってきたら、一つの数字が入ってくるとは思わなかったかもしれません.thisは何であるかと同時に、これらは上記のように厳格なモードであるかどうかと関係があり、異なるモードでもthisは変化します.
例を見に来ました.
class Greeting { static from(control) { return new Greeting(control.value) } constructor(name) { this.name = name } hello() { console.log(`Hello ${this.name || 'world'}!`) } } [...myForm.elements] .map(e => [e, Greeting.from(e)]) .forEach(([e, {hello}]) => e.addEventListener('click', hello))
このようなコードは、 をクリックすると、どのような言葉が印刷されますか?
・・・・・
答えは印刷Hello chengduで、clickを呼び出すと、thisは実はelementを指しています.つまり、最後に印刷されたnameは実は元素上のname属性です.
このように見ると問題は大きくないようです.問題はすぐに位置づけられるので、コードをこう変えます.
class Greeting { static from(control) { return new Greeting(control.value) } constructor(name) { this.name = name } hello() { console.log(`Hello ${this.name || 'world'}!`) } } [...myForm.elements] .map(e => [e, Greeting.from(e)]) .forEach(([e, {hello}]) => e.addEventListener('click', hello))
私達の実行ロジックはinput上のvalueをプリントしたいです.プリントアウトしたら、間違いなくHello chengduをプリントしました.実はこのコードは上記と比べてBugがあります.今度メンテナンスしたら、バグが出ても位置決めが難しいです.Promiseの中のthisを見に来ました.
Promise.all(numberPromises)
    .then(values => {
        const nonNumbers = values.filter(isNaN)
        return Promise.all(values.concat(nonNumbers.map(Promise.reject)))
    })
    .then(nextStep)
    .catch(errorLogger)
これは文章が変なコードです.つまり、数字ではない値を選別して、組織でないものがあれば、catchに入れ、errorを呼び出します.error Loggarで情報を印刷する時、私達は数字ではない値を印刷したいと思いますが、実際にcatchに行ったら、印刷したのは
PromiseReject caled on non-object
なぜですか?Promise.rejectは静的な関数だと思いますが、標準Promiseの定義にはthisが必要です.Promiseの定義には布団類化が可能です.Promise.rejectを呼び出したら、彼は現在のthisが指している種類を見ます.Promiseサブクラスであれば、Promiseサブクラスのインスタンスを作成します.ほとんどの人にとっては、Promise.rejectにthisが必要かどうかはよく分かりませんよね.
thisの問題
習っても問題があるかもしれません.具体的には:
  • 穴を掘ることができます.
  • は、
  • を隠すことができます.
  • 位置決めが難しい
  • ES 6ソリューション
    JSでは、thisを使っているところは主にあります.
  • 一般関数
  • コールバック関数
  • コンストラクタ
  • メソッド
    ES 6において、私たちはclassを提供して、コンストラクタthisの問題を解決し、allow functionを提供して解決しました.実は私たちはthisの判断に失敗する可能性がある場合、 および がまだあります.普通の状況では、私たち自身のコードはまだthisを指していますが、第三者ライブラリとフレームワークについては、何を指しているのか分かりにくいです.賀さんは次のT 39会議で提出します.言語レベルでthisの曖昧さを解決します.
    Outdated draft:gilbert/es-explicit-this
    thisを表示
    関数では、thisは実は隠蔽変数として開発者に提供されていることを知っています.thisを表示してもいいですか?TSでは、このようなコードが使えます.
    Number.prototype.toHex = function (this: number) {
        return this.toString(16)
    }
    
    表示されたthisはnumberタイプのコードであり、thisを示す方式を参考にして、次のようなコードを構築することができます.
    function getX(this) { //    this
        return this.x
    }
    function getX(this o) { //   
        return o.x
    }
    function getX(this {x}) { //   
        return x
    }
    
    このように書くと、何かメリットがありますか?
    例1
    // original code
    class Player {
        attack(opponent) {
            return Game.calculateResult(
                this.input(),
                opponent.input(),
            )
        }
    }
    
    // better naming
    class Player {
        attack(this offense, defense) {
            return Game.calculateResult(
                offense.input(),
                defense.input(),
            )
        }
    }
    
    別名を通してコードの可読性を高めることができます.
    例2
    // original code
    function process (name) {
        this.taskName = name;
        const that = this
        doAsync(function (amount) {
            this.x += amount;
            that.emit('change', this)
        });
    };
    
    
    // better naming
    function process (this obj, name) {
        obj.taskName = name;
        doAsync(function callback (this result, amount) {
            result.amount += 2;
            obj.emit('change', result)
        });
    };
    
    私たちは、同じ名前の変数は前者をカバーし、thisも例外ではないことを知っています.この例はthisの方法で、 thisが欲しいものであることを保証します.別の変数を定義しなくてもいいです.
    例3
    function div(@int32 this numerator, @int32 denominator) {
        // if (numerator !== numerator|0) throw new TypeError()
        // if (denominator !== denominator|0) throw new TypeError()
        // ...
    }
    
    
    表示されたthisを使用した後、いくつかの共通論理処理thisをthisでカプセル化することもできます.この提案は主にdecoratorが紛らわしい問題を指摘して解決しました.ここで、賀さんはミスを指摘している問題を早期発見するために、別の提案を出しました.
    Outdated draft:hax/proposal-functionn-this
    具体的な属性を追加しました.thisを使用して、この属性を使用して、予め私たちがミスをしたかどうかを発見しました.例えば、API thisArgumentExpectedを定義します.
    // safer API:
    function on(eventTarget, eventType, listener, options) {
        if (listener.thisArgumentExpected) throw new TypeError('listener should not expect this argument')
        return eventTarget.addEventListener(eventType, listener, options)
    }
    
    このアプリがonがtrueであることを発見したら、事前にエラーを投げて、クリックを待つ必要がない時にエラーを発見します.早ければ早いほど、コードの安定性がよくなります.また、場合によってはthisArgumentExpectedthisArgumentExpectedも自動的に変化します.
    //     
    let arrow = () => { this }
    arrow.thisArgumentExpected // false
    
    //   bind   
    let bound = f1.bind()
    bound.thisArgumentExpected // false
    
    //    bind     
    function func() {}
    func.thisArgumentExpected // false
    
    //   bind     
    function implicitThis() { this }
    implicitThis.thisArgumentExpected // true
    
    //   this     
    function explicitThis(this) {}
    explicitThis.thisArgumentExpected // true
    
    //   
    class C {
        m1() {}
        m2() { this }
        m3(this) {}
        m4() { super.foo }
        static m1() {}
        static m2() { this }
        static m3(this) {}
        static m4() { super.foo }
    }
    C.prototype.m1.thisArgumentExpected // false
    C.prototype.m2.thisArgumentExpected // true
    C.prototype.m3.thisArgumentExpected // true
    C.prototype.m4.thisArgumentExpected // true
    C.m1.thisArgumentExpected // false
    C.m2.thisArgumentExpected // true
    C.m3.thisArgumentExpected // true
    C.m4.thisArgumentExpected // true
    C.thisArgumentExpected // null
    
    このような形式によって、対応するAPIの増加thisArgumentExpectedを検出すると、私たちのコードがthisArgumentExpectedでエラーを指す可能性があるかどうかを事前に知ることができる.
    締め括りをつける
    上の紹介は今は提案だけで、実際に支持していません.合格するかどうかは分かりません.しかし紹介から見て、確かにthisの問題を解決しました.第三者倉庫とフレームを開発した学生にとってはいいニュースです.普通の開発者にとっても、間接的に開発体験を高めることができます.
    ここで述べた提案の住所
    Outdated draft:gilbert/es-explicit-this Outdated draft:hax/proposal-function-this