1. 범위
범위는 변수가 선언된 함수 본문과 이 함수 본문이 중첩된 모든 함수 본문에 정의됩니다.
function scope(){ var foo = "global"; if(window.getComputedStyle){ var a = "I'm if"; console.log("if:"+foo); //if:global } while(1){ var b = "I'm while"; console.log("while:"+foo);//while:global break; } !function (){ var c = "I'm function"; console.log("function:"+foo);//function:global }(); console.log( foo,//global a, // I'm if b, // I'm while c // c is not defined ); } scope();
(1) 범위 함수에 정의된 foo 변수는 자체적으로 액세스할 수 있을 뿐만 아니라 if 문, while 문 및 내장된 익명 함수에서도 액세스할 수 있습니다. 따라서 foo의 범위는 범위 함수 본문입니다.
(2) JavaScript에서는 if, while, for 및 기타 코드 블록이 독립적인 범위를 형성할 수 없습니다. 따라서 JavaScript에는 블록 수준 범위가 없고 함수 범위만 있습니다.
하지만 JS에는 특별한 경우가 있습니다.
var를 사용하여 변수를 선언하지 않으면 window에 이 속성이 있으므로 이 변수의 범위는 특정 함수 본문에 속하지 않고 창 개체에 속합니다.
function varscope(){ foo = "I'm in function"; console.log(foo);//I'm in function } varscope(); console.log(window.foo); //I'm in function
2. 스코프 체인(scope chain)
소위 스코프 체인은 다음과 같습니다. a 함수 본문에는 여러 계층의 함수 본문이 중첩되어 있으며, 동일한 변수가 다른 함수 본문에 정의되어 있습니다. 함수 중 하나가 이 변수에 액세스하면 범위 체인이 형성됩니다.
foo = "window"; function first(){ var foo = "first"; function second(){ var foo = "second"; console.log(foo); } function third(){ console.log(foo); } second(); //second third(); //first } first();
일 때 JS 엔진은 두 번째 범위를 연결 목록의 선두에 배치하고 그 다음 첫 번째 범위, 마지막으로 창 객체를 배치하므로 다음과 같은 범위 체인이 형성됩니다.
second->first->window 이때 JS 엔진은 스코프 체인을 따라 변수 foo를 검색하고 찾은 것은 "second"
third가 실행될 때 , third: third->first->window로 구성된 범위 체인이므로 "frist"
특수 사례: with 문
JS의 with 문은 주로 다음에서 사용됩니다. 범위 체인을 확장할 때 문의 개체가 범위의 머리 부분에 추가됩니다. with 문이 끝나면 범위 체인이 정상으로 돌아갑니다.
foo = "window"; function first(){ var foo = "first"; function second(){ var foo = "second"; console.log(foo); } function third(obj){ console.log(foo); //first with (obj){ console.log(foo); //obj } console.log(foo); //first } var obj = {foo:'obj'}; third(obj); } first();
third()를 실행하면 obj 객체가 전달되고 obj는 foo 속성을 갖습니다. with 문을 실행할 때 JS 엔진은 obj를 원래 연결 목록의 선두에 배치하여 범위를 형성합니다. 체인은 다음과 같습니다:
obj->third->first->window. 이때 발견된 foo는 obj의 foo이므로 출력은 "obj"이고 before 및 with 이후에는 원래 연결 목록을 따라 검색되는데, 이는 with 문이 끝난 후 범위 체인이 정상으로 돌아왔음을 보여줍니다.
3. 이 키워드
함수에서는 항상 현재 함수의 소유자 개체를 가리킵니다. this는 런타임 시 특정 포인터와 호출 개체만 확인할 수 있습니다.
이 문장은 이것에 대한 모든 것을 요약합니다, 기억하세요, 기억하세요, 기억하세요! (ps: 중요한 것을 세 번 말하세요!)
window.name = "window"; function f(){ console.log(this.name); } f();//window var obj = {name:'obj'}; f.call(obj); //obj
f()를 실행할 때 f()의 호출자는 window 객체이므로 "window"를 출력한다는 것은
f.call(obj)은 obj 객체에 f()를 배치하는 것을 의미합니다. 이때 f의 this는 obj이므로 출력은 "obj"
입니다. 4. 실제 적용
code1:
var foo = "window"; var obj = { foo : "obj", getFoo : function(){ return function(){ return this.foo; }; } }; var f = obj.getFoo(); f(); //window
code2:
var foo = "window"; var obj = { foo : "obj", getFoo : function(){ var that = this; return function(){ return that.foo; }; } }; var f = obj.getFoo(); f(); //obj
code1과 code2는 이 내용과 범위에 대한 가장 좋은 요약입니다. 실행 결과에 대한 의문이 있으면 토론하십시오!
코드 분석:
code1: 执行var f = obj.getFoo()返回的是一个匿名函数,相当于: var f = function(){ return this.foo; } f() 相当于window.f(), 因此f中的this指向的是window对象,this.foo相当于window.foo, 所以f()返回"window" code2: 执行var f = obj.getFoo() 同样返回匿名函数,即: var f = function(){ return that.foo; } 唯一不同的是f中的this变成了that, 要知道that是哪个对象之前,先确定f的作用域链:f->getFoo->window 并在该链条上查找that,此时可以发现that指代的是getFoo中的this, getFoo中的this指向其运行时的调用者,从var f = obj.getFoo() 可知此时this指向的是obj对象,因此that.foo 就相当于obj.foo,所以f()返回"obj"