이 기사는 js의 비공개 멤버에 대한 포괄적인 분석을 제공합니다. 이는 특정 참고 가치가 있습니다. 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.
JavaScript에 대한 클래스 필드 선언(JavaScript 클래스에 대한 필드 선언)이 이제 3단계에 진입했습니다. 여기에는 OOP 개발자가 매우 우려하는 항목인 비공개 필드가 포함됩니다. JavaScript에 비공개 멤버가 없었던 것은 당연하므로 이 제안은 JavaScript에 새로운 도전을 가져옵니다. 그러나 동시에 JavaScript는 ES2015가 출시되었을 때 이미 민영화 문제를 고려하고 있었기 때문에 private 멤버를 구현하는 데 기반이 없는 것은 아닙니다.
먼저 구덩이를 파세요. 이는 JS 코드의 일부입니다. BusinessView
에서 수행해야 할 작업은 두 가지입니다. 양식과 지도에 대한 작업을 수행합니다. BusinessView
中要干两件事情,即对表单和地图进行布局。
class BaseView { layout() { console.log("BaseView Layout"); } } class BusinessView extends BaseView { layout() { super.layout(); this._layoutForm(); this._layoutMap(); } _layoutForm() { // .... } _layoutMap() { // .... } }
然后,由于业务的发展,发现有很多视图都存在地图布局。这里选用继承的方式来实现,所以从 BusinessView
中把地图相关的内容抽象成一个基类叫 MapView
:
class MapView extends BaseView { layout() { super.layout(); this._layoutMap(); } _layoutMap() { console.log("MapView layout map"); } } class BusinessView extends MapView { layout() { super.layout(); this._layoutForm(); this._layoutMap(); } _layoutForm() { // .... } _layoutMap() { console.log("BusinessView layout map"); } }
上面这两段代码是很典型的基于继承的 OOP 思想,本意是期望各个层次的类都可以通过 layout()
来进行各层次应该负责的布局任务。但理想和现实总是有差距的,在 JavaScript 中运行就会发现 BusinessView._layoutMap()
被执行了两次,而 MapView._layoutMap()
未执行。为什么?
JavaScript 中如果在祖先和子孙类中定义了相同的名称的方法,默认会调用子孙类中的这个方法。如果想调用祖先类中的同名方法,需要在子孙类中通过 super.
来调用。
这里可以分析一下这个过程:
在子类创建对象的时候,其类和所有祖先类的定义都已经加载了。这个时候
调用 BusinessView.layout()
找到 super.layout()
,开始调用 MapView.layout()
MapView.layout()
中调用this._layoutMap()
于是从当前对象(BusinessView
对象)寻找 _layoutMap()
找到,调用它
你看,由于 BusinessView
定义了 _layoutMap
,所以压根都没去搜索原型链。对的,这是基于原型关系的 OOP 的局限。如果我们看看 C# 的处理过程,就会发现有所不同
调用 BusinessView.layout()
找到 base.layout()
,开始调用 MapView.layout()
MapView.layout()
中调用 this._layoutMap()
如果是,往子类找到最后一个重载(override)函数,调用
如果不是,直接调用
在 MapView
中找到 _layoutMap()
检查是否虚函数
发现区别了吗?关键是在于判断“虚函数”。
然而,这跟私有成员又有什么关系呢?因为私有函数肯定不是虚函数,所以在 C# 中,如果将 _layoutMap
定义为私有,那 MapView.layout()
调用的就一定是 MapView._layoutMap()
。
虚函数的概念有点小复杂。不过可以简单理解为,如果一个成员方法被声明为虚函数,在调用的时候就会延着其虚函数链找到最后的重载来进行调用。
JavaScript 中虽然约定 _
前缀的是私有,那也只是君子之约,它实质上仍然不是私有。君子之约对人有效,计算机又不知道你有这个约定……。但是,如果 JavaScript 真的实现了私有成员,那么计算机就知道了,_layoutMap()
是个私有方法,应该调用本类中的定义,而不是去寻找子类中的定义。
JavaScript 当下没有私有成员,但是我们又需要切时有效地解决私有成员问题,怎么办?当然有办法,用 Symbol
은 _ 접두어가 비공개로 동의됨을 의미합니다
const MapView = (() => { const _layoutMap = Symbol(); return class MapView extends BaseView { layout() { super.layout(); this[_layoutMap](); } [_layoutMap]() { console.log("MapView layout map"); } } })(); const BusinessView = (() => { const _layoutForm = Symbol(); const _layoutMap = Symbol(); return class BusinessView extends MapView { layout() { super.layout(); this[_layoutForm](); this[_layoutMap](); } [_layoutForm]() { // .... } [_layoutMap]() { console.log("BusinessView layout map"); } } })();그러다가 사업의 발전으로 인해 많은 뷰에 지도 레이아웃이 있다는 사실이 발견되었습니다. 여기에서는 상속 방법이 사용되므로 지도 관련 콘텐츠가
BusinessView
에서 MapView
라는 기본 클래스로 추상화됩니다. The 위 두 가지 코드 조각은 상속을 기반으로 하는 매우 일반적인 OOP 아이디어입니다. 원래 의도는 각 수준의 클래스가const _layoutMap = Symbol(); export class MapView extends BaseView { layout() { super.layout(); this[_layoutMap](); } [_layoutMap]() { console.log("MapView layout map"); } }
layout()
을 사용하여 각 수준이 담당해야 하는 레이아웃 작업을 수행할 수 있다고 기대하는 것입니다. 하지만 이상과 현실의 괴리는 항상 존재합니다. JavaScript로 실행해 보면 BusinessView._layoutMap()
이 두 번 실행되는데 MapView._layoutMap()
)을 보게 됩니다. > 실행되지 않습니다. 왜요? Virtual function#🎜🎜##🎜🎜#JavaScript에서는 상위 클래스와 하위 클래스에 동일한 이름의 메소드가 정의되어 있으면 하위 클래스에서 이 메소드가 호출됩니다. 기본적으로. 조상 클래스에서 같은 이름의 메소드를 호출하려면 자손 클래스에서 super.
를 통해 호출해야 합니다. #🎜🎜##🎜🎜#여기서 이 프로세스를 분석할 수 있습니다. #🎜🎜##🎜🎜#하위 클래스가 객체를 생성하면 해당 클래스의 정의와 모든 상위 클래스가 로드됩니다. 이때 #🎜🎜#BusinessView.layout()
#🎜🎜#super.layout()
을 찾아 MapView.layout()
#🎜🎜# 호출을 시작하세요. MapView .layout()에서 <code>this._layoutMap()
을 호출#🎜🎜#BusinessView
)에서 _layoutMap()
#🎜🎜#BusinessView
는 _layoutMap
을 정의하므로, 그래서 프로토타입 체인은 검색도 안 했어요. 예, 이는 프로토타입 관계를 기반으로 한 OOP의 한계입니다. C#의 프로세스를 살펴보면 다르다는 것을 알 수 있습니다#🎜🎜#BusinessView.layout() code >#🎜🎜#
base.layout()
을 찾아 MapView.layout()
#🎜🎜#MapView.layout()
호출 this._layoutMap()
#🎜🎜#MapView
에서 _layoutMap()
#🎜🎜을 찾으세요 #_layoutMap
이 비공개로 정의된 경우 MapView.layout()
가 MapView(). #🎜🎜##🎜🎜#가상함수의 개념은 좀 복잡합니다. 그러나 멤버 메서드가 가상 함수로 선언되면 호출될 때 가상 함수 체인을 따라 호출할 마지막 오버로드를 찾는다는 점을 간단히 이해하면 됩니다. #🎜🎜##🎜🎜#자바스크립트에서는 <code>_
접두어를 비공개로 합의했지만 이는 신사의 약속일 뿐이며 여전히 비공개가 아닙니다. 신사협정은 사람에게는 유효하지만, 컴퓨터는 여러분이 이 계약을 맺고 있는지 알지 못합니다. 그러나 JavaScript가 실제로 비공개 멤버를 구현하는 경우 컴퓨터는 _layoutMap()
이 비공개 메서드임을 인식하고 하위 클래스에서 정의를 찾는 대신 이 클래스에서 정의를 호출해야 합니다. #🎜🎜##🎜🎜#현재 민영화 문제 해결#🎜🎜##🎜🎜#JavaScript 현재 비공개 회원이 없는데, 비공개 회원 문제를 신속하고 효과적으로 해결하려면 어떻게 해야 할까요? 물론 Symbol
과 클로저를 사용하여 해결하는 방법이 있습니다. #🎜🎜##🎜🎜#여기서의 클로저는 함수에서 클로저를 생성하는 방법에 대한 안내가 아니라는 점에 유의하세요. 계속 읽어주세요 #🎜🎜##🎜🎜#우선 이 민영화를 처리할 것임을 분명히 하겠습니다. 유연한 방식으로 발행— — 이는 상위 클래스 호출자가 메소드를 호출할 때 먼저 하위 클래스에서 검색하지 않음을 의미합니다. 이 문제는 문법적으로 해결할 수 없습니다. JavaScript는 특정 인스턴스에서 거꾸로 이름을 지정하는 방법을 찾아야 합니다. 하지만 메소드 이름을 찾을 수 없다면 어떻게 될까요? #🎜🎜#之所以能找到,因为方法名是字符串。一个字符串在全局作用域内都表示着同样的意义。但是 ES2015 带来了 Symbol
,它必须实例化,而且每次实例化出来一定代表着不同的标识 —— 如果我们将类定义在一个闭包中,在这个闭包中声明一个 Symbol
,用它来作为私有成员的名称,问题就解决了,比如
const MapView = (() => { const _layoutMap = Symbol(); return class MapView extends BaseView { layout() { super.layout(); this[_layoutMap](); } [_layoutMap]() { console.log("MapView layout map"); } } })(); const BusinessView = (() => { const _layoutForm = Symbol(); const _layoutMap = Symbol(); return class BusinessView extends MapView { layout() { super.layout(); this[_layoutForm](); this[_layoutMap](); } [_layoutForm]() { // .... } [_layoutMap]() { console.log("BusinessView layout map"); } } })();
而现代基于模块的定义,甚至连闭包都可以省了(模块系统会自动封闭作用域)
const _layoutMap = Symbol(); export class MapView extends BaseView { layout() { super.layout(); this[_layoutMap](); } [_layoutMap]() { console.log("MapView layout map"); } }
const _layoutForm = Symbol(); const _layoutMap = Symbol(); export class BusinessView extends MapView { layout() { super.layout(); this[_layoutForm](); this[_layoutMap](); } [_layoutForm]() { // .... } [_layoutMap]() { console.log("BusinessView layout map"); } }
改革过后的代码就可以按预期输出了:
BaseView Layout MapView layout map BusinessView layout map
相关推荐:
위 내용은 js의 private 멤버에 대한 종합 분석(코드 포함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!