js의 private 멤버에 대한 종합 분석(코드 포함)

不言
풀어 주다: 2018-08-14 11:09:52
원래의
1994명이 탐색했습니다.

이 기사는 js의 비공개 멤버에 대한 포괄적인 분석을 제공합니다. 이는 특정 참고 가치가 있습니다. 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.

JavaScript에 대한 클래스 필드 선언(JavaScript 클래스에 대한 필드 선언)이 이제 3단계에 진입했습니다. 여기에는 OOP 개발자가 매우 우려하는 항목인 비공개 필드가 포함됩니다. JavaScript에 비공개 멤버가 없었던 것은 당연하므로 이 제안은 JavaScript에 새로운 도전을 가져옵니다. 그러나 동시에 JavaScript는 ES2015가 출시되었을 때 이미 민영화 문제를 고려하고 있었기 때문에 private 멤버를 구현하는 데 기반이 없는 것은 아닙니다.

pit

먼저 구덩이를 파세요. 이는 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라는 기본 클래스로 추상화됩니다.
const _layoutMap = Symbol();

export class MapView extends BaseView {
    layout() {
        super.layout();
        this[_layoutMap]();
    }

    [_layoutMap]() {
        console.log("MapView layout map");
    }
}
로그인 후 복사
로그인 후 복사
The 위 두 가지 코드 조각은 상속을 기반으로 하는 매우 일반적인 OOP 아이디어입니다. 원래 의도는 각 수준의 클래스가 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#의 프로세스를 살펴보면 다르다는 것을 알 수 있습니다#🎜🎜#
  • #🎜🎜#Call BusinessView.layout() code >#🎜🎜#
  • #🎜🎜#base.layout()을 찾아 MapView.layout()#🎜🎜#
  • #🎜🎜#MapView.layout() 호출 this._layoutMap()#🎜🎜#
    • #🎜🎜#그렇다면 서브클래스에서 마지막 재정의 함수를 찾아 #🎜🎜#
    • #🎜을 호출하세요. 🎜#그렇지 않다면 #🎜🎜#
    • #🎜🎜#MapView에서 _layoutMap()#🎜🎜을 찾으세요 #
    • #🎜🎜#가상함수인지 확인#🎜🎜#
#🎜🎜#차이점을 느끼셨나요? 핵심은 "가상 기능"을 결정하는 것입니다. #🎜🎜##🎜🎜#그런데 이게 비공개 회원이랑 무슨 상관이 있는 걸까요? 비공개 함수는 확실히 가상 함수가 아니기 때문에 C#에서는 _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中对执行上下文以及变量对象的解析

js中字符方法以及字符串操作方法的总结(附代码)

위 내용은 js의 private 멤버에 대한 종합 분석(코드 포함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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