ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript におけるこのポインティング問題の詳細な分析

JavaScript におけるこのポインティング問題の詳細な分析

巴扎黑
巴扎黑オリジナル
2017-04-29 15:50:241080ブラウズ

JavaScript でのこのポインティングの問題について説明しているブログは数多くあり、今でも多くの人が質問しています。

多くの一般的な言語とは異なり、JavaScript 関数の this ポインターは、関数が定義されたときではなく、呼び出されたときに決定されます。つまり、関数の呼び出し方法によって、これが何を指すかが決まります。

JavaScript では、関数を呼び出す一般的な方法として、直接呼び出し、メソッド呼び出し、新規呼び出しの 3 つがあります。さらに、bind() を介して関数をオブジェクトにバインドしてから呼び出す、call() や apply() を介して呼び出すなど、特別な呼び出しメソッドがいくつかあります。 es6 でアロー関数が導入されてから、アロー関数が呼び出されるときの this ポインタが異なります。このような状況での this ポインタを分析してみましょう。

に直接電話してください 直接呼び出しは関数名 (...) を通じて呼び出されます。このとき、関数内の this はグローバルオブジェクトを指します。ブラウザではグローバルオブジェクトは window、NodeJs ではグローバルオブジェクトがグローバルです。

例を見てみましょう:

// 简单兼容浏览器和 NodeJs 的全局对象
const _global = typeof window === "undefined" ? global : window;

function test() {
    console.log(this === _global);    // true
}

test();    // 直接调用

ここで注意すべき点の 1 つは、直接呼び出しは、グローバル スコープでの呼び出しを指すわけではないということです。どのスコープでも、関数名 (...) を使用して関数を直接呼び出すことは、直接呼び出しと呼ばれます。たとえば、次の例では

(function(_global) {
    // 通过 IIFE 限定作用域

    function test() {
        console.log(this === _global);  // true
    }

    test();     // 非全局作用域下的直接调用
})(typeof window === "undefined" ? global : window);

を直接呼び出します 直接呼び出しに対するbind()の影響

もう 1 つ注意すべき点は、bind() の影響です。 Function.prototype.bind() の機能は、現在の関数を指定されたオブジェクトにバインドし、新しい関数を返すことです。どのように呼び出されても、この新しい関数は常にバインドされたオブジェクトを指します。例を見てみましょう:

const obj = {};

function test() {
    console.log(this === obj);
}

const testObj = test.bind(obj);
test();     // false
testObj();  // true

では、bind() は何をするのでしょうか?これにどのような影響を与えるかを理解するために、bind() をシミュレートしてみましょう。

りー

上記の例からわかるように、まず、クロージャを通じて、バインドされたオブジェクトであるターゲットが維持され、次に関数が呼び出されるときに、元の関数で apply メソッドが使用され、関数の this が指定されます。 。もちろん、ネイティブのbind()実装はこれとは異なり、より効率的になる可能性があります。ただし、この例は、bind() で何ができるかを示しています。

電話と申請の影響

上記の例では Function.prototype.apply() が使用されていますが、Function.prototype.call() もそれに似ています。これら 2 つの方法の使用方法については、リンクからドキュメントを自分で読んでください。ただし、指定された関数が実行されるとき、最初のパラメーターはすべてこれを指します。

ただし、apply と call を使用するときは、ディレクトリ関数自体が this オブジェクトにバインドされた関数である場合、apply と call は期待どおりに実行されません (

const obj = {};

function test() {
    console.log(this === obj);
}

// 自定义的函数,模拟 bind() 对 this 的影响
function myBind(func, target) {
    return function() {
        return func.apply(target, arguments);
    };
}

const testObj = myBind(test, obj);
test();     // false
testObj();  // true

など)。 bind() は関数に大きな影響を与えることがわかりますので、注意して使用してください。

メソッド呼び出し

メソッド呼び出しとは、オブジェクトを通じてそのメソッド関数を呼び出すことを指します。これは、object.method 関数 (...) の形式になります。この場合、関数内の this は、メソッドが呼び出されるオブジェクトを指します。ただし、bind() の影響にも注意する必要があります。

りー

ここで注意する必要があるのは、最後の 3 つのメソッドはすべて事前定義された関数であり、そのメソッドとして obj オブジェクトにアタッチされるということです。繰り返しますが、関数内の this ポインターは定義とは何の関係もなく、呼び出しメソッドの影響を受けます。

メソッド内の this がグローバル オブジェクトを指している場合

ここで述べられていることは、メソッド呼び出しではなくメソッド内にあることに注意してください。メソッド内の this がグローバル オブジェクトを指している場合、bind() が原因ではない場合は、

const obj = {};

function test() {
    console.log(this === obj);
}

// 绑定到一个新对象,而不是 obj
const testObj = test.bind({});
test.apply(obj);    // true

// 期望 this 是 obj,即输出 true
// 但是因为 testObj 绑定了不是 obj 的对象,所以会输出 false
testObj.apply(obj); // false

などのメソッド呼び出しが原因である必要があります。 t は obj のテストメソッドですが、 t() が呼び出されるとき、これはグローバルを指します。

この状況が特に言及されている理由は、オブジェクト メソッドをコールバックとして関数に渡した後、実行結果が期待と一致しないことがよくあるためです。これは、呼び出しメソッドの影響が無視されているためです。たとえば、次の例は、ページ上の特定のものをカプセル化した後に特に発生しやすい問題です:

const obj = {
    // 第一种方式,定义对象的时候定义其方法
    test() {
        console.log(this === obj);
    }
};

// 第二种方式,对象定义好之后为其附加一个方法(函数表达式)
obj.test2 = function() {
    console.log(this === obj);
};

// 第三种方式和第二种方式原理相同
// 是对象定义好之后为其附加一个方法(函数定义)
function t() {
    console.log(this === obj);
}
obj.test3 = t;

// 这也是为对象附加一个方法函数
// 但是这个函数绑定了一个不是 obj 的其它对象
obj.test4 = (function() {
    console.log(this === obj);
}).bind({});

obj.test();     // true
obj.test2();    // true
obj.test3();    // true

// 受 bind() 影响,test4 中的 this 指向不是 obj
obj.test4();    // false

明らかに、 this.onButtonClick がパラメーターとして on() に渡された後、イベントがトリガーされると、この関数はメソッド呼び出しではなく直接呼び出されるため、これはグローバル オブジェクトを指すことになります。この問題を解決するにはたくさんの方法があります

const obj = {
    test() {
        console.log(this === obj);
    }
};

const t = obj.test;
t();    // false

ただし、アロー関数を jQuery コールバックとして使用する場合は、関数内でのこれの使用に注意する必要があることに注意してください。 jQuery ほとんどのコールバック関数 (アロー関数以外) では呼び出しターゲットを表すため、$(this).text() のようなステートメントを作成できますが、jQuery はアロー関数の this ポインタを変更できません。全然違う。 。

 new 调用

  在 es6 之前,每一个函数都可以当作是构造函数,通过 new 调用来产生新的对象(函数内无特定返回值的情况下)。而 es6 改变了这种状态,虽然 class 定义的类用 typeof 运算符得到的仍然是 "function",但它不能像普通函数一样直接调用;同时,class 中定义的方法函数,也不能当作构造函数用 new 来调用。

  而在 es5 中,用 new 调用一个构造函数,会创建一个新对象,而其中的 this 就指向这个新对象。这没有什么悬念,因为 new 本身就是设计来创建新对象的。

var data = "Hi";    // 全局变量

function AClass(data) {
    this.data = data;
}

var a = new AClass("Hello World");
console.log(a.data);    // Hello World
console.log(data);      // Hi

var b = new AClass("Hello World");
console.log(a === b);   // false

 箭头函数中的 this

  先来看看 MDN 上对箭头函数的说明

An arrow function expression has a shorter syntax than a function expression and does not bind its own this, arguments, super, or new.target. Arrow functions are always anonymous. These function expressions are best suited for non-method functions, and they cannot be used as constructors.

  这里已经清楚了说明了,箭头函数没有自己的 this 绑定。箭头函数中使用的 this,其实是直接包含它的那个函数或函数表达式中的 this。比如

const obj = {
    test() {
        const arrow = () => {
            // 这里的 this 是 test() 中的 this,
            // 由 test() 的调用方式决定
            console.log(this === obj);
        };
        arrow();
    },

    getArrow() {
        return () => {
            // 这里的 this 是 getArrow() 中的 this,
            // 由 getArrow() 的调用方式决定
            console.log(this === obj);
        };
    }
};

obj.test();     // true

const arrow = obj.getArrow();
arrow();        // true

  示例中的两个 this 都是由箭头函数的直接外层函数(方法)决定的,而方法函数中的 this 是由其调用方式决定的。上例的调用方式都是方法调用,所以 this 都指向方法调用的对象,即 obj。

  箭头函数让大家在使用闭包的时候不需要太纠结 this,不需要通过像 _this 这样的局部变量来临时引用 this 给闭包函数使用。来看一段 Babel 对箭头函数的转译可能能加深理解:

// ES6
const obj = {
    getArrow() {
        return () => {
            console.log(this === obj);
        };
    }
}
// ES5,由 Babel 转译
var obj = {
    getArrow: function getArrow() {
        var _this = this;
        return function () {
            console.log(_this === obj);
        };
    }
};

  另外需要注意的是,箭头函数不能用 new 调用,不能 bind() 到某个对象(虽然 bind() 方法调用没问题,但是不会产生预期效果)。不管在什么情况下使用箭头函数,它本身是没有绑定 this 的,它用的是直接外层函数(即包含它的最近的一层函数或函数表达式)绑定的 this。

以上がJavaScript におけるこのポインティング問題の詳細な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。