JavaScript의 바인딩 방법 요약

angryTom
풀어 주다: 2019-11-29 15:42:21
앞으로
2146명이 탐색했습니다.

최근에 js에 대한 기본 지식을 복습하다가 "JS You Don't Know" 시리즈를 다시 읽었습니다. 이것이 항상 최우선 과제이므로 향후 복습을 용이하게 하기 위해 이와 관련된 지식을 체계적으로 요약하기로 결정했습니다. .

JavaScript의 바인딩 방법 요약

이 네 가지 바인딩 규칙

1. 기본 바인딩

가장 일반적으로 사용되는 함수 호출 유형입니다. 독립 함수 호출(즉, 함수 참조를 사용하지 않고 직접 함수를 만듭니다.) 수정)이라고 합니다. 다른 규칙을 적용할 수 없는 경우 이 규칙을 기본 규칙으로 생각하세요.

기본값은 비엄격 모드에서는 window를 가리키고 엄격 모드에서는 undefine을 가리킵니다. 예를 들어 비엄격 모드에서 다음 함수 foo는 다음과 같습니다.

var a = 2;
function foo(){
    var a = 3;
    console.log(this.a);
}
foo(); //2
로그인 후 복사

[관련 과정 권장 사항: JavaScript 비디오 튜토리얼]

여기 foo() 메서드의 This는 창을 가리키므로 window.a = 2;

엄격 모드에서 this.는 정의되지 않음을 가리키므로 this.a에 액세스하면 오류가 보고됩니다.

var a = 2;
function foo(){
    "use strict";
    var a = 3;
    console.log(this.a);
}
foo(); //Uncaught TypeError: Cannot read property 'a' of undefined
로그인 후 복사

2. 숨겨진 암시적 바인딩

호출 위치에 컨텍스트 개체가 있거나 개체에 의해 "소유"되거나 "포함"된 경우 암시적 바인딩이 사용됩니다.

function foo() {
    console.log( this.a );
}
var obj = {
    a: 2,
    foo: foo
};
obj.foo(); // 2
로그인 후 복사

위의 예에서 foo는 obj.foo()를 통해 호출됩니다. 호출 위치는 obj 컨텍스트를 사용하여 함수를 참조하므로 foo의 이는 obj를 가리킵니다.

그리고 foo는 obj에 참조로 추가되는데, obj에 직접 정의하든지 먼저 정의한 후 참조 속성으로 추가하든 엄밀히 말하면 foo는 obj에 속하지 않으므로 "owned"와 " 위의 정의에서 "포함"은 이해를 돕기 위해 따옴표로 묶여 있습니다.

일반적인 암시적 호출 시나리오:

obj.fn();
arguments[i]();//其实就是将点的调用方式变为了[]调用
el.onClick(function(){console.log(this);//this指向el})
로그인 후 복사

암시적 손실

먼저 코드 부분을 살펴보겠습니다.

function foo() {
    console.log( this.a );
}
var obj = {
    a: 2,
    foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "global"; // a 是全局对象的属性
bar(); // "global"
로그인 후 복사

위 코드는 실제로 호출 메서드인 bar()에만 의존합니다. 이는 실제로 코드입니다. 데코레이트된 함수가 호출되므로 기본 바인딩이 적용됩니다.

암시적 손실을 유발하는 또 다른 매개변수 전달 방법이 있습니다. 원리는 실제로 위의 예와 동일합니다.

function foo() {
    console.log( this.a );
}
function doFoo(fn) {
    // fn 其实引用的是foo
    fn(); // <-- 调用位置!
}
var obj = {
    a: 2,
    foo: foo
};
var a = "global"; // a 是全局对象的属性
doFoo( obj.foo ); // "global"
로그인 후 복사

디스플레이 바인딩

이 값을 지정하려면 호출, 적용 및 바인딩 메서드를 사용하세요. 이 바인딩 방법을 디스플레이 바인딩이라고 합니다.

function foo() {
    console.log( this.a );
}
var obj = {
    a:2
};
foo.call( obj ); // 2
로그인 후 복사

foo.call(obj)을 통해 호출 시 foo의 this가 obj에 바인딩되도록 강제할 수 있습니다.

새 바인딩

new 연산자는 인스턴스화인 "생성자" 인스턴스를 기반으로 새 객체를 생성할 수 있습니다. new의 과정은 다음과 같습니다:

● 완전히 새로운 객체를 생성(또는 구성)합니다.

● 이 새로운 개체는 [[ 프로토타입]]으로 연결됩니다.

● 이 새 객체는 함수 호출의 this에 바인딩됩니다.

● 함수가 다른 개체를 반환하지 않는 경우 새 표현식의 함수 호출은 자동으로 새 개체를 반환합니다.

new의 인스턴스화 프로세스를 명확히 한 후 다음 코드를 생각해 보세요.

function foo(a) {
    this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
로그인 후 복사

new foo(2) 후에 새 인스턴스 객체 막대가 생성되고 새 객체 막대가 foo 함수에서 여기에 바인딩됩니다. 이것은 .a = a 이후에 실행되며 a는 실제로 bar.a

Priority

에 할당됩니다. 일반적으로 this의 바인딩은 위의 네 가지 바인딩 규칙을 기반으로 하므로 동시에 나타날 때 어떤 순서로 진행되나요? 구체적인 규칙은 다음과 같습니다.

new(새 바인딩)에서 함수가 호출됩니까? 그렇다면 새로 생성된 객체(var bar = new foo())에 바인딩됩니다.

호출을 통해 호출되는 함수는 적용(명시적 바인딩)인가요, 아니면 하드 바인딩인가요? 그렇다면 이는 지정된 객체( var bar = foo.call(obj2) )에 바인딩됩니다.

일부 컨텍스트 개체에서 함수가 호출됩니까(암시적으로 바인딩됨)? 그렇다면 이는 해당 컨텍스트 개체에 바인딩됩니다. ( var bar = obj1.foo() )

둘 다 아니라면 기본 바인딩을 사용하세요. 엄격 모드에 있으면 정의되지 않은 상태로 바인딩되고, 그렇지 않으면 전역 개체에 바인딩됩니다. (var bar = foo())

바인딩 예외

1. call, appy 및 바인딩과 같은 명시적 바인딩 방법을 사용하면 매개변수가 null로 전달되거나 컨텍스트로 정의되지 않은 경우 함수 호출은 계속해서 다음을 사용합니다. 기본 Binding

function foo() {
    console.log( this.a );
}
var a = 2;
foo.call( null ); // 2
로그인 후 복사

어떤 상황에서 컨텍스트를 null로 전달해야 합니까?

1. 바인드 함수를 사용하여 currying

function foo(a,b) {
    console.log(a,b);
}
// 使用 bind(..) 进行柯里化
var bar = foo.bind( null, 2 );
bar( 3 ); // 2,3
로그인 후 복사

2. Apply(..)를 사용하여 배열을 확장하고 이를 매개변수로 함수에 전달합니다.

function foo(a,b) {
    console.log(a,b);
}
// 把数组展开成参数
foo.apply( null, [2, 3] ); // 2,3
로그인 후 복사

사실 위의 두 가지 사용 시나리오는 신경 쓰지 않습니다. /app/bind의 첫 번째 매개변수 값은 무엇입니까? 자리 표시자 값을 전달하고 싶습니다.

그러나 항상 null을 전달하면 추적하기 어려운 버그가 발생할 수 있습니다. 예를 들어 사용 중인 타사 라이브러리의 함수에 이 버그가 있으면 전역 개체에 잘못 바인딩되어 예측할 수 없는 버그가 발생할 수 있습니다. 결과(전역 변수 수정)

var a = 1;//全局变量
const Utils = {
    a: 2,
    changeA: function(a){
        this.a = a;
    }
}
Utils.changeA(3);
Utils.a //3
a //1
Utils.changeA.call(null,4);
Utils.a //3
a //4,修改了全局变量a!
로그인 후 복사

더 안전한 접근:

var o = Object.create(null);
Utils.changeA.call(o,6);
a //1, 全局变量没有修改
o.a // 6 改的是变量o
로그인 후 복사

2.

function foo() {
    console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2
로그인 후 복사

赋值表达式p.foo = o.foo 的返回值是目标函数的引用,因此调用位置是foo() 而不是p.foo() 或者o.foo()。根据我们之前说过的,这里会应用默认绑定。

this词法(箭头函数)

上述的几种规则适用于所有的正常函数,但不包括ES6的箭头函数。箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域(词法作用域)来决定this

function foo() {
// 返回一个箭头函数
    return (a) => {
        //this 继承自foo()
        console.log( this.a );
    };
}
var obj1 = {
    a:2
};
var obj2 = {
    a:3
};
var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, 不是3 !
로그인 후 복사

foo() 内部创建的箭头函数会捕获调用时foo() 的this。由于foo() 的this 绑定到obj1,bar(引用箭头函数)的this 也会绑定到obj1,箭头函数的绑定无法被修改。(new 也不行!)

几个例子加深理解

this的理论知识讲解得差不多了,来几个例子看看自己有没有理解全面:

1.经典面试题:以下输出结果是什么

var length = 10;
function fn() {
    console.log(this.length);
}
var obj = {
  length: 5,
  method: function(fn) {
    fn();
    arguments[0]();
  }
};
obj.method(fn, 1);
로그인 후 복사

obj中method方法里面调用了两次fn。第一次是直接调用的“裸露”的fn,因此fn()中this使用默认绑定,this.length为10.第二次调用时通过arguments0的方式调用的,arguments[0]其实指向的就是fn,但是是通过obj[fn]这种对象上下文的隐式绑定的,因此this指向arguments,而arguments只有一个一项(method中只有fn一个参数),因此arguments.length为1。因此打印的结果为:

10
1
로그인 후 복사

2.以下输出什么

var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = function () {
            return new Date().getFullYear() - this.birth; // this指向window或undefined
        };
        return fn();
    }
};
obj.getAge();
로그인 후 복사

答案是严格模式下会报错,非严格模式下输出NaN

原因也是因为在调用obj.getAge()后,getAge方法内的this使用隐式绑定。但是return fn()的时候用的是“裸露的fn”使用默认绑定,fn里面的this指向window或者undefined。

使用箭头函数来修正this的指向:

var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
        return fn();
    }
};
obj.getAge(); // 25
로그인 후 복사

使用箭头函数后,fn中的this在他的词法分析阶段就已经确定好了(即fn定义的时候),跟调用位置无关。fn的this指向外层的作用域(即getAge中的this)

3.以下输出为什么是'luo'

var A = function( name ){ 
    this.name = name;
};
var B = function(){ 
    A.apply(this,arguments);
};
B.prototype.getName = function(){ 
    return this.name;
};
var b=new B(&#39;sven&#39;);  // B {name: "luo"}
console.log( b.getName() ); // 输出:  &#39;luo&#39;
로그인 후 복사

执行new B('seven')后会返回一个新对象b,并且B函数中的this会绑定到新对象b上,B的函数体内执行A.apply(this.arguments)也就是执行b.name = name;这个时候b的值就是{name:'luo'},所以b.getName()就能输出'luo'啦~

实际在业务使用中,逻辑会更复杂一些,但是万变不离其宗,都按照上面写的规则来代入就好了

本文来自 js教程 栏目,欢迎学习!  

위 내용은 JavaScript의 바인딩 방법 요약의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:cnblogs.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿