>  기사  >  웹 프론트엔드  >  JavaScript의 이 포인팅 문제에 대한 심층 분석

JavaScript의 이 포인팅 문제에 대한 심층 분석

巴扎黑
巴扎黑원래의
2017-04-29 15:50:241106검색

JavaScript에서 이 포인팅 문제를 설명하는 많은 블로그가 있으며 여전히 많은 사람들이 질문합니다.

많은 일반 언어와 달리 JavaScript 함수의 this 포인터는 함수가 정의될 ​​때가 아니라 호출될 때 결정됩니다. 즉, 함수가 어떻게 호출되는지에 따라 이것이 무엇을 가리키는지 결정됩니다.

JavaScript에는 함수를 호출하는 세 가지 일반적인 방법이 있습니다: 직접 호출, 메서드 호출, 새 호출. 그 밖에도 바인딩()을 통해 함수를 객체에 바인딩한 후 호출하거나, call(), Apply() 등을 통해 호출하는 등 특수한 호출 방법이 있습니다. es6에 화살표 함수가 도입된 후 화살표 함수가 호출되면 해당 포인터가 달라집니다. 이러한 상황에서 this 포인터를 분석해 보겠습니다.

에 직접 전화하세요 직접 호출은 함수 이름(...)을 통해 호출됩니다. 이때 함수 ​​내부의 this는 전역 객체를 가리킵니다. 브라우저에서는 전역 객체가 window이고, NodeJs에서는 전역 객체가 전역입니다.

예를 살펴보겠습니다.

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

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

test();    // 直接调用

여기서 주목해야 할 점은 직접 호출은 전역 범위에서 호출하는 것을 의미하지 않는다는 점입니다. 어떤 범위에서든 함수 이름(...)을 통해 직접 함수를 호출하는 것을 직접 호출이라고 합니다. 예를 들어 다음 예에서는

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

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

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

를 직접 호출하기도 합니다. ​바인드()가 직접 호출에 미치는 영향

주목해야 할 또 다른 사항은 바인드()의 영향입니다. Function.prototype.bind()의 함수는 현재 함수를 지정된 객체에 바인딩하고 새 함수를 반환하는 것입니다. 호출 방법에 관계없이 이 새 함수는 항상 바인딩된 객체를 가리킵니다. 예를 살펴보겠습니다.

const obj = {};

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

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

그렇다면 바인딩()은 무엇을 하는가? 이것이 어떻게 영향을 미치는지 이해하기 위해 바인드()를 시뮬레이션해 보겠습니다.

아아아아

위의 예에서 볼 수 있듯이 먼저 클로저를 통해 바인딩된 개체인 대상을 유지한 다음 함수가 호출될 때 원래 함수에 적용 메서드를 사용하여 함수의 this를 지정합니다. . 물론 기본 바인딩() 구현은 다르고 더 효율적일 수 있습니다. 하지만 이 예제는 바인딩()이 무엇을 할 수 있는지 보여줍니다.

​콜과 신청이 미치는 영향

위의 예제에서는 Function.prototype.apply()가 사용되었으며 Function.prototype.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

와 같이 적용 및 호출이 예상대로 실행되지 않습니다. 바인딩()이 함수에 큰 영향을 미친다는 것을 알 수 있으므로 주의해서 사용하세요!

메소드 호출

메소드 호출은 객체를 통해 해당 메소드 함수를 호출하는 것을 의미합니다. object.method 함수(...) 형식입니다. 이 경우 함수의 this는 메서드가 호출되는 개체를 가리킵니다. 그러나 바인드()의 효과도 알고 있어야 합니다.

아아아아

여기서 주목해야 할 점은 마지막 세 가지 메소드는 모두 미리 정의된 함수이며 obj 객체에 해당 메소드로 첨부된다는 것입니다. 다시 말하지만, 함수 내부의 this 포인터는 정의와 아무 관련이 없으며 호출 메서드의 영향을 받습니다.

메소드의 this가 전역 객체를 가리키는 경우

여기서 말하는 내용은 메서드 호출이 아니라 메서드에 있다는 점에 유의하세요. 메소드 내에서 이는 전역 객체를 가리킵니다. 바인딩() 때문이 아니라면

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()에 매개변수로 전달된 후 이벤트가 트리거될 때 메서드 호출 대신 이 함수가 직접 호출되므로 이것이 전역 개체를 가리킵니다. 이 문제를 해결하는 방법은 여러 가지가 있습니다

아아아아

다만, 화살표 함수를 jQuery 콜백으로 사용하는 경우 함수 내에서 사용 시 주의가 필요하다는 점에 유의하시기 바랍니다. jQuery This는 대부분의 콜백 함수(화살표가 아닌 함수)에서 호출 대상을 나타내므로 $(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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.