>  기사  >  웹 프론트엔드  >  당신이 알고 싶은 모든 것이 여기에 있습니다

당신이 알고 싶은 모든 것이 여기에 있습니다

阿神
阿神원래의
2017-01-23 16:12:061119검색

자바스크립트를 일상적으로 사용하거나 프론트엔드 면접 과정에서 나타나는 비율이 매우 높습니다. 이는 의심할 여지없이 이것의 중요성을 보여줍니다. 그러나 이는 매우 유연하기 때문에 많은 사람들이 이 동작을 이해하기 어렵게 만듭니다. 이 기사는 이것이 왜 필요한지부터 시작하여 이에 대한 6가지 규칙을 요약하여 귀하의 혼란에 답하는 데 도움이 되기를 바랍니다.

소개

이 매개변수는 실제로 개발 중에 수동으로 전달될 수도 있고 JS 또는 세 번째 세 당사자로부터 수신됩니다.
이 매개변수는 일반적으로 함수가 실행될 때 "소유자"를 가리킵니다. 이 메커니즘은 기능 설계를 더욱 간결하고 재사용 가능하게 만들 수 있습니다.

함수가 실행될 때 바인딩됩니다. 총 6가지 바인딩 규칙이 있습니다.

●new 바인딩: new 키워드를 사용하여 객체를 생성할 때 this가 바인딩됩니다. 생성된 개체에 대해

● 명시적 바인딩: 호출, 적용 또는 바인드 메서드를 사용하여 명시적으로 바인딩하는 경우 첫 번째 매개변수입니다.

●암시적 바인딩: 객체에 함수가 실행되면 시스템이 이를 객체에 암시적으로 바인딩합니다.

●기본 바인딩: 함수가 독립적으로 실행될 때 엄격 모드에서 this의 기본 바인딩 값은 정의되지 않고, 그렇지 않으면 전역 개체입니다.

●화살표 함수 바인딩: 화살표 함수를 사용할 때 this의 바인딩 값은 외부 일반 함수(또는 전역 개체 자체)의 this와 동일합니다.

●시스템 또는 제3자 바인딩: 시스템이나 제3자가 제공하는 인터페이스에 함수가 매개변수로 전달되면 전달된 함수의 해당 함수가 시스템 또는 제3자에 의해 바인딩됩니다.

이 메커니즘의 역할은 객체를 암시적으로 전달하는 우아한 방법을 제공하여 기능 설계를 더욱 간결하고 재사용 가능하게 합니다.

클릭하면 배경이 빨간색으로 바뀌는 두 개의 버튼에 대한 다음 예를 살펴보세요.

function changeBackgroundColor(ele) {
  ele.style.backgroundColor = 'red';
}
btn1.addEventListener('click',function () {
  changeBackgroundColor(btn1);
});
btn2.addEventListener('click',function () {
  changeBackgroundColor(btn2);
});

여기서는 클릭한 요소를 명시적으로changeBackgroundColor 함수에 전달합니다. 그러나 실제로는 암시적으로 컨텍스트를 전달하는 이 기능을 활용하여 현재 클릭된 요소를 함수에서 직접 가져올 수 있습니다. 다음과 같습니다:

function changeBackgroundColor() {    this.style.backgroundColor = 'red';
}
btn1.addEventListener('click',changeBackgroundColor);
btn2.addEventListener('click',changeBackgroundColor);

첫 번째 예에서는 클릭한 요소가 이 형식 매개변수인 ele로 대체됩니다. 두 번째 예에서는 특수 키워드 this로 대체되었습니다. 이는 형식 매개변수와 유사한 기능을 가지고 있으며, 본질적으로 객체에 대한 참조이며, 수동으로 값을 전송할 필요가 없으므로 사용이 더 간단하고 편리하다는 것이 특징입니다.

6가지 규칙

실제로 이것이 가리키는 대상이 무엇인지 가장 혼란스럽습니다. 이 문서에서는 6가지 유형의 시나리오를 분류하고 이에 대한 6가지 바인딩 규칙을 요약합니다.

1.new 바인딩

new를 사용하여 객체를 생성할 때 클래스의 this를 참조합니다. 무엇입니까?

class Person {
  constructor(name){
    this.name = name;
  }
  getThis(){
    return this
  }
}
const xiaoMing = new Person("小明");
console.log(xiaoMing.getThis() === xiaoMing); // true
console.log(xiaoMing.getThis() === Person); // false
console.log(xiaoMing.name === "小明"); // true

위의 예에서 Person 클래스는 ES6 구문을 사용하여 생성되었습니다. new 키워드를 사용하여 개체를 생성하는 과정에서 이는 시스템에 의해 생성된 개체, 즉 xiaoMing에 자동으로 바인딩됩니다.

규칙 1: new 키워드를 사용하여 객체를 생성할 때 생성된 객체에 바인딩됩니다.

2. 명시적 바인딩


시나리오 2, 호출, 적용 및 바인딩 메서드를 사용하여 이 매개변수를 명시적으로 바인딩합니다.

call을 예로 들어 보겠습니다. call 메소드에 전달된 첫 번째 매개변수는 이것이 참조하는 객체입니다.

function foo() {  console.log( this === obj ); // true
  console.log( this.a === 2 ); // true}const obj = {
  a: 2};
foo.call( obj );

명시적 전달의 경우 이것이 가리키는 객체가 분명하며 이는 호출, 적용 또는 바인드 메소드의 첫 번째 매개변수입니다.

규칙 2: 호출, 적용 또는 바인드 메소드를 사용하여 명시적으로 바인딩하는 경우 이것이 첫 번째 매개변수입니다.

3. 암시적 바인딩


명시적 바인딩과 암시적 바인딩의 차이점은 개발자의 몫입니다. 암시적으로 바인딩되면 함수나 메서드에 "소유자"가 있고 이 "소유자"는 직접 호출되는 함수나 메서드 개체를 나타냅니다.

예제 1

먼저 가장 간단한 예를 살펴보겠습니다.

function bar() {  console.log( this === obj );
}const obj = {
  foo: function () {    console.log( this === obj );
  },
  bar: bar
};
obj.foo(); // trueobj.bar(); // true


foo 함수는 obj 객체에 직접 걸려 있고, bar 함수는 외부에 정의된 후 obj 객체에 걸려 있습니다. 함수가 어디에 정의되어 있든 상관없이 함수가 최종적으로 호출될 때 함수의 "소유자"는 obj입니다. 따라서 이는 함수가 호출될 때 "소유자" obj를 가리킵니다.

예제 2

더 깊은 이해를 위해 새로운 객체에 기능을 재할당하는 경우를 생각해 보겠습니다.

function bar() {  console.log( this === obj1 ); // false
  console.log( this === obj2 ); // true}const obj1 = {
  foo: function () {    console.log( this === obj1 ); // false
    console.log( this === obj2 ); // true
  },
  bar: bar
};const obj2 = {
  foo: obj1.foo,
  bar: obj1.bar
};

obj2.foo();
obj2.bar();

이 예에서는 obj1의 foo 및 bar 메소드가 obj2에 할당됩니다. 함수가 호출되면 "소유자"는 obj1이 아니라 obj2입니다. 따라서 이것은 obj2를 가리킵니다.

예 3

객체는 여러 수준에 중첩될 수 있습니다. 이 경우 함수가 실행될 때 함수의 "소유자"는 누구입니까?

const obj1 = {
  obj2: {
    foo: function foo() {      console.log( this === obj1 );      // false
      console.log( this === obj1.obj2 ); // true
    }
  }
};

obj1.obj2.foo()

foo 메서드/함수의 직접 호출자는 obj1이 아니라 obj2입니다. 따라서 함수의 "소유자"는 가장 가까운 직접 호출자를 가리킵니다.

예시 4

如果一个方法/函数,在它的直接对象上调用执行,又同时执行了 call 方法,那么它是属于隐式绑定还是显式绑定呢?

const obj1 = {
  a: 1,
  foo: function () {    console.log(this === obj1); // false
    console.log(this === obj2); // true
    console.log(this.a === 2);  // true
  }
};const obj2 = {
  a: 2};

obj1.foo.call(obj2); // true

由上,可以看出,如果显式绑定存在,它就不可能属于隐式绑定。

规则三:如果函数是挂在对象上执行的,这个时候系统会隐式的将 this 绑定为函数执行时的“拥有者”。

4.默认绑定

前一小段,讨论了函数作为对象的方法执行时的情况。本小段,要讨论的是,函数独立执行的情况。

在函数直接调用的情况下,this 绑定的行为,称之为默认绑定。

例一

为了简单起见,先讨论在浏览器的非严格模式的下绑定行为。

function foo() {  console.log( this === window); // true}
foo();

在上面的例子中,系统将 window 默认地绑定到函数的 this 上。

例二

在这里,先介绍一种我们可能会在代码中见到的显式绑定 null 的写法。

function foo() {  console.log( this == window ); // true}

foo.apply(null);

将例一默认绑定的情况,改为了显式绑定 null 的情况。

在实际开发中,我们可能会用到 apply 方法,并在第一个参数传入 null 值,第二个参数传入数组的方式来传递数组类型的参数。这是一种传统的写法,当然现在可以用 ES6 的写法来代替,但是这不在本文的讨论范围内。

在本例最需要关注的是,this 竟然指向的 window 而不是 null。个人测试的结果是,在函数独立调用时,或者显式调用,传入的值为 null 和 undefined 的情况下,会将 window 默认绑定到 this 上。

在函数多次调用,形成了一个调用栈的情况下,默认绑定的规则也是成立的。

例三

接着,探讨下严格模式下,this 的默认绑定的值。

"use strict";

function foo() {
  console.log( this === undefined );
}

foo();               // true
foo.call(undefined); // true
foo.call(null);      // false

在严格模式下,this 的默认绑定的值为 undefined。

规则四:在函数独立执行的情况下,严格模式 this 的默认绑定值为 undefined,否则默认绑定的值为 window。

5.箭头函数绑定

箭头函数实际上,只是一个语法糖,实际上箭头函数中的 this 实际上是其外层函数(或者 window/global 本身)中的 this。

// ES6
function foo() {
  setTimeout(() => {
    console.log(this === obj); // true
  }, 100);
}

const obj = {
  a : 1
}

foo.call(obj);

// ES5
function foo() {
  var _this = this;

  setTimeout(function () {
    console.log(_this === obj); // true
  }, 100);
}

var obj = {
  a : 1
}

foo.call(obj);

规则五:使用箭头函数时,this 的绑定值和其外层的普通函数(或者 window/global 本身) this 绑定值相同。

6.系统或第三方绑定

在 JavaScript 中,函数是第一公民,可以将函数以值的方式,传入任何系统或者第三方提供的函数中。现在讨论,最后一种情况。当将函数作为值,传入系统函数或者第三方函数中时,this 究竟是如何绑定的。

我们在文章一开始提到的,两个按钮例子,系统自动将 this 绑定为点击的按钮。

function changeBackgroundColor() {    console.log(this === btn1); // true}

btn1.addEventListener('click',changeBackgroundColor);

接着测试系统提供的 setTimeout 接口在浏览器和 node 中绑定行为。

// 浏览器
setTimeout(function () {
  console.log(this === window); // true
},0)

// node
setTimeout(function () {
  console.log(this === global); // false
  console.log(this); // Timeout
},0)

很神奇的是,setTimeout 在 node 和浏览器中的绑定行为不一致。如果我们将 node 的中的 this 打印出来,会发现它绑定是一个 Timeout 对象。

如果是第三发提供的接口,情况会更加复杂。因为在其内部,会将什么值绑定到传入的函数的 this 上,事先是不知道的,除非查看文档或者源码。

系统或者第三方,在其内部,可能会使用前面的五种规则一种或多种规则,对传入函数的 this 进行绑定。所以,规则六,实际上一条在由前五条规则上衍生出来的规则。

规则六:调用系统或者第三方提供的接口时,传入函数中的 this 是由系统或者第三方绑定的。

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