Говоря о частных переменных класса

JavaScript

Прошлое и настоящее класса

До es6, хотя JS и Java были языками ООП (объектно-ориентированными), в JS существовала только концепция объектов и не было классов.

В JS традиционный способ создания объектов экземпляра — через конструктор, например:

function A (x) {
    this.x = x
}

// 在原型链上挂载原型方法
A.prototype.showX = function () {
    return this.x
}

// 生成对象实例
let a = new A(1)
// 调用原型方法
a.showX()	// 1

По сравнению с написанием классов на традиционных языках ООП, этот метод написания сбивает с толку многих новичков в JS, которые изучили другие языки ООП.

Чтобы реализовать желание писать Java на JS, некоторые люди инкапсулировали метод написания конструктора в синтаксический сахар класса, аналогичный методу написания классов в Java.

Кто-то может спросить, почему класс вместо класса? Конечно, поскольку class является зарезервированным ключевым словом в JS, прямое использование class сообщит об ошибке.

Так было много лет, до выхода es6, в es6 класс наконец-то превратил запаску в плюс, трансформировался в класс, и наконец воплотил мечту с официальной точки зрения.

Предыдущий код, преобразованный в класс, выглядит так:

class A {
    // 构造函数,相当于之前的函数A
    constructor(x) {
        this.x = x
    }
    
    // 相当于挂载在原型链上的原型方法
    showX () {
        return this.x
    }
}

// 生成对象实例
let a = new A(1)
// 调用原型方法
a.showX()	// 1

Можно обнаружить, что метод написания класса ближе к традиционному языку ООП.

Недостаточность класса

Похоже, появление класса в es6 сблизило JS и традиционные ООП-языки. Однако, как и класс, упомянутый ранее, это просто синтаксический сахар, и он не может выполнять те же функции, что и традиционные языки ООП. Среди них одной из самых больших болевых точек является проблема частных переменных.

Что такое частная переменная? Частная переменная — это переменная, доступ к которой возможен только внутри класса и невозможен снаружи. В процессе разработки многие переменные или методы, к которым вы не хотите, чтобы другие имели доступ, могут быть определены как частные переменные, чтобы предотвратить использование других. В Java можно использовать private для реализации приватных переменных, но, к сожалению, в JS эта функция недоступна.

Взгляните на следующий код:

class A {
    constructor(x) {
        this.x = x
    }
	
    // 想要通过该方法来暴露x
    showX () {
        return this.x
    }
}

let a = new A(1)

// 直接访问x成功
a.x	// 1

Можно видеть, что хотя намерение состоит в том, чтобы выставить значение x через метод showX, его можно передать напрямую черезa.xдля прямого доступа к значению x.

Очевидно, это влияет на инкапсуляцию кода. Имейте в виду, что эти свойства доступныfor...inвыйти.

Следовательно, необходимо реализовать функцию частной переменной класса.

Реализовать частные переменные класса

Хотя сам класс не предоставляет функцию частных переменных, мы можем получить функции, в некотором роде похожие на частные переменные.

соглашение об именовании

Во-первых, это наиболее широко используемый метод в настоящее время:соглашение об именовании,Также известен как:Обманывать себяилиНегласные правила.

Метод очень прост, то есть команда согласовывает метод именования, представляющий частные переменные, обычно добавляя знак подчеркивания перед именем частной переменной. код показывает, как показано ниже:

class A {
    constructor(x) {
        // _x 是一个私有变量
        this._x = x
    }

    showX () {
        return this._x
    }
}

let a = new A(1)

// _x 依然可以被使用
a._x		// 1
a.showX()	//1

Можно обнаружить, что самым большим преимуществом этого метода является простота и удобство, поэтому многие команды приняли этот метод.

Однако такой подход принципиально не решает проблему, если использоватьfor...inТак называемые частные переменные все еще могут быть пройдены, что, как можно сказать, лечит симптомы, а не первопричины.

Тем не менее, об этом методе стоит сказать, что другим удобно читать код по соглашению.

Закрытие

Замыкания часто используются для решения проблемы модульности.Очевидно, что приватные переменные по сути являются модульной проблемой, поэтому мы также можем использовать замыкания для решения проблемы приватных переменных.

Мы определяем локальную переменную в конструкторе, а затем ссылаемся на нее через метод, переменная становится настоящей частной переменной.

class A {
    constructor (x) {
        let _x = x
        this.showX = function () {
            return _x
        }
    }
}

let a = new A(1)
// 无法访问
a._x		// undefined
// 可以访问
a.showX()	// 1

Самым большим преимуществом этого метода является то, что он по существу решает проблему частных переменных.

Но есть большая проблема, в этом случае метод, который ссылается на приватную переменную, не может быть определен на цепочке прототипов, а может быть определен только в конструкторе, то есть на экземпляре. Это приводит к двум недостаткам:

  1. Добавлены дополнительные накладные расходы на производительность
  2. Конструктор содержит методы, которые раздуты и создают определенные проблемы для последующего сопровождения (много раз, когда вы видите код, написанный в кучу, вы не хотите его читать -_-)

Расширенное закрытие

Усовершенствованная версия метода замыкания может в основном прекрасно решить вышеуказанную проблему: поскольку определить замыкание внутри конструктора так сложно, могу ли я поместить его вне класса?

мы можем пройтиIIFE(Немедленно выполнить функциональное выражение) Создать замыкание, создать в нем переменную и класс и реализовать приватную переменную через переменную ссылки на класс.

код показывает, как показано ниже:

// 利用闭包生成IIFE,返回类A
const A = (function() {
    // 定义私有变量_x
    let _x

    class A {
        constructor (x) {
            // 初始化私有变量_x
            _x = x
        }

        showX () {
            return _x
        }
    }

    return A
})()

let a = new A(1)

// 无法访问
a._x		// undefined
// 可以访问
a.showX()	//1

Можно обнаружить, что этот метод отлично решает проблему предыдущего закрытия, но метод записи относительно сложен, кроме того, требуется дополнительное создание.IIFE, с небольшим дополнительным снижением производительности.

Примечание 1: этот метод также нельзя использоватьIIFE, можно напрямую поместить приватные переменные в глобальные, но это не способствует инкапсуляции, поэтому я использовал здесьIIFEПуть.

Примечание 2: ДляIIFEЭто закрытие, вYou-Dont-Know-JSВ этой книге были споры, и заинтересованные студенты могут пойти узнать об этом, и я не буду повторять их здесь.

Symbol

Этот способ используетSymbolУникальность -Самое большое преимущество врага в том, что он знает значение нашего ключа.Если я сделаю значение ключа уникальным, враг не сможет получить к нему доступ?(Заложник является ключом к этой миссии.Когда у врага больше нет заложника, миссия завершена.)

код показывает, как показано ниже:

// 定义symbol
const _x = Symbol('x')

class A {
    constructor (x) {
        // 利用symbol声明私有变量
        this[_x] = x
    }
    showX () {
        return this[_x]
    }
}

let a = new A(1)

// 自行定义一个相同的Symbol
const x = Symbol('x')
// 无法访问
a[x]		// undefined
// 可以访问
a.showX()	//1

Судя по результатам, частные переменные класса отлично реализованы.

Лично для меня это самый совершенный способ реализации приватных переменных на данный момент, единственный недостаток в том, чтоSymbolОн широко не используется, и многие студенты не знакомы с ним.

Предложение для частной собственности

В ответ на проблему, что классы в es6 не имеют приватных атрибутов, было сгенерировано предложение - добавление перед именем атрибута#, используемый для представления частных свойств.

class A {
    #x = 0
    constructor (x) {
        #x = x
    }
    showX () {
        return this.#x
    }
}

У многих студентов возникнет вопрос, почему в предложении частного атрибута вместо этого не используется частный атрибут.#?Это искажение человеческой природы или упадок нравственности?Это связано с производительностью компилятора (на самом деле лично я считаю, что есть большой резон приблизиться к Python, ведь JS развивается в сторону Python с es6), и заинтересованные студенты могут узнать об этом.

Однако это предложение всего лишь предложение, и оно не вошло в стандарт, поэтому пока недоступно.

наконец

Если вам недостаточно всех вышеперечисленных методов, есть ультимативный метод — TypeScript. Используя TS, вы получите удовольствие от написания Java на JS! Просто частные переменные, не более того.

Судя по тренду развития этого года, TS стал одним из необходимых навыков во фронтенде, даже You Da, который раньше диссил TS, начал переписывать Vue с TS (You Da: реально ароматный).

последний из последних

Пришло время снова попросить звезду. Если вы считаете, что эта статья неплохая, почему бы вам не поставить мне звезду перед уходом~

Github