>웹 프론트엔드 >JS 튜토리얼 >JavaScript의 바인딩 방법 요약

JavaScript의 바인딩 방법 요약

angryTom
angryTom앞으로
2019-11-29 15:42:212199검색

최근에 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으로 문의하시기 바랍니다. 삭제