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 关键字实现...