>웹 프론트엔드 >JS 튜토리얼 >JavaScript 고급 프로그래밍(3판) 학습 노트 8 js 함수(2부)_기본 지식

JavaScript 고급 프로그래밍(3판) 학습 노트 8 js 함수(2부)_기본 지식

WBOY
WBOY원래의
2016-05-16 17:49:25995검색
6. 실행 환경 및 범위

(1) 실행 컨텍스트: 모든 JavaScript 코드는 실행 환경에서 실행됩니다. 제어권이 JavaScript 실행 파일로 전달되면 코드가 실행됩니다. 환경. 활성 실행 환경은 논리적으로 스택을 형성합니다. 전역 실행 환경은 항상 이 스택의 맨 아래 요소이고, 스택의 맨 위 요소는 현재 실행 중인 실행 환경입니다. 각 함수에는 고유한 실행 환경이 있습니다. 실행 흐름이 함수에 들어가면 이 함수의 실행 환경이 스택의 맨 위로 푸시됩니다. 함수가 실행된 후 실행 환경이 팝업되고 제어가 이전으로 돌아갑니다. 실행 환경.

(2) 변수 객체: 각 실행 환경에는 해당하는 변수 객체가 있습니다. 실행 환경에서 정의된 모든 변수와 함수는 이 변수 ​​객체에 저장됩니다. 이 변수 객체는 백그라운드 구현에서의 객체이므로 코드에서 접근할 수는 없지만, 실행 환경 및 범위와 관련된 개념을 이해하는 데 도움이 됩니다.

(3) 스코프 체인: 실행 환경에서 코드가 실행되면 변수 객체들로 구성된 스코프 체인이 생성됩니다. 이 체인의 앞부분은 현재 코드가 위치한 환경의 변수 개체이고, 체인의 끝은 전역 환경의 변수 개체입니다. 실행 환경에서 식별자를 구문 분석할 때 현재 실행 환경의 해당 변수 개체를 검색하고, 발견되지 않으면 범위 체인을 따라 수준별로 검색합니다. 전역 환경의 변수 객체가 발견될 때까지 참조 예외가 발생합니다.

(4) 활성화 개체: 실행 환경이 함수 실행 환경인 경우 변수 개체를 활성화 개체라고도 합니다. 활성 객체에는 처음에 인수 객체라는 하나의 변수만 포함됩니다(이 객체는 전역 환경의 변수 객체에 존재하지 않습니다).

이 네 가지 개념은 다소 추상적이지만 여전히 비교적 자연스럽습니다. "JavaScript 고급 프로그래밍(3판)"의 예를 통해 자세히 경험할 수 있습니다. 🎜>
코드 복사
코드는 다음과 같습니다. // 전역 범위를 입력하고 전역 변수 개체를 생성합니다var color = " blue";
functionchangeColor(){
//changeColor 범위를 입력하고 ChangeColor에 해당하는 변수 개체를 만듭니다.
var anotherColor = "red";

function swapColors( color1 , color2){
// swapColors 범위를 입력하고 swapColors의 해당 변수 객체를 만듭니다.
var tempColor = anotherColor
anotherColor = color;
/*
* swapColors 범위 내에서 액세스할 수 있는 개체는 다음과 같습니다.
* 전역 변수 개체의 color,changeColor
* anotherColor,changeColor 함수의 해당 변수 개체의 swapColors
* 해당 변수 개체의 tempColor swapColors 함수
*/
}
swapColors('white')
/*
*changeColor 범위 내에서 액세스할 수 있는 객체는 다음과 같습니다.
* 색상,changeColor 전역 변수 객체
* 해당하는changeColor 함수 anotherColor, 변수 객체의 swapColors
*/
}

changeColor()
/*
* 전역 범위에서 액세스할 수 있는 항목은 다음과 같습니다.
* 전역 변수 object color,changeColor
*/


전체 프로세스는 다음과 같습니다.

(1) 전역 환경에 들어가서 전역 변수 개체를 생성하고 전역 환경을 스택 맨 위로 푸시합니다(이 역시 스택의 맨 아래). 선언 승격에 대한 이전 결론에 따르면 여기서 전역 변수 객체를 생성하는 가능한 프로세스는 먼저 전역 변수 객체를 생성한 다음 함수 선언을 처리하고 해당 함수에changeColor 속성을 설정한 다음 변수 선언 및 속성 색상을 정의되지 않음으로 설정합니다.

(2) 글로벌 환경에서 코드를 실행합니다. 먼저 색상 변수 초기화를 수행하고 'blue'에 값을 할당한 다음,changeColor() 함수를 호출합니다.

(3)changeColor() 함수를 호출하고,changeColor 함수 실행 환경에 들어가서 이 환경에 해당하는 변수 객체(즉, 활성 객체)를 생성하고 이 환경을 스택의 맨 위에 푸시합니다. . 활성 객체를 생성할 수 있는 과정은 먼저 활성 객체를 생성하고 내부 함수 선언을 처리한 후 swapColors 속성을 해당 함수로 설정하고 함수 매개변수를 처리하여 활성 객체의 속성 인수 객체를 생성한 다음 내부 함수를 처리하는 것입니다. anotherColor 속성을 정의되지 않음으로 설정하는 변수 선언입니다.

(4)changeColor() 함수 코드를 실행합니다. 먼저 anotherColor를 실행하여 'red'로 초기화한 다음 swapColors() 함수를 호출합니다.

(5) swapColors() 함수를 호출하고, swapColors 함수 실행 환경에 들어간 다음, 해당 변수 개체(활성 개체)를 생성하고, swapColors 실행 환경을 스택 맨 위에 푸시합니다. 여기서 활성 객체를 생성할 수 있는 과정은 먼저 활성 객체를 생성하고, 함수 매개변수를 처리하고, 형식 매개변수를 활성 객체의 속성으로 사용하여 이를 정의되지 않은 상태로 할당하고, 활성 객체의 속성 인수 객체를 생성하고 초기화하는 것입니다. 실제 매개변수에 따른 형식 매개변수와 인수 값과 속성(color1 및 인수[0] 속성을 'white'로 초기화합니다. 두 번째 실제 매개변수가 없으므로 color2의 값은 정의되지 않으며 길이는 인수의 수는 1개뿐입니다. 함수 매개변수를 처리한 후 함수의 내부 변수 선언을 처리하고 tempColor를 활성 객체의 속성으로 사용하여 정의되지 않은 상태로 할당합니다.

(6) swapColors() 함수 코드를 실행합니다. 먼저 초기화하고 tempColor에 값을 할당한 다음 값 교환 기능을 구현합니다(여기서 color 및 anotherColor 값은 범위 체인을 따라 읽혀집니다).

(7) swapColors() 함수 코드가 실행된 후 정의되지 않음을 반환하고 해당 실행 환경을 스택에서 팝하고 파괴합니다(여기서 실행 환경은 파괴되지만 해당 활성 개체는 실행 환경이 반드시 파괴되지는 않습니다), 현재 실행 환경은changeColor() 함수의 실행 환경으로 복원됩니다. swapColor() 함수가 실행을 완료하고 반환함에 따라,changeColor()도 실행을 완료하고 정의되지 않은 값을 반환하며,changeColor()함수의 실행 환경은 스택에서 팝되어 소멸되고 현재 실행 환경은 전역으로 복원됩니다. 환경. 전체 처리 과정이 종료되며, 해당 페이지가 종료될 때까지 전역 환경은 소멸됩니다.

또한 범위 체인은 함수가 내부적으로 자신을 재귀적으로 호출할 수 있는 이유를 설명합니다. 함수 이름은 함수가 정의된 실행 환경과 해당 함수의 내부 실행 환경에서 해당 변수 개체의 속성입니다. 함수, 범위를 따라갈 수 있습니다. 체인은 함수 이름이 가리키는 함수 개체에 액세스하기 위해 한 수준 위로 올라갑니다. 함수 이름이 함수 내의 새 함수를 가리키는 경우 재귀 호출이 올바르지 않습니다.
코드 복사 코드는 다음과 같습니다. 다음 :

function fn(num){
if(1 == num){
return 1
}else{
fn = function(){
return 0;
};
return num * fn(num - 1)
}
}
console.info(fn(5));//0

범위 및 선언 호이스팅과 관련하여 또 다른 예를 살펴보겠습니다.
코드 복사 코드는 다음과 같습니다. :

var name = 'linjisong';
function fn(){
console.info(name);//undefine
var name = 'oulinhai'; >console.info( name);//oulinhai
}
fn();
console.info(name);//linjisong

여기서 가장 직관적이지 않은 것은 출력의 3번째 줄은 name이 전역적으로 정의되어 있기 때문에 undefine이 되지만, 위의 파싱 단계에 따라 한 번만 파싱하면 올바른 결과를 얻을 수 있습니다. 또한, ECMAScript에는 전역 실행 환경과 함수 실행 환경만 있고 그에 따라 전역 범위와 함수 범위만 있고, 블록 문은 있지만 블록 범위는 없다는 점을 강조하고 싶습니다.

코드 복사 코드는 다음과 같습니다.
function fn(){
var fnScope = ' a';

{
var blockScope = 'b';
blockScope = fnScope;
}
console.info(blockScope);//블록이 없습니다. 범위이므로 괜찮습니다. 전체 함수 범위
console.info(fnScope)
}
fn();//ba,a

console.info(blockScope); //ReferenceError, 함수 범위 밖에서 내부적으로 정의된 변수에 접근할 수 없습니다.
console.info(fnScope);//ReferenceError


범위 체인의 경우 with 및 try-catch 문의 catch 블록을 사용하여 확장할 수도 있습니다.

•with(obj){} 문을 사용할 때 obj 개체를 현재 범위 체인 프런트 엔드입니다.
•try{}catch(error){} 문을 사용할 때 현재 범위 체인 앞에 오류 객체를 추가합니다.
전체 읽기의 원활함에 영향을 미치지 않기를 바라며 좀 더 추상적인 개념을 삽입했습니다. 사실 여기서는 "클로저"라는 개념을 조용히 우회했습니다. 아래에서 자세히 설명합니다. .

7. 함수 내부 객체와 이

이는 객체지향 언어 사용자에게 매우 친숙한 생성자입니다. 물체! 그러나 ECMAScript에서는 이를 가볍게 여기지 마십시오. new 연산자를 사용하여 함수를 호출할 때 이는 새로 생성된 객체를 가리키지만 이는 this의 값을 지정하는 방법일 뿐입니다. 이 객체의 값을 지정하는 방법은 더 많습니다. 즉, 이는 동적이며 우리가 자유롭게 지정할 수 있습니다.

(1) 전역 환경에서 this

전역 환경에서 this는 브라우저의 창인 전역 객체 자체를 가리킵니다. 여기서 전역 환경에서도 this가 가능합니다. 전역 실행 환경의 해당 변수 개체로 이해됩니다. 전역 환경에 정의된 변수 및 함수는 이 변수 ​​개체의 속성입니다.
코드 복사 코드는 다음과 같습니다:

var vo = 'a'
vo2 = 'b'
function fn(){
return 'fn ';
}
console.info(this === window);//true
console.info(this.vo);//a
console.info(this.vo2); //b
console .info(this.fn());//fn

window를 직접 사용할 수도 있지만 사용자 정의 함수에서 전역 개체를 참조하려는 경우 a 더 좋은 방법은 매개변수가 함수에 전달될 때 전역 개체를 사용하는 것입니다. 이는 JS 라이브러리에서 매우 일반적인 방법입니다.
코드 복사 코드는 다음과 같습니다.

(function(global){
console.info(global === window);//내부적으로 window 대신 global을 사용할 수 있습니다
})(this);

이 방법은 호환성이 더 좋습니다(ECMAScript 구현의 전역 개체가 모두 창이 아닐 수도 있음). 압축 시 창을 사용하는 대신 전역을 g로 단순화할 수도 있습니다.

(2) 함수 내부 속성 this

함수 환경에서는 함수에 해당하는 활성 객체의 속성으로 이해될 수 있는 내부 속성 객체이며, 값 이 내부 속성은 동적입니다. 이 값은 어떻게 동적으로 결정됩니까?

•new를 사용하여 함수를 호출할 때 해당 함수를 생성자라고도 합니다. 이때 함수 ​​내부의 이것을 새로 생성된 객체로 지정합니다.
코드 복사 코드는 다음과 같습니다.

function fn(){
var name = 'oulinhai';//함수에 해당하는 활성 객체의 속성
this.name = 'linjisong';//new를 사용하여 함수 호출 시 새로 생성된 객체로 지정, 즉 속성 추가 새로 생성된 객체에
}
var person = new fn();
console.info(person.name);//linjisong

var arr = [fn]; >console.info(arr[ 0]());//undef

함수 실행 환경에서 정의한 속성(즉, 활성 객체의 속성)을 구분하는데 주의가 필요합니다. ) 및 이 객체의 속성. 배열 요소를 사용하여 함수를 호출할 때 함수 내부의 this가 배열 자체를 가리키므로 위의 예에서는 최종적으로 undefine이 출력됩니다.

•일반 함수로 호출할 경우 전역 객체를 가리킵니다.
•객체의 메소드로 호출될 경우 이 메소드를 호출하는 객체를 가리킵니다.
다음 예를 보세요.

코드 복사 코드는 다음과 같습니다.
var name = '오린하이';
var person = {
name:'linjisong',
getName:function(){
return this.name; 🎜>console.info(person.getName());//linjisong
var getName = person.getName
console.info(getName());//oulinhai


함수 개체 자체는 익명이며 사람 개체의 속성으로 사용됩니다. 개체 속성으로 호출하면 이 함수를 다른 함수에 할당한 후 호출할 때 이를 가리킵니다. 일반 함수. , 이는 전역 객체를 가리킵니다. 이 예는 "함수가 객체의 메서드로 호출될 때 내부 속성 this가 호출 객체를 가리키고, 함수가 일반 함수로 호출될 때 내부 속성 this가 전역 객체를 가리킨다"는 것을 충분히 보여줍니다. 또한 함수가 개별적으로 정의되거나 객체 메서드로 정의되는지 여부에 관계없이 this의 지정이 동적이며 호출 시 지정된다는 것을 보여줍니다. 객체의 메소드로서 함수가 호출될 때 this는 호출 객체를 가리키기 때문에 this가 함수 내에서 반환되어야만 호출 객체의 다음 메소드가 계속될 수 있기 때문입니다. jQuery의 주요 기능).

•apply(), call() 또는 바인딩()을 사용하여 함수를 호출할 때 첫 번째 매개변수 개체를 가리킵니다. 매개변수가 전달되지 않거나 null 또는 정의되지 않은 값이 전달되면 이는 전역 객체를 가리킵니다(ES5 엄격 모드에서는 null로 설정됩니다). 전달된 첫 번째 매개변수가 단순 유형인 경우 이는 해당 단순 유형 래퍼 객체로 설정됩니다.
코드 복사 코드는 다음과 같습니다.

var name = 'linjisong'; >function fn (){
return this.name;
}
var person = {
name:'oulinhai',
getName:fn
}
var person2 = {name:'hujinxing'};
var person3 = {name:'huanglanxue'};
console.info(fn());//linjisong, 일반 함수 호출, this가 가리키는 내부 속성 전역 객체이므로 this.name은 linjisong
console.info(person.getName()); //oulinhai, 객체 메서드로 호출되므로 this는 이 객체를 가리키므로 여기서는 person.name
console을 반환합니다. info(fn.apply(person2) );//hujinxing, 적용, 호출 또는 바인딩을 사용하여 함수를 호출하고 전달된 첫 번째 매개변수 객체를 실행하므로 person2.name
console.info(fn.call(person2) );//hujinxing
var newFn = fn.bind(person3);//ES5의 새로운 메소드는 새로운 함수 인스턴스를 생성하고 이를 반환합니다. 내부 this 값은 전달된 매개변수 객체
console로 지정됩니다. .info(newFn()); //huanglanxue

위에 나열된 예는 모두 일반적인 상황입니다. 첫 번째 매개변수가 null이거나 정의되지 않은 상황은 나열되지 않습니다. . 이 값의 결정과 관련하여 원서에는 또 다른 예가 있습니다.

코드 복사 코드는 다음과 같습니다.
var name = 'The Window';
var object = {
name : 'My Object',
getName:function(){
return this.name; 🎜>},
getNameFunc:function(){
return function(){
return this.name;
}
}

콘솔. info(object.getName ());//내 개체
console.info((object.getName)());//내 개체
console.info((object.getName = object.getName)( ));// 창
console.info(object.getNameFunc()());//창


第1个是正常输出,第2个(object.getName)与object.getName的效果是相同的,而第3个(object.getName=object.getName)最终返回的是函数对象本身,也就是说第3个会作为一般函数来调用,第4个则先是调用getNameFunc这个方法,返回一个函数,然后再调用这个函数,也是作为一般函数来调用。

8、函数属性和方法

  函数是一个对象,因此也可以有自己的属性和方法。不过函数属性和方法与函数内部属性很容易混淆,既然容易混淆,就把它们放一起对照着看,就好比一对双胞胎,不对照着看,不熟悉的人是区分不了的。

  先从概念上来区分一下:

(1)函数内部属性:可以理解为函数相应的活动对象的属性,是只能从函数体内部访问的属性,函数每一次被调用,都会被重新指定,具有动态性。

(2)函数属性和方法:这是函数作为对象所具有的特性,只要函数一定义,函数对象就被创建,相应的属性和方法就可以访问,并且除非你在代码中明确赋为另一个值,否则它们的值不会改变,因而具有静态性。有一个例外属性caller,表示调用当前函数的函数,也是在函数被调用时动态指定,在《JavaScript高级程序设计(第3版)》中也因此将caller属性和函数内部属性arguments、this一起讲解,事实上,在ES5的严格模式下,不能对具有动态特性的函数属性caller赋值。

  光从概念上区分是非常抽象的,也不是那么容易理解,再把这些属性列在一起比较一下(没有列入一些非标准的属性,如name):

类别 名称 继承性 说明 备注
函数内部属性 this - 函数据以执行的环境对象 和一般面向对象语言有很大区别
arguments -

表示函数实际参数的类数组对象

arguments本身也有自己的属性:length、callee和caller

1、length属性表示实际接收到的参数个数

2、callee属性指向函数对象本身,即有:

  fn.arguments.callee === fn

3、caller属性主要和函数的caller相区分,值永远都是undefined

函数属性 caller 调用当前函数的函数 虽然函数一定义就可访问,但是不在函数体内访问时永远为null,在函数体内访问时返回调用当前函数的函数,在全局作用域中调用函数也会返回null
length 函数形式参数的长度 就是定义函数时命名的参数个数
prototype 函数原型对象 原型对象是ECMAScript实现继承的基础
constructor 继承自Object,表示创建函数实例的函数,也就是Function() 值永远是Function,也就是内置的函数Function()
函数方法 apply 调用函数自身,以(类)数组方式接受参数

这三个方法主要作用是动态绑定函数内部属性this

1、apply和call在绑定之后会马上执行

2、bind在绑定之后可以在需要的时候再调用执行

call 调用函数自身,以列举方式接受参数
bind 绑定函数作用域,ES5中新增
toLocalString 覆盖

覆盖了Object类型中的方法,返回函数体

不同浏览器实现返回可能不同,可能返回原始代码,也可能返回去掉注释后的代码

toString 覆盖
valueOf 覆盖
hasOwnProperty 直接继承自Object类型的方法,用法同Object
propertyIsEnumerable
isPropertyOf

Object에서 상속된 것 외에도 함수 속성 및 메서드에는 함수 자체에 고유한 속성 및 메서드도 포함됩니다. 가장 일반적으로 사용되는 메서드는 당연히 이전 섹션에서 언급한 apply() 및 call()입니다. 메소드는 함수 범위를 확장하기 위해 함수의 내부 속성을 설정하는 데 사용됩니다. 그러나 apply()는 함수 범위를 확장할 때 (클래스) 배열 형식으로 함수의 매개 변수를 허용합니다. 함수 범위를 확장하려면 함수 매개변수를 하나씩 나열하고 전달해야 합니다. 다음 예를 참조하세요.

코드 복사 코드는 다음과 같습니다.

function sum(){
 var total = 0,
 l =args.length;

 for(; l; l- -){
 total = 인수[l -1];
 }
return total
}

console.info(sum.apply(null,[1,2, 3,4]));//10
console.info(sum.call(null,1,2,3,4));//10

그러나 적용 및 호출의 주요 역할은 함수 범위를 확장하는 것임을 강조하세요. Apply 및 call은 범위를 확장할 때 즉시 함수를 호출하므로 애플리케이션에 큰 제한이 있습니다. 따라서 ES5에서는 이 함수도 범위를 확장하는 데 사용되지만 반드시 그럴 필요는 없습니다. 함수 인스턴스를 반환하고 전달된 첫 번째 매개변수를 원래 함수의 범위로 사용하는 함수입니다. 가능한 구현은 다음과 같습니다.
코드 복사 코드는 다음과 같습니다.

function bin(scope ){
var that = this;
return function(){
that.apply(scope, 인수)
}
}
Function.prototype.bind = 바인딩;

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