введение
в предыдущем постеИнтервьюер: Расскажите мне о принципе реализации входа в WeChat по скан-коду?После публикации статьи я не ожидал много лайков.В сообществе Nuggets так много front-end партнеров.Давайте в будущем будем более активными в сообществе Nuggets.Кратко расскажу о серии рубрик,которые я буду организовать:
раньше, былCSDN
платформа для публикации блогов,Чаойи の Блог по технологиям обучения, я обнаружил, что фронтальная активность не очень высока, но в сообществе Nuggets я увидел серию отличных статей с тысячами лайков и сотнями тысяч посещений, и качество статей действительно высокое, я могу научиться много Знаний.
Здесь я исследую проблему, смотрю на проблему с любопытством и пытаюсь представить ее всем в кратких и простых для понимания словах.Возможность учить других также весьма полезна и полезна для меня.
Здесь я хотел бы поделиться следующими правилами, которые люди помнят из лекции преподавателя управления ИТ-проектами в прошлом семестре:
- 5% того, что слышно
- 10% прочитанного
- просмотрено 30% контента
- 50% того, что обсуждалось
- 75% контента сделано лично
- 90% того, что было преподано другим
Если это поможет вам, пожалуйстаОдна кнопка три ссылки, Конечно, если в этой статье есть какие-либо проблемы, читатели могут меня поправить, это также процесс обучения, спасибо~
возвращаемый текст
Спешите вернуться к теме, этот вопрос такой же, как и в предыдущем блоге, он тоже был задан в августе этого года, в то время я знал о классе, и я видел некоторые знания о наследовании классов в сообществе, но я особо не трогал Код, до сих пор помню, что сцена диалога в то время была такая:
Интервьюер: Вы должны знать ES6, верно? (Конечно), ну тогда вы знаете, что в ES6 есть класс, вы можете спроектировать и реализовать его приватные свойства?
Я: эммм (я думал об этом в это время, кажется, я могу сделать это с замыканиями), могу ли я использовать идею замыканий, чтобы сделать это?
Интервьюер: Конечно (покажите мне код)
Итак, я написал этот код:
class classA{
// xxx省略
let fun = function () {
var a = 0;
return function () {
console.log(++a);
}
}
// xxx省略
}
Небольшая часть кода опущена, но в целом код похож на приведенный выше код. Оглядываясь назад, я понимаю, что в то время это было действительно смешно. Неудивительно, что интервьюер остановился и синтаксис был неправильным. При этом, когда я ее писал, я был в оцепенении, потому что код синтаксиса был не очень знаком, но сегодня я решил эту проблему с любопытством.
Почему появляется класс
На самом деле научилсяjava
приятель должен быть правclass
Я с этим не знаком. Во время учебы на втором и младшем курсах я также былjava
Код пишется и пишется. так почемуJS
быть представленным вclass
Шерстяная ткань?
До es6, хотя JS и Java были языками ООП (объектно-ориентированными), в JS существовала только концепция объектов и не было классов.
Появление класса в es6 сократило дистанцию между JS и традиционными языками ООП. Однако это лишьсинтаксический сахарНу, он не может достичь та же функций, что и традиционные языки ООП. Среди них один из самых больших болевых точек - проблема частных атрибутов.
Что такое частная собственность?
Частные свойства являются очень распространенной функцией в объектно-ориентированном программировании (ООП) и обычно соответствуют следующим характеристикам:
- Можно получить доступ различными методами внутри класса, но не вне класса;
- Подклассы не могут наследовать частные свойства суперклассов.
В Java вы можете использоватьprivate
Реализовать приватные переменные, но, к сожалению, в JS эта функция недоступна.
Предложение для частной собственности
В июне 2015 года в качестве стандарта был выпущен ES6.В ознаменование этого исторического момента этот стандарт также называется ES2015.До сих пор класс в JavaScript был изменен с запасного колеса. Однако проблема частной собственности не была решена, и возникло предложение - добавить перед названием собственности#
, используемый для представления частных свойств.
class Foo {
#a; // 定义私有属性
constructor(a, b) {
this.#a = a;
this.b = b
}
}
Объявление частных свойств приведенного выше кода должно быть скомпилировано компилятором, таким как Babel, прежде чем его можно будет использовать в обычном режиме.
А почему бы и нетprivate
Как насчет ключевых слов? Ссылаясь на то, что сказал большой парень, это то, что одной из основных причин является сближение с Python, В конце концов, JS развивается в сторону Python с es6.
Как спроектировать и реализовать частную собственность?
Выше мы ввели причину Class, и это не решило проблему частных свойств, тогда мы использовалиJSer
Как вы сами его проектируете? Разберемся с любопытством:
соглашение об именовании
В настоящее время наиболее распространенный метод:соглашение об именовании, так как это еще не решено, мы не можем определить его сами, можно ли использовать его как частное свойство для специального имени? Если все будут следовать этой норме, разве эта проблема не будет решена?
/* 约定命名 */
class ClassA {
constructor(x) {
this._x = x;
}
getX() {
return this._x;
}
}
let classa = new ClassA(1);
/* 此时可以访问我们自定义私有属性命名的_x */
console.log(classa._x); // 1
console.log(classa.getX()); // 1
Очевидно, что вышеописанный метод прост и удобен, каждый может следовать спецификации, а лучше читать чужой код.
Закрытие
Одним из преимуществ замыканий является то, что они могут защищать внутренние свойства, чего я и хотел добиться в самом начале.constructor
В области следующий код:
/* 闭包 */
class ClassB {
constructor(x) {
let _x = x;
this.getX = function(){
return _x;
}
}
}
let classb = new ClassB(1);
/* 此时不可以访问我们自定义私有属性命名的_x */
console.log(classb._x); // undefined
console.log(classb.getX()); // 1
Очевидно, что если приватных свойств становится все больше и больше, он будет выглядеть раздутым, вызывая определенные затруднения при последующем обслуживании, и читать другим будет не очень дружелюбно. В то же время методы, которые ссылаются на закрытые переменные, не могут быть определены в цепочке прототипов.
Расширенное закрытие
в состоянии пройтиIIFE
(Немедленная функция исполнения. Выражение) Установление закрытия, в котором установление переменной и класса, ссылочные переменные для достижения частных переменных через класс.
/* 进阶版闭包 */
const classC = (function () {
let _x;
class ClassC {
constructor(x) {
_x = x;
}
getX() {
return _x;
}
}
return ClassC;
})();
let classc = new classC(3);
/* 此时不可以访问我们自定义私有属性命名的_x */
console.log(classc._x); // undefined
console.log(classc.getX()); // 3
Таким образом, это немногомодульныйИдею модульности рекомендую в этой предыдущей статье:
Проблемы, возникающие в связи с приближением замыканий?
Выше мы использовали замыкания и расширенные замыкания для решения проблемы приватных свойств, но это проблематично.В качестве примера возьмем расширенные замыкания:
/* 进阶版闭包带来的问题 */
const classC = (function () {
let _x;
class ClassC {
constructor(x) {
_x = x;
}
getX() {
return _x;
}
}
return ClassC;
})();
let classc1 = new classC(3);
/* 此时不可以访问我们自定义私有属性命名的_x */
console.log(classc1._x); // undefined
console.log(classc1.getX()); // 3
/* 问题引出:此时新创建一个实例 */
let classc2 = new classC(4);
/* 出现了问题:实例之间会共享变量 */
console.log(classc1.getX()); // 4
Из вышеприведенного кода видно, что нельзя создавать приватные переменные с замыканиями, переменные будут общими между инстансами, как если бы их инстанцировало несколько человек, но операция все равно остается одним и тем же свойством, что явно нежелательно.
Symbol
использоватьSymbol
переменные могут быть объектамиkey
Функции, которые мы можем смоделировать, чтобы реализовать более реалистичные частные свойства.
/* Symbol */
const classD = (function () {
const _x = Symbol('x');
class ClassD {
constructor(x) {
this[_x] = x;
}
getX() {
return this[_x];
}
}
return ClassD;
})();
let classd = new classD(4);
/* 此时不可以访问我们自定义私有属性命名的_x */
console.log(classd._x); // undefined
console.log(classd.getX()); // 4
classd[_x] = 1;
console.log(classd[_x]); // ReferenceError: _x is not defined
Что касается приведенного выше кода, я ссылаюсь на ответ в области комментариев внизу статьи:
Sysmol работает с синтаксисом шаблонов импорта/экспорта. Например, в A.js вы определяете класс A и Symbol (просто используйте свой метод написания) и предоставляете внешнему миру только класс A. Затем введите экземпляр класса A в другие файлы js, значение Symbol не может быть получено, а имя переменной не может быть доступно через '.' (Symbol уникален и не может быть получен без раскрытия внешнего мира). Это личное.
Через шаблонную перспективу мы выставляемClassD
,Symbol
Единственный, он не будет разоблачен, и внешний мир не сможет его получить, но это не лишено недостатков, см. следующий код:
console.log(classd[Object.getOwnPropertySymbols(classd)[0]]); // 4
Оказывается, ES6Object.getOwnPropertySymbols
Вы можете получить атрибут символа, и сегодня я узнал кое-что новое(*^▽^*)
Чтобы решить вышеуказанную проблему, мы должны ввести новую вещь:WeakMap
WeakMap
/* WeakMap */
const classE = (function () {
const _x = new WeakMap();
class ClassE {
constructor(x) {
_x.set(this, x);
}
getX() {
return _x.get(this);;
}
}
return ClassE;
})();
let classe = new classE(5);
/* 此时不可以访问我们自定义私有属性命名的_x */
console.log(classe._x); // undefined
console.log(classe.getX()); // 5
Этот метод очень хорошо решает проблему частной собственности.WeakMap 和 Map
Сопутствующие знания, планирую продолжить обсуждение в следующей статье, эти знания в настоящее время особо не знакомы, наверное понимаю, что их нельзя пройти, слабо ссылаться, можно обратить внимание на последующие статьи.
О дополнительных обновлениях WeakMap
Дополнительное обновление от 12 октября
Вопрос, заданный @HsuYang в области комментариев: если вы хотите поддерживать несколько частных переменных, есть ли проблемы с использованием Map здесь?
Итак, я попробовал несколько частных переменных, сначала взгляните на следующий код:
/* WeakMap */
const classE = (function () {
const _x = new WeakMap();
class ClassE {
constructor(x, y) {
_x.set(this, x);
_x.set(this, y);
}
getX() {
return _x.get(this);;
}
}
return ClassE;
})();
let classe = new classE(5, 6);
/* 此时不可以访问我们自定义私有属性命名的_x */
console.log(classe.getX()); // 6
Эй, вы нашли проблему? Последний вывод, который у нас есть, только_y
Первоначально эта частная собственность появиласьпроблема покрытия, так как решить эту проблему?
Поскольку частные свойства должны быть связаны с экземплярами, возможно ли создать объект, содержащий все частные свойства для обслуживания? Таким образом, все частные свойства хранятся в нем, что решает проблему нескольких частных переменных. В то же время эта техника также полезна, то есть при прохождении свойств или выполненияJSON.stringify
Частные свойства экземпляра не отображаются.
Но он опирается на доступный и управляемыйWeakMap
Переменная.
const map = new WeakMap();
// 创建一个在每个实例中存储私有变量的对象
const internal = (obj) => {
if (!map.has(obj)) {
map.set(obj, {});
}
return map.get(obj);
}
class ClassE {
constructor(name, age) {
internal(this).name = name;
internal(this).age = age;
}
get userInfo() {
return '姓名:' + internal(this).name + ',年龄:' + internal(this).age;
}
}
const classe1 = new ClassE('Chocolate', 18);
const classe2 = new ClassE('Lionkk', 19);
console.log(classe1.userInfo); // 姓名:Chocolate,年龄:18
console.log(classe2.userInfo); // 姓名:Lionkk,年龄:19
/* 无法访问私有属性 */
console.log(classe1.name); // undefined
console.log(classe2.age); // undefined
Proxy
В области комментариев друг @ shu millet предположил, что его можно использоватьПерехват настроек проксиЭтот способ сделать, теперь добавить.
Прокси — это замечательная новая функция в JavaScript, которая позволит вам эффективно обернуть объект в объект, называемый прокси, и перехватывать все взаимодействия с этим объектом. Мы будем использовать прокси и следовать вышеизложенномусоглашение об именованиидля создания закрытых переменных, но сделать доступ к этим закрытым переменным ограниченным за пределами класса.
Прокси может перехватывать множество различных типов взаимодействий, но мы хотим сосредоточиться наget
а такжеset
, Proxy позволяет нам перехватывать операции чтения и записи для свойства соответственно. При создании прокси вы указываете два параметра, первый предназначен для переносаПример, а второй вы определяете, что хотите перехватывать разные методыОбъект «обработчик».
Наш процессор будет выглядеть так:
const handler = {
get: function(target, key) {
if (key[0] === '_') {
throw new Error('Attempt to access private property');
}
return target[key];
},
set: function(target, key, value) {
if (key[0] === '_') {
throw new Error('Attempt to access private property');
}
target[key] = value;
}
};
В каждом случае мы проверяем, начинается ли имя свойства, к которому осуществляется доступ, с символа подчеркивания, и если да, то выдаем ошибку, препятствующую доступу к нему.
Зарезервировано для использования вышеуказанным методомinstanceof
способность (эта проблема возникает в закрывающем элементе), но в это время возникает новая проблема:
когда мы пытаемся выполнитьJSON.stringify
Проблема возникает из-за того, что он пытается отформатировать частное свойство. Чтобы исправить это, нам нужно переписатьtoJSON
функционировать толькоВозвращает "общедоступное" свойство. Мы можем сделать это, обновив нашget
процессор для обработкиtoJSON
Конкретный случай:
Примечание. Это переопределит любую пользовательскую функцию toJSON.
get: function(target, key) {
if (key[0] === '_') {
throw new Error('Attempt to access private property');
} else if (key === 'toJSON') {
const obj = {};
for (const key in target) {
if (key[0] !== '_') { // 只复制公共属性
obj[key] = target[key];
}
}
return () => obj;
}
return target[key];
}
Затем мы можем интегрировать код:
class Student {
constructor(name, age) {
this._name = name;
this._age = age;
}
get userInfo() {
return '姓名:' + this._name + ',年龄:' + this._age;
}
}
const handler = {
get: function (target, key) {
if (key[0] === '_') { // 访问私有属性,返回一个 error
throw new Error('Attempt to access private property');
} else if (key === 'toJSON') {
const obj = {};
for (const key in target) { // 只返回公共属性
if (key[0] !== '_') {
obj[key] = target[key];
}
}
return () => obj;
}
return target[key]; // 访问公共属性,默认返回
},
set: function (target, key, value) {
if (key[0] === '_') {
throw new Error('Attempt to access private property');
}
target[key] = value;
}
}
const stu = new Proxy(new Student('Chocolate', 21), handler);
console.log(stu.userInfo); // 姓名:Chocolate,年龄:21
console.log(stu instanceof Student); // true
console.log(JSON.stringify(stu)); // "{}"
for (const key in stu) {
console.log(key); // _name _age
}
Теперь мы инкапсулировали наши частные свойства, и ожидаемая функциональность все еще существует, единственное предостережение заключается в том, что наши частные свойства все еще доступны для просмотра.for(const key in stu)
перечислит_name
а также_age
.
Чтобы решить вышеупомянутую проблему обхода частных свойств, я думаю, что дескриптор свойства может манипулировать соответствующими свойствами объекта, а затем настраиватьenumerable
, в самый разProxy
может справиться с этим, он может перехватитьgetOwnPropertyDescriptor
вызывать и манипулировать выводом нашего частного свойства, код выглядит следующим образом:
getOwnPropertyDescriptor(target, key) {
const desc = Object.getOwnPropertyDescriptor(target, key);
if (key[0] === '_') {
desc.enumerable = false;
}
return desc;
}
Для получения подробной информации см.:
Справочная документация по Object.getOwnPropertyDescriptor
Наконец-то у нас есть финальная полная версия, поздравляю(*^▽^*)
, код интеграции выглядит следующим образом:
class Student {
constructor(name, age) {
this._name = name;
this._age = age;
}
get userInfo() {
return '姓名:' + this._name + ',年龄:' + this._age;
}
}
const handler = {
get: function (target, key) {
if (key[0] === '_') { // 访问私有属性,返回一个 error
throw new Error('Attempt to access private property');
} else if (key === 'toJSON') {
const obj = {};
for (const key in target) { // 只返回公共属性
if (key[0] !== '_') {
obj[key] = target[key];
}
}
return () => obj;
}
return target[key]; // 访问公共属性,默认返回
},
set: function (target, key, value) {
if (key[0] === '_') {
throw new Error('Attempt to access private property');
}
target[key] = value;
},
// 解决私有属性能遍历问题,通过访问属性对应的属性描述符,然后设置 enumerable 为 false
getOwnPropertyDescriptor(target, key) {
const desc = Object.getOwnPropertyDescriptor(target, key);
if (key[0] === '_') {
desc.enumerable = false;
}
return desc;
}
}
const stu = new Proxy(new Student('Chocolate', 21), handler);
console.log(stu.userInfo); // 姓名:Chocolate,年龄:21
console.log(stu instanceof Student); // true
console.log(JSON.stringify(stu)); // "{}"
for (const key in stu) { // No output 不能遍历私有属性
console.log(key);
}
stu._name = 'Lionkk'; // Error: Attempt to access private property
новый подход
С точки зрения тенденций развития, TS стал одним из необходимых навыков во фронтенде.TypeScript
Частное — хорошее решение проблемы частной собственности, и я узнаю об этом позже.ts
Добавьте позже.
Приложение: Обработка в TypeScript
TypeScript — это расширенный набор JavaScript, который компилируется в собственный JavaScript для использования в производстве. Разрешение указывать частные, общедоступные или защищенные свойства — одна из функций TypeScript.
class Student {
private name;
private age;
constructor(name, age) {
this.name = name;
this.age = age;
}
get userInfo() {
return '姓名:' + this.name + ',年龄:' + this.age;
}
}
const stu = new Student('Chocolate', 21);
console.log(stu.userInfo); // 姓名:Chocolate,年龄:21
При использовании TypeScript важно отметить, что он работает только тогда, когдакомпилироватьЭти типы известны только во время компиляции, а модификаторы private и public вступают в силу только во время компиляции. если вы попытаетесь получить доступstu.name
, вы обнаружите, что это действительно возможно. Просто TypeScript выдаст вам ошибку при компиляции, но не остановит компиляцию.
// 编译时错误:属性 ‘name’ 是私有的,只能在 ‘Student ’ 类中访问。
console.log(stu.name); // 'Chocolate'
TypeScript не будет достаточно умен, чтобы попытаться предотвратить доступ кода к закрытым свойствам во время выполнения. Я просто перечисляю это здесь, чтобы люди поняли, что это не решает проблему напрямую.
Кроме того, частные переменные класса TypeScript окончательно компилируются черезWeakMap
Для этого ответы друзей в комментариях~
На этом эта статья окончена, и последующие статьи будут ускорены и ближе, изучая и размышляя с любопытством~
Если у ваших друзей есть лучший способ, добро пожаловать в общение.Конечно, в этой статье могут быть сомнения.Вы можете меня поправить.Это также процесс обучения.Спасибо~
Особая благодарность друзьям в разделе комментариев,Класс дизайна в ES6 для реализации частных свойствЯ лучше разбираюсь в этой проблеме, спасибо Thanks♪(・ω・)ノ
Ссылка в этой статье
Говоря о частных переменных класса
Несколько методов реализации приватных свойств в ES6 Class
ES6 имитирует приватные атрибуты + поверхностный тест интерфейса 100
Спасибо за вышеуказанные статьи, уважаю плоды труда, настоящим выдвигаю ссылку на оригинальный текст.
наконец
Вывод статей не из легких, надеюсь вы поддержите волну!
Прошлые выборы:
Обратите внимание на склад передней части маленького льва
Друзья могут отправлять свой собственный код решения проблем в задачах, 🤝 Добро пожаловать в раздел «Участие», вы можете зарегистрироваться и решить проблему, Поставьте ⭐️, если этот проект помог вам!
Посетите блог Чаои, друзьям удобно читать и играть~
学如逆水行舟,不进则退