Home  >  Article  >  Web Front-end  >  Comprehensive analysis of private members in js (with code)

Comprehensive analysis of private members in js (with code)

不言
不言Original
2018-08-14 11:09:521991browse

This article brings you a comprehensive analysis of private members in js (with code). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

Class field declarations for JavaScript (Field declarations of JavaScript classes) have now entered stage-3, which includes an item that OOP developers are very concerned about: Private fields. It’s not for nothing that JavaScript has never had private members, so this proposal brings new challenges to JavaScript. But at the same time, JavaScript was already considering the issue of privatization when ES2015 was released, so it is not without foundation to implement private members.

pit

First dig a pit - this is a piece of JS code. BusinessView needs to do two things, namely layout the form and map.

Represents the _ prefix as private
class BaseView {
    layout() {
        console.log("BaseView Layout");
    }
}

class BusinessView extends BaseView  {
    layout() {
        super.layout();
        this._layoutForm();
        this._layoutMap();
    }

    _layoutForm() {
        // ....
    }

    _layoutMap() {
        // ....
    }
}

Then, due to the development of the business, it was discovered that many views have map layouts. The inheritance method is used here, so the map-related content is abstracted from BusinessView into a base class called 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");
    }
}

The above two pieces of code are A very typical OOP idea based on inheritance, the original intention is to expect that classes at each level can use layout() to perform the layout tasks that each level should be responsible for. But there is always a gap between ideal and reality. When running in JavaScript, you will find that BusinessView._layoutMap() is executed twice, while MapView._layoutMap() is not executed. Why?

Virtual function

In JavaScript, if a method with the same name is defined in the ancestor and descendant classes, the method in the descendant class will be called by default. If you want to call the method with the same name in the ancestor class, you need to call it through super. in the descendant class.

Here you can analyze this process:

When a subclass creates an object, the definitions of its class and all ancestor classes have been loaded. At this time

  • callBusinessView.layout()

  • findsuper.layout() , start calling MapView.layout()

  • MapView.layout() and call this._layoutMap()

    • So find _layoutMap()

      ## from the current object (
    • BusinessView
    • object) #Find and call it

You see, because

BusinessView defines _layoutMap, the prototype chain is not searched at all. Yes, this is a limitation of OOP based on prototype relationships. If we look at the C# process, we will see a difference

  • Call

    BusinessView.layout()

  • Find

    base.layout() and start calling MapView.layout()

  • ##MapView.layout()

    Call this._layoutMap()

    • If so, find the last override function in the subclass and call
    • If not, call
    • directly and find
    • _layoutMap()

      # in MapView

      ##Check if the virtual function
    • Did you find the difference? The key is to determine the "virtual function".
However, what does this have to do with private members? Because private functions are definitely not virtual functions, in C#, if

_layoutMap

is defined as private, then

MapView.layout() must call MapView._layoutMap( ). The concept of virtual functions is a bit complicated. However, it can be simply understood that if a member method is declared as a virtual function, when it is called, it will follow its virtual function chain to find the last overload to call.

Although the
_
prefix is ​​agreed to be private in JavaScript, it is just a gentleman's agreement, and it is still not private in nature. A gentleman's agreement is valid for people, but the computer doesn't know that you have this agreement... However, if JavaScript really implements private members, then the computer will know that

_layoutMap() is a private method and should call the definition in this class instead of looking for the definition in the subclass. Solution to the current privatization problem

JavaScript There are currently no private members, but we need to solve the problem of private members promptly and effectively. What should we do? Of course there is a way to solve it using

Symbol

and closures.

Note that the closure here is not a guide to generating a closure in a function, please continue reading

First of all, let’s make it clear that we will look at this privatization issue in a flexible way - that is, let the ancestors When a class caller calls a method, it will not first search in the subclass. This problem cannot be solved grammatically. JavaScript has to look for the method of specifying the name from the specific instance backwards. But what if the method name cannot be found?

之所以能找到,因为方法名是字符串。一个字符串在全局作用域内都表示着同样的意义。但是 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中字符方法以及字符串操作方法的总结(附代码)

The above is the detailed content of Comprehensive analysis of private members in js (with code). For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn