问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

JavaScript 中有可能模拟出类的私有变量吗

发布网友 发布时间:2022-04-25 16:12

我来回答

1个回答

热心网友 时间:2022-04-22 17:07

作者:寸志
链接:https://www.hu.com/question/23588490/answer/30007521
来源:知乎
著作权归作者所有,转载请联系作者获得授权。

在JavaScript中谈私有属性和私有方法就是扯淡,"private"还杵在保留字的位置上,不知道什么时候提上来实现真正的私有。那今天咱就来讨论下如何以JS当前的特性来实现私有成员。
闭包
(比较枯燥,可以跳过本单元)JavaScript实现私有属性必须依赖闭包特性(可以先通过该链接补习)。下面也稍微补习下,看下面的例子:
var uniqueId;
uniqueId = (function() {
var index;
index = 0;
return function(prefix) {
return prefix + "_" + index++;
};
})();
//c_0
console.log(uniqueId("c"));
//c_1
console.log(uniqueId("c"));

通常所说的或所看到的闭包就是这样子——(function(){})(),但这不是它的全部或者是本质。在定义uniqueId这个函数的时候,我们使用了匿名函数表达式(请注意(function(){})是函数表达式)定义了一个函数且立即执行,把function(prefix){/*some code*/}作为返回值赋值给了quniqueId,此时这个function(prefix){/*some code*/}已经生成了函数实例,在函数实例生成的过程中:
【1】通俗的讲将index这个外部函数定义的变量记住了(为什么要记住?没记住你让我怎么给你计算prefix+"_"+index的值嘛!);
【2】再次我们没法通过什么this.index或者someObj.index引用到index,改变其值了,(function(){})()这个一执行完,局部变量index在外面怎么调得到嘛;
【3】怎么调得到,只能靠function(prefix){/*some code*/},因为我们还能通过它间接的取得或改变index值。这就是闭包了。
比较学术的解释:
【1】JS是词法作用域(就是程序看上去啥样就啥样)的,使用一个叫做[[scope]]的内部属性来标识每个执行上下文的作用域(我可以读写哪些变量啊,调用哪些哪些函数啊);每个函数执行时都由该[scope]作用域加上活动对象来构成真实的执行上下文;
【2】而这个执行上下文[[scope]]属性是在函数生成时就指定的了,不严格的讲为生成该函数时的执行上下文;
【3】于是function(prefix){/*some code*/}生成时其内部的[[scope]]属性引用了(function(){})()执行上下文的scope链;该scope链即包含了该函数的[[scope]]和活动对象,且活动对象包含了index的定义引用;
【4】GC的回收规则,没人用我我就是垃圾!因此uniqueId引用了function(prefix){/*some code*/}函数实例,而该函数实例的[[scope]]引用了(function(){})()执行期的scope链,其包含活动对象,即有index的引用;ok,index还有人引用它,它不是垃圾,因此闭包形成了,我们可以通过uniqueId函数间接的读取或者修改index。
总结:其实学术解释和通俗解释一个意思,不过闭包其实是相对的,并不是我们不能修改index,只是需要间接的方法(是不是有点私有属性和私有方法的感觉)。
私有属性和私有方法
相对来说,构造单例对象的私有属性和方法都比较简单。
var aira;
aira = (function () {
var __getName, __name;
//private variable
__name = "HTC mobile";
//private method
__getName = function () {
return __name;
};
aira = {
init: function () {
//change private variable inner
__name = "aira";
},
hello: function () {
//execute private method inner
console.log("hello,my name is " + (__getName()));
}
};
return aira;
})();
aira.init();
//hello,my name is aira
aira.hello();

使用双下划线"__"表示私有;aira手机有一个私有属性__name和私有方法__getName;我们可以在init中修改__name,在hello中调用__getName,且在闭包外面无法直接调用和修改这两个成员。我们做到了,这就是单例对象的私有属性和私有方法。
但是更确切的说,其实aira能够有私有属性和方法仅仅是因为它有私有的一个闭包,即init和hello成员的[[scope]]都引用了闭包的活动对象。
然而,一个构造函数(类)的私有属性和方法就么这么简单了。
var Phone, aira;
//wrap by function
Phone = function (name) {
var phone;
phone = (function () {
var __getName, __name;
__name = name;
__getName = function () {
return __name;
};
phone = {
init: function (number) {
__name += "#" + number;
},
hello: function () {
console.log("hello,my name is " + (__getName()));
}
};
return phone;
})();
return phone;
};
aira1 = Phone("aira");
aira1.init(1);
//hello,my name is aira#1
aira1.hello();

aira2 = Phone("aira");
aira2.init(2);
//hello,my name is aira#2
aira2.hello();

我们先来简单的将单例对象的构造包裹一个函数,实现产生不同的对象。我们可以说Phone是一个类,因为它可以产生不同的对象,有类似的功能。同样aira1和aira2都有自己闭包,于是都有自己的私有属性和方法。
我想对自己说,别逗了你,这样就行啦?!JS中类的概念就是构造函数。
var Phone, aira1, aira2;
Phone = function (name) {
var __getName, __name;
__name = name;
__getName = function () {
return __name;
};
this.init = function (number) {
__name += "#" + number;
};
this.hello = function () {
console.log("hello,my name is " + (__getName()));
};
};
aira1 = new Phone("aira");
aira1.init(1);
//hello,my name is aira#1
aira1.hello();

aira2 = new Phone("aira");
aira2.init(1);
//hello,my name is aira#2
aira2.hello();

Phone构造函数其实就是闭包的功能,每个Phone实例的init和hello都能引用其构造期间的形成的私有的__name和__getName。
真的,我已经无力回天了,每个实例必须由闭包产生私有属性和方法,因此只能在该闭包中定义公共方法暴露出来(比如说init和hello),这就意味着每次构造一个实例我们都必须生成init和hello的函数实例,这是多么的低效,因为JS有原型。
var Phone, aira;
Phone = function (name) {
var __getName, __name;
__name = name;
__getName = function () {
return __name;
};
};
Phone.prototype.init = function (number) {
__name += "#" + number;
};
Phone.prototype.hello = function () {
console.log("hello,my name is " + (__getName()));
};
aira = new Phone("aira");

上面的代码是错误的(在init中的__name是全局的,hello中的__getName方法因为不存在,所以会报错),这就是问题所在,能够引用私有属性和变量的公共方法必须在闭包中定义,然后暴露出来,然而原型方法并不能在闭包中定义。
曲线救国:
再来确定下什么是私有属性和私有方法,即每个类实例都拥有且只能在类内访问的变量和函数。也就是说变量和方法只能由类的方法来调用。说到这里,我们或许可以尝试下,不让类外的方法调用类的私有方法。
var inner, outer;
outer = function () {
inner();
};
inner = function () {
console.log(arguments.callee.caller);
};
/*
function(){
inner();
}
*/
outer();

从arguments的callee中可获得当前的执行函数inner,而inner的动态属性caller指向了调用inner的外层函数outer,由此看来我们可以使用arguments.callee.caller来确定函数的执行环境,实现私有方法和属性。
var Phone, aira1, aira2;
Function.prototype.__public = function (klass) {
this.klass = klass;
return this;
};
Function.prototype.__private = function () {
var that;
that = this;
return function () {
if (this.constructor === arguments.callee.caller.klass) {
return that.apply(this, arguments);
} else {
throw new Error("" + that + " is a private method!");
}
};
};
Phone = function (name) {
var __name;
__name = name;
this.__getName = (function () {
return __name;
}).__private();
this.__setName = (function (name) {
__name = name;
}).__private();
};
Phone.prototype.init = (function (number) {
this.__setName(this.__getName() + "#" + number);
}).__public(Phone);
Phone.prototype.hello = (function () {
console.log("hello,my name is " + (this.__getName()));
}).__public(Phone);
aira1 = new Phone("aira");
aira1.init(1);
//hello,my name is aira#1
aira1.hello();
aira2 = new Phone("aira");
aira2.init(1);
//hello,my name is aira#2
aira2.hello();
//hello,my name is aira#1
aira1.hello();

try {
aira1.__getName();
} catch (e) {
/*
Error Object
message:"function () {return __name;} is a private method!"
*/
console.log(e);
}

【1】请原谅我给Function原型上添加了两个方法__public和__private以此来实现私有方法的调用环境检测;
【2】其次,我无法给私有属性添加检测,所以私有属性直接不可见,使用私有的get,set方法访问;
【3】本身在aira1外部调用时我们还是能看到__getName和__setName方法,只是不能调用而已;
【4】唯一好的一点是原型方法(公共方法)终于可以从构造函数闭包中解放出来。
私有约定
var Phone, aira1, aira2;
Phone = function (name) {
//"__" private variable
this.__name = name;
};
Phone.prototype.init = function (number) {
this.__name += "#" + number;
};
Phone.prototype.hello = function () {
console.log("hello,my name is " + (this.__getName()));
};
//"__" private method
Phone.prototype.__getName = function () {
return this.__name;
};
aira1 = new Phone("aira");
aira1.init(1);
//hello,my name is aira#1
aira1.hello();
aira2 = new Phone("aira");
aira2.init(2);
//hello,my name is aira#2
aira2.hello();

以双下划线“__”表示私有,用最近看到的一代码注释来解释:“神奇,勿动”。
这是私有方法么?
var Phone, aira1, aira2;
Phone = (function () {
var __getName, __name;
__getName = function () {
return __name;
};
Phone = function (name) {
__name = name;
};
Phone.prototype.init = function (number) {
__name += "#" + number;
};
Phone.prototype.hello = function () {
console.log("hello,my name is " + (__getName()));
};
return Phone;
})();
aira1 = new Phone("aira");
aira1.init(1);
//hello,my name is aira#1 right!
aira1.hello();
aira2 = new Phone("aira");
aira2.init(2);
//hello,my name is aira#2 right!
aira2.hello();
//hello,my name is aira#2 wrong!
aira1.hello();

试图用闭包包住构造函数,形成闭包,但是得到的结果是__name和__getName其实都是类的私有属性,而不是实例的。aira1和aira2共用了__name和__getName。
JavaScript 中有可能模拟出类的私有变量吗

在JavaScript中谈私有属性和私有方法就是扯淡,"private"还杵在保留字的位置上,不知道什么时候提上来实现真正的私有。那今天咱就来讨论下如何以JS当前的特性来实现私有成员。闭包 (比较枯燥,可以跳过本单元)JavaScript实现私有属性必须依赖闭包特性(可以先通过该链接补习)。下面也稍微补习下,看下面的例子...

基于JavaScript如何实现私有成员的语法特征及私有成员的实现方式_jav...

主要的思路是,为每一个私有成员的名称产生一个随机且唯一的字符串key,这个 key 对外不可见,对内的可见性则是通过 js 的闭包变量实现,示例代码如下:JavaScript优点弥补了命名约定方案的缺陷,外部无法通过正常途径获得私有成员的访问权。调试便捷程度上可以接受,一般是通过给 symbol 的构造函数传入一个...

JavaScript 中的 Symbols 怎么用

私有属性 既然任意两个 symbol 都不相等,symbol 可以方便的模拟 JavaScript 中的私有属性。Symbols 不会在 Object.key(),中出现,因为除非你明确 export 一个 symbol,没有任何代码可以访问到这个属性,除非使用 Object.getOwnPropertySymbols() 方法。function getObj() { const symbol = Symbol('test...

JavaScript 为什么要有 Symbol 类型?

JavaScript 之所以引入 Symbol 类型,是因为它为对象属性管理带来了独特的优势,尤其是在避免命名冲突和模拟私有属性方面。尽管字符串在某些场景下也能使用,但 Symbol 提供了不可变性和唯一性,使得在序列化、命名冲突和检查操作中表现出更强的隐私保护。此外,Symbol 还能与 Proxy 结合,实现更复杂的对象操...

什么是javascript封装,封装的方法有几种

如果产生很多实例,内存空间会大幅增加,这个问题是不可忽略的,因此在JS里面实现属性私有不太现实,即使在ES6的class语法也没有实现。但是可以给类添加静态的私有成员变量,这个私有的变量为类的所有实例所共享,如下面的案例: var Worker;(function(){ var id = 1000;Worker = function(){id++;};Worker.prototype....

...11个ES2022(ES13)中惊人的JavaScript新特性

接下来,我们来看看await运算符的增强。在JavaScript中,await运算符用于暂停执行直到Promise对象被解决(即成功或失败)。在ES13之前,仅能在async函数中使用await关键字。然而,现在我们可以在全局范围内使用await运算符,这大大扩展了其适用场景。ES13还引入了静态类字段和静态私有方法,允许在类中声明只对...

JavaScript里面的类是什么意思? 比如下面这个函数里面有类吗?类是...

其实JS并不是一个面对像对象的变成语言,它没有严格的类和继承等。它只是通过模拟来模拟完成了面向对象的类和继承。你给出的这个例子可以叫做类(因为调用的时候是通过NEW关键字来获取的,感觉像是面向对象中的类)。也有不少人叫它构造函数。因为它就是一个函数的形式。所以叫它类或者构造函数都行。

JavaScript如何申明变量和数据类型实例详解

您可以通过JavaScript变量来做算数,使用的是 和+这类运算符: 例子:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> JS变量和数据类型 假设 y=5,计算 x=y+2,并显示结果。 点击这里 function myFunction...

如何注释javascript-jsDoc

在某些情况下则必须使用这个标记 private 指示一个类或函数是私有的。私有类和函数不会出现在HTML文档中,除非运行JSDoc时提供了--private命令行选项 final 指示一个值是常量值。要记住JavaScript无法真正保证一个值是常量 ignore JSDoc忽略有这个标记的函数 ...

ES13的11个超赞的新属性

其次,ES13允许给类定义私有方法和成员变量,通过在属性名前添加 # 符号,实现真正的私有属性保护,避免外界误操作。再者,ES13支持在最外层使用 await,简化了异步操作的代码结构,使全局作用域使用 await 成为可能。在ES13中,类支持定义静态成员和静态私有方法,以及静态代码块,通过 static 关键字实现...

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
如何自制美味的芹菜炒粉丝? 学电脑专业需要懂电脑原理和打字吗 大学里会专门学习五笔打字(电脑专业的)吗?谢谢 肝腹水可以治疗好吗 肝腹水体重增加怎么治疗 我想买一个平板电脑,有这样的要求:价格在1000左右,朝左,可以外接键鼠... 我妈不会在电脑上打字,请问能到那里找些有用的软件来帮帮她?希望能多... 4000能买到既能流畅玩游戏又能练打字的电脑吗在哪买 家庭阳台草莓种植方法下雨怎么办 今日头条抄袭别人文章怎么处罚 电脑升级Win10系统后之前的照片资料还在吗 js中如何让变量公有化 我想问,电脑的系统更新到win10 文件照片还有什么的还在不? 公共变量与私有变理的定义与区别 ​本人电脑刚刚升级成win10,怎么找到当前桌面的壁纸位置 本地变量,私有变量和公共变量的区别? java的类中成员变量前的私有和公共有什么区别 我国信用的主要形式是? javascript:什么叫做私有静态变量呢? javascript关于私有变量的问题。 信用形式有哪些 国家信用的主要工具是(A,*债券 B,银行贷款 C,银行透支 D,发行银行券 地方政府债券是国家信用的主要形式吗? 4399电脑端迷你世界微信登录怎么可以登录一次以后不用登录? 国家信用的基本形式有哪些? 国际信用有哪些具体形式 4399迷你世界怎么打开shop?电脑版 国际信用的主要类型不包括 4399迷你世界电脑版怎么设置密码? 迷你世界4399电脑号和手机版是互通的吗? js怎么在私有方法里调用公有方法 javascript怎么在定义函数内定义公共变量? 如何用 JavaScript 实现真正的私有属性 电脑升级win10之后,桌面上的文件会莫名其妙不见,不管是pdf,word文件或者是图片文件 如何用js实现类似于面向对象语言中类的私有变量 保护变量的安全实现JS私有属性和私有方法 win10这个自动更新的图片在哪存放? 怎样把jquery中的私有方法变为公有的? 体重上升的原因有几种 体重为什么会突然增加 js的私有变量和作用域链的练习题求解答 体重突然上升是什么原因 为什么体重会突然增加 莫名其妙的增加 没有猛吃东西 体重突然上升的原因 最近体重一直上涨怎么回事 为什么体重突然上涨 什么因素会引起体重快速升高? 体重为什么突然上升 什么原因体重突然增加 体重每天都在上升是什么原因