자바스크립트에서 이 포인터에 대한 심층적인 이해

小云云
풀어 주다: 2018-03-17 16:21:10
원래의
1580명이 탐색했습니다.

이 글에서는 주로 JavaScript의 this 포인터에 대한 심층적인 이해를 공유합니다. Java를 작성할 때 이 포인터를 잘못 사용하면 Idea에서 직접 오류를 보고합니다.

예를 들어...



객체 지향 프로그래밍에는 두 가지 중요한 개념이 있습니다. 하나는 클래스이고 다른 하나는 인스턴스화된 객체입니다. 사용(Use) 은유적으로 말하면 클래스는 금형과 같으며, 인스턴스화된 객체는 이 금형을 통해 제작된 제품입니다. 그러나 인스턴스화된 객체는 우리에게 필요한 실제 객체입니다. 클래스의 기능은 인스턴스화된 객체를 사용할 때 이를 대체할 수 없습니다. 금형과 금형으로 만든 제품의 관계처럼 둘의 용도가 다릅니다.

위 코드에서 this 포인터는 Java 언어의 인스턴스화된 객체에서만 사용할 수 있음을 알 수 있습니다. this 포인터는 인스턴스화된 객체와 동일하며 이 뒤에 도트 연산자가 추가됩니다. 사물은 이름, 직업, 손, 발 등 이 소유물입니다.

사실JavaScript에서 이 포인터의 논리적 개념은 인스턴스화된 객체이기도 합니다. 이는 Java 언어의 this 포인터와 일치합니다. 그러나 JavaScript의 this 포인터는 Java에서 이 포인터를 이해하기가 훨씬 더 어렵습니다. 결국 저는 개인적으로 세 가지 근본적인 이유가 있다고 생각합니다:

이유 1: Javascript는 함수형 프로그래밍 언어입니다. 이상한 점은 이 포인터도 있다는 것입니다. 이는 이 함수형 프로그래밍 언어가 객체 지향 언어이기도 함을 보여줍니다. 좀 더 구체적으로 말하자면, JavaScript에서는 고차 함수입니다. 프로그래밍 언어의 고차 함수는 동시에 객체로 전달될 수도 있습니다. 인스턴스화된 객체를 생성하면 메서드가 실행될 때 this 포인터가 생성되며 방향이 지속적으로 변경되어 제어하기가 어렵습니다.

이유 2: 자바스크립트의 전역 범위는 this 포인터에 큰 영향을 미칩니다. 위의 자바 예에서 this 포인터는 new 연산자를 사용한 후에만 적용된다는 것을 알 수 있지만, 자바스크립트에서는 이것이 새로운 작업이 수행되지 않더라도 적용됩니다. 이때 이는 전역 개체 창을 가리키는 경우가 많습니다.

이유 3: JavaScript의 호출 및 적용 연산자는 this 포인터를 마음대로 변경할 수 있습니다. 이는 매우 유연해 보이지만 이러한 불합리한 접근 방식은 이 포인터에 대한 원래의 의미를 파괴하고 작성 시 이를 이해하기 어렵게 만듭니다.



위의 세 가지 이유는 모두 이 포인터를 사용하는 전통적인 방법을 위반하는 것이며 실제 개발에서는 세 가지 이유가 다릅니다. . 서로 얽히는 경향이 있어서 이게 다 구름 속에 있어요...


입문서: 웹 개발자를 위한 professional Javascript, - 고급 버전은 이렇습니다:

이것은 항상 포인트입니다. 이 메소드가 호출되는 객체입니다!



var name="zhoulujun";

function say(){

console.log(this.name)

}

say(); //zhoulujun



이 포인터(창 개체를 가리키는 결과)는 창 개체입니다. 삼중 등호가 사용되면 동일합니다. 전역 범위는 종종 JavaScript 언어의 특성에 대한 우리의 이해를 방해합니다.

JavaScript 언어에서

전역 범위는 창 개체로 이해될 수 있습니다. 즉, window는 인스턴스화된 개체입니다. 이 인스턴스화 프로세스는 페이지가 로드될 때 프로그래머가 제어할 수 없기 때문에 전체 페이지의 요소가 이 창 개체로 압축됩니다. 그리고 이 인스턴스화 프로세스를 작동하므로 개발 중에 이 포인터를 구성한다는 느낌이 없으며 종종 이를 무시합니다. 이는 이 포인터가 코드에서 창을 가리키는 상황을 이해하는 데 방해가 됩니다.

여기서는 창 개체를 가리키므로 this.name->zhoulujun!



say 함수가 실행되면 JavaScript는 실행 컨텍스트(실행 컨텍스트)를 생성하고 실행 컨텍스트에는 say 함수가 실행되는 동안 필요한 모든 정보가 포함됩니다. 실행 컨텍스트에는 자체 범위 체인도 있습니다. 함수가 실행되면 JavaScript 엔진은 먼저 say 함수를 사용하여 범위 체인에서 실행 컨텍스트의 범위 체인을 초기화합니다.

여기에서 대략적으로 기억할 수 있는 내용은 다음과 같습니다.

var myObj={
name:"zhoulujun",
fn:function(){
console.log(this.name)
}
};
myObj.fn();
로그인 후 복사



这里的this指向obj,因为fn()运行在obj里面……

然后再来看……

var name="zhoulujun";
function say(){
console.log(this.name)
console.log(this)
}
say();
function say2(){
var site="zhoulujun.cn";
console.log(this.site);
}
say2();
로그인 후 복사


myObj2={
site:"zhoulujun.cn",
fn:function(){
console.log(this.site)
}
}
로그인 후 복사



这里的this指向的是对象myObj2,因为你调用这个fn是通过myObj2.fn()执行的,那自然指向就是对象myObj2,这里再次强调一点,this的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁,一定要搞清楚这个

然后,我们更深入(受不了 …………

myObj3={
site:"zhoulujun.cn",
andy:{
site:"www.zhoulujun.cn",
fn:function(){
console.log(this.site)
}
}
};
myObj3.andy.fn();
로그인 후 복사



这里同样也是对象Object点出来的,但是同样this并没有执行它,那你肯定会说我一开始说的那些不就都是错误的吗?其实也不是,只是一开始说的不准确,接下来我将补充一句话,我相信你就可以彻底的理解this的指向的问题。

如果,你实在理解不了,就这么样背下来吧!

情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。

情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。

情况3:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象,如果不相信,那么接下来我们继续看几个例子。

这样既对了吗??深入点(就受不了了……讨厌……

myObj3={
site:"zhoulujun.cn",
andy:{
site:"www.zhoulujun.cn",
fn:function(){
console.log(this)
console.log(this.site)
}
}
};
//    myObj3.andy.fn();
var fn=myObj3.andy.fn;
fn();
로그인 후 복사



其实,这里的 fn等价于

fn:function(age){

console.log(this.name+age);

}

下面我们来聊聊函数的定义方式:声明函数和函数表达式

我们最上面第一个案例定义函数的方式在javascript语言称作声明函数,第二种定义函数的方式叫做函数表达式,这两种方式我们通常认为是等价的,但是它们其实是有区别的,而这个区别常常会让我们混淆this指针的使用,我们再看看下面的代码:



Say3은 실행될 수 있는데 왜 Say3는 실행될 수 없나요? 그 이유는:



say3의 인쇄 결과가 정의되지 않은 이유는 이전 기사에서 언급한 바와 같습니다. undefine은 메모리의 스택 영역에 이미 변수 이름이 있다는 것을 의미하지만, 스택 영역에는 변수 값이 없습니다. 동시에 힙 영역에는 특정 개체가 없습니다. 이는 미리 로드하는 동안 JavaScript 엔진이 변수 정의를 검색했기 때문입니다. 그러나 ftn01의 인쇄 결과는 매우 놀랍습니다. 완성된 함수 정의가 인쇄되고 코드가 순서대로 실행되지 않는데 이는 한 가지 문제만 설명할 수 있습니다.

자바스크립트 언어에서는 함수 선언으로 함수를 정의합니다. 자바스크립트 엔진은 전처리 중에 함수 정의 및 할당 작업을 완료합니다. 여기서는 자바스크립트에서 전처리의 특징을 추가하겠습니다. 실제로 전처리는 실행 환경과 관련이 있습니다. 이전 글에서 실행 환경에는 크게 전역 실행 환경과 로컬 실행 환경이 있다고 언급했습니다. 실행 환경은 컨텍스트 변수를 통해 반영됩니다. 실제로 이 프로세스는 함수가 실행되기 전에 완료됩니다. 전처리는 실행 환경 구축의 또 다른 용어입니다. 간단히 말해서, 전처리 및 실행 환경 구축의 주요 목적은 변수 정의를 명확하게 하는 것입니다. 다만, 전역 범위 구축이나 전역 변수 전처리 과정에서 함수를 선언하는 경우에는 변수 정의와 할당 작업이 동시에 완료되므로 위 코드의 결과를 보면 알 수 있습니다. . 선언된 함수는 전역 범위 구성 중에 완료되므로 선언된 함수는 모두 창 개체의 속성입니다. 이는 함수를 어디에 선언하든 결국 선언된 함수가 창 개체에 속하는 이유를 설명합니다.

여기를 살펴보는 것이 좋습니다 - Java 클래스의 실행 순서:

http://www.zhoulujun.cn/zhoulujun/html/java/javaBase/7704.html



其实在javascript语言里任何匿名函数都是属于window对象,它们也都是在全局作用域构造时候完成定义和赋值,但是匿名函数是没有名字的函数变量,但是在定义匿名函数时候它会返回自己的内存地址,如果此时有个变量接收了这个内存地址,那么匿名函数就能在程序里被使用了,因为匿名函数也是在全局执行环境构造时候定义和赋值,所以匿名函数的this指向也是window对象,所以上面代码执行时候fn的this都是指向window,因为javascript变量名称不管在那个作用域有效,堆区的存储的函数都是在全局执行环境时候就被固定下来了,变量的名字只是一个指代而已。

类似的情况(面试题喜欢这么考!)……比如:



this都是指向实例化对象,前面讲到那么多情况this都指向window,就是因为这些时候只做了一次实例化操作,而这个实例化都是在实例化window对象,所以this都是指向window。我们要把this从window变成别的对象,就得要让function被实例化,那如何让javascript的function实例化呢?答案就是使用new操作符。

再来看 构造函数:

function  User(){
this.name="zhoulujun";
console.log(this);
}
var andy=new User();
console.log(andy.name)
로그인 후 복사



why andy 的name 是 zhoulujun,那是:因为:

new关键字可以改变this的指向,将这个this指向对象andy,

那andy什么时候又成了思密达,oh,no,is Object?

new 키워드를 사용하는 것은 객체 인스턴스를 생성하는 것이기 때문입니다(중요한 내용은 조용히 세 번 읽어보세요)

우리는 변수 andy를 사용하여 User 사용자 인스턴스를 생성합니다(User의 복사본을 객체 andy에 복사하는 것과 같습니다). 방금 생성되었지만 실행되지 않았습니다. User 함수를 호출하는 개체는 andy 개체이므로 이는 당연히 개체 User에 이름이 있는 것입니다. andy. 에 new 키워드를 사용하는 것은 복사본을 만드는 것과 같습니다.

java 프로그래머: 클래스 user=new User(); 친숙해 보입니다...



function은 함수이자 객체입니다. 생성자로 사용되기 때문에 javascript의 생성자는 클래스와 생성자를 하나로 결합하는 것이라고 생각하는 경우가 많습니다. 물론 javascript 언어 사양에는 클래스 개념이 없지만 제가 이해한 바는 다음과 같습니다. 생성자와 일반 함수로 사용하면 함수의 차이를 이렇게 이해하는 것이 더 쉬울 것입니다. 아래에는 "Javascript 고급 프로그래밍"에 new 연산자에 대한 설명이 게시되어 있습니다.

new 연산자는 생성자를 다음과 같이 변경합니다.

1. 새 개체를 만듭니다. 생성자의 범위가 새 객체에 할당됩니다(따라서 이는 새 객체를 가리킵니다). 생성자에서 코드를 실행합니다(이 새 객체에 속성을 추가합니다).

4. 返回新对象

……

妈的:读的那么拗口,不明觉厉…………看图……还不

不明白……

var myObj5={
name:"andy"
};
var myObj6=new Object();
myObj6.name="andy";
function  say5(name){
console.log(name)
}
var say6=new Function("name","console.log(name)");
console.log(myObj5)
console.log(myObj6)
say5("andy");
say6("andy");
로그인 후 복사



还不明白,就请奶奶买块豆腐,撞死算了……

第四点也要着重讲下,记住构造函数被new操作,要让new正常作用最好不能在构造函数里写return,没有return的构造函数都是按上面四点执行,有了return情况就复杂了

return这王八蛋……



那么我这样呢……



does it have to be like this?Tell me why(why),is there something I have missed?

Tell me why(why),cos I don't understand…………



那是因为……because of u?no return……



所以:如果返回的是基本类型,就会丢掉…只能返回Object类型……typeof xx ===“object”

看到called 没有?什么鬼!!

其实new关键字会创建一个空的对象,然后会自动调用一个函数apply方法,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代。

var a={
name:"andy",
site:"zhoulujun.cn",
fn:function(age){
console.log(this.name+age);
}
};
var b={
name:"zhoulujun",
site:"www.zhoulujun.cn",
fn:function(age){
console.log(this.name+age);
}
};
a.fn(2); //andy2
a.fn.call(b,2) //zhoulujun2
a.fn.apply(b,[2])//zhoulujun2
当然,还有bind……
var arr = [1, 2];
var add = Array.prototype.push.bind(arr, 3);
// effectively the same as arr.push(3)
add();
// effectively the same as arr.push(3, 4)
add(4);
console.log(arr);
// <- [1, 2, 3, 3, 4]
在下面的例子,this将无法在作用域链中保持不变。这是规则的缺陷,并且常常会给业余开发者带来困惑。
function scoping () {
console.log(this);
return function () {
console.log(this);
};
}
scoping()();
// <- Window
// <- Window
有一个常见的方法,创建一个局部变量保持对this的引用,并且在子作用域中不能有同命变量。子作用域中的同名变量将覆盖父作用域中对this的引用。
function retaining () {
var self = this;
return function () {
console.log(self);
};
}
retaining()();
// <- Window
除非你真的想同时使用父作用域的this,以及当前this值,由于某些莫名其妙的原因,我更喜欢是使用的方法.bind函数。这可以用来将父作用域的this指定给子作用域。
function bound () {
return function () {
console.log(this);
}.bind(this);
}
bound()();
// <- Window
로그인 후 복사

写到这里,都看不下去,逻辑有点混乱,有的是从前辈哪里引用的。

改天有时间整理下,然后,在去讲下闭包(……closer





위 내용은 자바스크립트에서 이 포인터에 대한 심층적인 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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