~
JavaScript를 학습하는 과정에서 일부 개념을 명확하게 이해하지 못하는 경우가 있지만 어떤 식으로든 적어 보면 자신의 기억을 촉진하기 위해 이러한 개념에 대해 편향된 결론을 성급하게 도출하기 쉽습니다.
더 해로운 것은 일부 부정확한 결론이 인터넷에 널리 유포된다는 점입니다.
예를 들어 이것이 무엇을 가리키는지 이해하는 데에는 누가 부르든 이것이 가리키는 말이 있습니다. 처음 이 말씀을 배우기 시작했을 때 저는 이 말씀을 무척 믿었습니다. 어떤 경우에는 이러한 이해가 의미가 있기 때문입니다. 하지만 개발 과정에서 몇 가지 다른 상황에 자주 직면하게 됩니다. 이로 인해 잘못된 호출이 하루 종일 혼란스러울 수 있습니다. 그때도 정보를 찾아보고 그룹 내 전문가에게 물어봤지만 여전히 '내가 어디서 잘못됐나?'를 알 수 없었다. 사실 그것은 단지 내 마음 속에 잘못된 결론이 있기 때문일 뿐입니다.
바이두검색에 대한 불만사항입니다. 검색에 나온 기사 중 상당수가 잘못된 내용으로 인해 오랫동안 노사에 상처를 주었습니다
그래서 모든 분들이 이를 전반적으로 이해할 수 있도록 돕는 그런 글이 필요하다고 생각합니다. 모든 사람이 이에 대해 정확하고 포괄적으로 이해하도록 하십시오.
그 전에 실행 컨텍스트를 검토해야 합니다.
이전 글에서 실행 컨텍스트의 생명주기에 대해 여러 군데 언급했는데 기억이 나지 않으실까봐 아래와 같이 다시 복습해 보겠습니다.
실행 컨텍스트 수명 주기
실행 컨텍스트 생성 단계에서 변수Object 를 생성하고, 범위 체인을 설정하고 이에 대한 요점을 결정합니다. 변수 객체와 스코프 체인을 꼼꼼히 정리했는데, 여기서 핵심은 이것의 요점을 파악하는 것이다.
여기서 명심해야 할 매우 중요한 결론을 도출해야 합니다.이것은 함수 가 호출될 때 결정됩니다. 즉, 실행 컨텍스트가 생성될 때 결정됩니다. 따라서 함수의 this 포인터가 매우 유연할 수 있다는 것을 쉽게 이해할 수 있습니다. 예를 들어, 다음 예제에서는 호출 방법이 다르기 때문에 동일한 함수가 다른 객체를 가리킵니다.
var a = 10; var obj = { a: 20 } function fn () { console.log(this.a); } fn(); // 10 fn.call(obj); // 20그리고
함수 실행 중에는 한번 결정되면 변경할 수 없습니다.
var a = 10; var obj = { a: 20 } function fn () { this = obj; // 这句话试图修改this,运行后会报错 console.log(this.a); } fn();
1. 전역 객체에 있는 이
전역 객체에 있어서는 앞서 변수 객체를 요약할 때 언급한 A입니다. 오히려 특별한 존재. 이것은 글로벌 환경에서 그 자체를 가리킨다. 따라서 상대적으로 간단하고 고려해야 할 합병증이 많지 않습니다.// 通过this绑定到全局对象 this.a2 = 20; // 通过声明绑定到变量对象,但在全局环境中,变量对象就是它自身 var a1 = 10; // 仅仅只有赋值操作,标识符会隐式绑定到全局对象 a3 = 30; // 输出结果会全部符合预期 console.log(a1); console.log(a2); console.log(a3);2. 함수의 This 함수의 this의 요점을 요약하기 전에 함수에서 this의 애매함을 느끼려면 몇 가지 이상한 예를 살펴봐야 할 것 같습니다.
// demo01 var a = 20; function fn() { console.log(this.a); } fn();
// demo02 var a = 20; function fn() { function foo() { console.log(this.a); } foo(); } fn();
// demo03 var a = 20; var obj = { a: 10, c: this.a + 20, fn: function () { return this.a; } } console.log(obj.c); console.log(obj.fn());독자들은 시간을 내어 이러한 예를 경험해 보아야 합니다. 무슨 일이 일어나고 있는지 이해하지 못하더라도 걱정하지 마십시오. 분석에 앞서 결론을 직접 도출해보자. 함수 컨텍스트에서 이는 호출자가 제공하며 함수가 호출되는 방식에 따라 결정됩니다.
호출자 함수가 개체의 소유인 경우 함수가 호출되면 내부 this가 개체를 가리킵니다. 함수가 독립적으로 호출되면 함수 내부의 this는 정의되지 않은을 가리킵니다. 그러나 엄격하지 않은 모드에서는 이것이 정의되지 않음을 가리키면 자동으로 전역 개체를 가리킵니다.
결론적으로 보면 이 점을 정확하게 판단하기 위해서는 함수의 호출자를 찾아내고, 그가 독립적으로 호출하고 있는지 구별하는 것이 매우 중요하다는 것을 알 수 있다.// 为了能够准确判断,我们在函数内部使用严格模式,因为非严格模式会自动指向全局 function fn() { 'use strict'; console.log(this); } fn(); // fn是调用者,独立调用 window.fn(); // fn是调用者,被window所拥有위의 간단한 예에서
는 정의에 따르면 내부 this 포인터가 정의되지 않은 독립적인 호출자입니다. 그리고 fn()
, fn은 window의 소유이므로 내부 this는 window.fn()
window 객체 를 가리킵니다.
但是我们需要特别注意的是demo03。在demo03中,对象obj中的c属性使用this.a + 20
来计算,而他的调用者obj.c
并非是一个函数。因此他不适用于上面的规则,我们要对这种方式单独下一个结论。
当obj在全局声明时,无论obj.c
在什么地方调用,这里的this都指向全局对象,而当obj在函数环境中声明时,这个this指向undefined,在非严格模式下,会自动转向全局对象。可运行下面的例子查看区别。
'use strict'; var a = 20; function foo () { var a = 1; var obj = { a: 10, c: this.a + 20, fn: function () { return this.a; } } return obj.c; } console.log(foo()); // 运行会报错
实际开发中,并不推荐这样使用this;
上面多次提到的严格模式,需要大家认真对待,因为在实际开发中,现在基本已经全部采用严格模式了,而最新的ES6,也是默认支持严格模式。
再来看一些容易理解错误的例子,加深一下对调用者与是否独立运行的理解。
var a = 20; var foo = { a: 10, getA: function () { return this.a; } } console.log(foo.getA()); // 10 var test = foo.getA; console.log(test()); // 20
foo.getA()
中,getA是调用者,他不是独立调用,被对象foo所拥有,因此它的this指向了foo。而test()
作为调用者,尽管他与foo.getA的引用相同,但是它是独立调用的,因此this指向undefined,在非严格模式,自动转向全局window。
稍微修改一下代码,大家自行理解。
var a = 20; function getA() { return this.a; } var foo = { a: 10, getA: getA } console.log(foo.getA()); // 10
灵机一动,再来一个。如下例子。
function foo() { console.log(this.a) } function active(fn) { fn(); // 真实调用者,为独立调用 } var a = 20; var obj = { a: 10, getA: foo } active(obj.getA);
三、使用call,apply显示指定this
JavaScript内部提供了一种机制,让我们可以自行手动设置this的指向。它们就是call与apply。所有的函数都具有着两个方法。它们除了参数略有不同,其功能完全一样。它们的第一个参数都为this将要指向的对象。
如下例子所示。fn并非属于对象obj的方法,但是通过call,我们将fn内部的this绑定为obj,因此就可以使用this.a访问obj的a属性了。这就是call/apply的用法。
function fn() { console.log(this.a); } var obj = { a: 20 } fn.call(obj);
而call与applay后面的参数,都是向将要执行的函数传递参数。其中call以一个一个的形式传递,apply以数组的形式传递。这是他们唯一的不同。
function fn(num1, num2) { console.log(this.a + num1 + num2); } var obj = { a: 20 } fn.call(obj, 100, 10); // 130 fn.apply(obj, [20, 10]); // 50
因为call/apply的存在,这让JavaScript变得十分灵活。因此就让call/apply拥有了很多有用处的场景。简单总结几点,也欢迎大家补充。
将类数组对象转换为数组
function exam(a, b, c, d, e) { // 先看看函数的自带属性 arguments 什么是样子的 console.log(arguments); // 使用call/apply将arguments转换为数组, 返回结果为数组,arguments自身不会改变 var arg = [].slice.call(arguments); console.log(arg); } exam(2, 8, 9, 10, 3); // result: // { '0': 2, '1': 8, '2': 9, '3': 10, '4': 3 } // [ 2, 8, 9, 10, 3 ] // // 也常常使用该方法将DOM中的nodelist转换为数组 // [].slice.call( document.getElementsByTagName('li') );
根据自己的需要灵活修改this指向
var foo = { name: 'joker', showName: function() { console.log(this.name); } } var bar = { name: 'rose' } foo.showName.call(bar);
实现继承
// 定义父级的构造函数 var Person = function(name, age) { this.name = name; this.age = age; this.gender = ['man', 'woman']; } // 定义子类的构造函数 var Student = function(name, age, high) { // use call Person.call(this, name, age); this.high = high; } Student.prototype.message = function() { console.log('name:'+this.name+', age:'+this.age+', high:'+this.high+', gender:'+this.gender[0]+';'); } new Student('xiaom', 12, '150cm').message(); // result // ---------- // name:xiaom, age:12, high:150cm, gender:man;
简单给有面向对象基础的朋友解释一下。在Student的构造函数中,借助call方法,将父级的构造函数执行了一次,相当于将Person中的代码,在Sudent中复制了一份,其中的this指向为从Student中new出来的实例对象。call方法保证了this的指向正确,因此就相当于实现了基层。Student的构造函数等同于下。
var Student = function(name, age, high) { this.name = name; this.age = age; this.gender = ['man', 'woman']; // Person.call(this, name, age); 这一句话,相当于上面三句话,因此实现了继承 this.high = high; }
在向其他执行上下文的传递中,确保this的指向保持不变
如下面的例子中,我们期待的是getA被obj调用时,this指向obj,但是由于匿名函数的存在导致了this指向的丢失,在这个匿名函数中this指向了全局,因此我们需要想一些办法找回正确的this指向。
var obj = { a: 20, getA: function() { setTimeout(function() { console.log(this.a) }, 1000) } } obj.getA();
常规的解决办法很简单,就是使用一个变量,将this的引用保存起来。我们常常会用到这方法,但是我们也要借助上面讲到过的知识,来判断this是否在传递中被修改了,如果没有被修改,就没有必要这样使用了。
var obj = { a: 20, getA: function() { var self = this; setTimeout(function() { console.log(self.a) }, 1000) } }
另外就是借助闭包与apply方法,封装一个bind方法。
function bind(fn, obj) { return function() { return fn.apply(obj, arguments); } } var obj = { a: 20, getA: function() { setTimeout(bind(function() { console.log(this.a) }, this), 1000) } } obj.getA();
当然,也可以使用ES5中已经自带的bind方法。它与我上面封装的bind方法是一样的效果。
var obj = { a: 20, getA: function() { setTimeout(function() { console.log(this.a) }.bind(this), 1000) } }
四、构造函数与原型方法上的this
在封装对象的时候,我们几乎都会用到this,但是,只有少数人搞明白了在这个过程中的this指向,就算我们理解了原型,也不一定理解了this。所以这一部分,我认为将会为这篇文章最重要最核心的部分。理解了这里,将会对你学习JS面向对象产生巨大的帮助。
结合下面的例子,我在例子抛出几个问题大家思考一下。
function Person(name, age) { // 这里的this指向了谁? this.name = name; this.age = age; } Person.prototype.getName = function() { // 这里的this又指向了谁? return this.name; } // 上面的2个this,是同一个吗,他们是否指向了原型对象? var p1 = new Person('Nick', 20); p1.getName();
我们已经知道,this,是在函数调用过程中确定,因此,搞明白new的过程中到底发生了什么就变得十分重要。
new연산자를 통해 생성자를 호출하는 과정은 다음 4단계를 거치게 됩니다.
새 개체를 만듭니다.
생성자의 this를 이 새 개체에 지정합니다. 🎜>생성자의 코드를 가리키고 이 개체에 속성, 메서드 등을 추가합니다.
새 개체를 반환합니다.
따라서 new 연산자가 생성자를 호출하면 이는 실제로 새로 생성된 객체를 가리키고, 마지막으로 새 객체가 인스턴스 객체 p1에 의해 반환되고 수신됩니다. 그러므로 이때 생성자의 this는 새로운 인스턴스 객체인 p1을 가리킨다고 할 수 있다.
프로토타입 메소드의 this는 위 함수의 this 정의에 따라
위 내용은 프론트엔드 고급 5: 이에 대한 포괄적인 해석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

JavaScript의 최신 트렌드에는 Typescript의 Rise, 현대 프레임 워크 및 라이브러리의 인기 및 WebAssembly의 적용이 포함됩니다. 향후 전망은보다 강력한 유형 시스템, 서버 측 JavaScript 개발, 인공 지능 및 기계 학습의 확장, IoT 및 Edge 컴퓨팅의 잠재력을 포함합니다.

JavaScript는 현대 웹 개발의 초석이며 주요 기능에는 이벤트 중심 프로그래밍, 동적 컨텐츠 생성 및 비동기 프로그래밍이 포함됩니다. 1) 이벤트 중심 프로그래밍을 사용하면 사용자 작업에 따라 웹 페이지가 동적으로 변경 될 수 있습니다. 2) 동적 컨텐츠 생성을 사용하면 조건에 따라 페이지 컨텐츠를 조정할 수 있습니다. 3) 비동기 프로그래밍은 사용자 인터페이스가 차단되지 않도록합니다. JavaScript는 웹 상호 작용, 단일 페이지 응용 프로그램 및 서버 측 개발에 널리 사용되며 사용자 경험 및 크로스 플랫폼 개발의 유연성을 크게 향상시킵니다.

Python은 데이터 과학 및 기계 학습에 더 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 더 적합합니다. 1. Python은 간결한 구문 및 풍부한 라이브러리 생태계로 유명하며 데이터 분석 및 웹 개발에 적합합니다. 2. JavaScript는 프론트 엔드 개발의 핵심입니다. Node.js는 서버 측 프로그래밍을 지원하며 풀 스택 개발에 적합합니다.

JavaScript는 이미 최신 브라우저에 내장되어 있기 때문에 설치가 필요하지 않습니다. 시작하려면 텍스트 편집기와 브라우저 만 있으면됩니다. 1) 브라우저 환경에서 태그를 통해 HTML 파일을 포함하여 실행하십시오. 2) Node.js 환경에서 Node.js를 다운로드하고 설치 한 후 명령 줄을 통해 JavaScript 파일을 실행하십시오.

쿼츠 타이머를 사용하여 작업을 예약 할 때 미리 쿼츠에서 작업 알림을 보내는 방법 작업의 실행 시간은 CRON 표현식에 의해 설정됩니다. 지금...

JavaScript 프로그래밍에서 JavaScript의 프로토 타입 체인에서 함수 매개 변수를 얻는 방법 프로토 타입 체인의 기능 매개 변수를 이해하고 조작하는 방법은 일반적이고 중요한 작업입니다 ...

WeChat 애플릿 웹 뷰에서 vue.js를 사용하는 동적 스타일 변위 실패가 vue.js를 사용하는 이유를 분석합니다.

동시 링크에 대한 요청을 여러 링크와 순서대로 판단하여 결과를 반환하는 방법은 무엇입니까? 탬퍼 몬키 스크립트에서는 종종 여러 체인을 사용해야합니다 ...


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

Atom Editor Mac 버전 다운로드
가장 인기 있는 오픈 소스 편집기

Eclipse용 SAP NetWeaver 서버 어댑터
Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

SecList
SecLists는 최고의 보안 테스터의 동반자입니다. 보안 평가 시 자주 사용되는 다양한 유형의 목록을 한 곳에 모아 놓은 것입니다. SecLists는 보안 테스터에게 필요할 수 있는 모든 목록을 편리하게 제공하여 보안 테스트를 더욱 효율적이고 생산적으로 만드는 데 도움이 됩니다. 목록 유형에는 사용자 이름, 비밀번호, URL, 퍼징 페이로드, 민감한 데이터 패턴, 웹 셸 등이 포함됩니다. 테스터는 이 저장소를 새로운 테스트 시스템으로 간단히 가져올 수 있으며 필요한 모든 유형의 목록에 액세스할 수 있습니다.

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

뜨거운 주제



