JavaScript学习笔记(二十三) 私有的属性和方法

本文详细阐述了在JavaScript中实现私有属性、特权方法及私有化的局限性,包括如何通过闭包实现私有成员,以及如何避免意外修改私有变量。此外,文章还介绍了对象字面量与私有化的关系,以及原型和私有化之间的关系。最后,讨论了暴露私有方法作为公共方法的方法,以保护API不受意外修改。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

私有的属性和方法(Private Properties and Methods)

JavaScript中没有特殊的语法去声明private,protected和public的属性和方法,不像Java和其它语言。所有的对象成员都是public的:
var myobj = {
    myprop: 1,
    getProp: function () {
        return this.myprop;
    }
};
console.log(myobj.myprop); // `myprop` is publicly accessible
console.log(myobj.getProp()); // getProp() is public too
当你使用构造函数创建对象也是一样,所有的成员都是public的:
function Gadget() {
    this.name = 'iPod';
    this.stretch = function () {
        return 'iPad';
    };
}
var toy = new Gadget();
console.log(toy.name); // `name` is public
console.log(toy.stretch()); // stretch() is public

私有的成员(Private Members)

虽然语言没有特殊的语法去声明私有成员,但你可以使用闭包(closure)去实现这个功能。
你的构造函数创建一个闭包,并且在闭包范围内的任何变量都不会暴露在构造函数之外。然而这些私有变量可以被public方法访问。
让我们看一个例子,name是一个私有成员,在构造函数之外无法被访问到:
function Gadget() {
    // private member
    var name = 'iPod';
    // public function
    this.getName = function () {
        return name;
    };
}
var toy = new Gadget();
// `name` is undefined, it's private
console.log(toy.name); // undefined
// public method has access to `name
console.log(toy.getName()); // "iPod"
正如你看到的,在JavaScript中实现私有是非常简单的。你所需要做的就是将你想私有的数据包裹在一个函数中让它成为函数的局部变量,意味着在这个函数外不可被访问。

特权方法(Privileged methods)

特权方法的概念不包含任何特殊的语法,就是能给能访问私有成员的pubic方法起的名字。
在前面的例子中getName()就是一个特权方法,因为他可以特殊的权利去访问私有的属性name

私有化的局限(Privacy Failures)

当你涉及到私有化时,有一些边界情况
  • 当你从特权方法直接返回一个私有变量并且这个变量恰巧是一个对象或数组时,那么外面的代码就可以修改这个私有的变量因为它们是通过引用传递的
让我们通过第二个例子说明的更具体些,下面这个Gadget实现看起来是没有问题的:
function Gadget() {
    // private member
    var specs = {
        screen_width:  320,
        screen_height: 480,
        color: "white"
    };
    // public function
    this.getSpecs = function () {
        return specs;
    };
}
这里的问题出在 getSpecs()返回的是指向specs对象的引用。这可以让Gadget的使用者修改看起来似乎是隐藏并私有的specs:
var toy = new Gadget(),
specs = toy.getSpecs();
specs.color = "black";
specs.price = "free";
console.dir(toy.getSpecs());
解决这个意想不到的行为的方法就是小心不要传递你想私有化对象或数值的引用。
一种方法去实现私有化这个目的是让getSpecs()返回一个新的对象,这个对象仅仅包含了一些对象使用者感兴趣的数据。这个也被成为最小权限原则(Principle of Least Authority),这表明你永远不应该提供比实际需要的多。
这个例子中,如果Gadget对象使用者关心的是它是否能被装的某个盒子中,它仅仅需要的是尺寸。
那么不需要提供所有的数据,你可以创建一个getDimensions(),它返回一个只包含width和height的新对象。你可能根本不需要提供getDimensions()。
另一种方法就是,当你需要传递所有的数据是,可以创建一个specs对象的副本(copy),通过一个通用的对象克隆函数。
后面我们会通过两个这样的函数,一个叫做extend()做一个浅复制(只复制顶层的参数),另一个叫做extendDeep()做一个深度复制,递归复制所以的属性和它们属性的属性。

对象字面量和私有化(Object Literals and Privacy)

到目前为止我们仅仅看到了使用构造函数去实现私有化的例子,但当你的对象是使用对象字面量创建会出现什么情况呢?它仍然能够拥有私有成员吗?
正如你前面看到的,你所需要的就是用一个函数去包裹私有数据。所以在对象字面量的情况下,你可以使用一个额外的匿名的立即执行函数创建闭包,像下面这样:
var myobj; // this will be the object
(function () {
    // private members
    var name = "my, oh my";
    // implement the public part
    // note -- no `var`
    myobj = {
        // privileged method
        getName: function () {
            return name;
        }
    };
}());
myobj.getName(); // "my, oh my"
相同的主意,但是实现有点不同的实现:
var myobj = (function () {
    // private members
    var name = "my, oh my";
    // implement the public part
    return {
        getName: function () {
            return name;
        }
    };
}());
myobj.getName(); // "my, oh my"
这个例子也是模块模式的本质。

原型和私有化(Prototypes and Privacy)

使用构造方法实现私有化的缺点就是这些私有成员在构造方法每次被调用创建新的对象时都会被重新创建一次。
在构造方法中你添加到this的任何成员都会存在这个问题,为了防止这种重复工作并且节约内存,你可以添加公共的属性和方法给构造方法的prototype属性。
通过这样方式,公共的部分在相同构造方法创建的所有实例间进行共享。
你也可以在所有实例之间共享隐藏的私有成员,为了实现这样的效果你可以使用两种模式的组合:构造方法的私有属性和对象字面量的私有属性。
因为prototype也就是一个对象,它也可以通过对象字面量创建。
举例:
function Gadget() {
    // private member
    var name = 'iPod';
    // public function
    this.getName = function () {
        return name;
    };
}
Gadget.prototype = (function () {
    // private member
    var browser = "Mobile Webkit";
    // public prototype members
    return {
        getBrowser: function () {
            return browser;
        }
    };
}());
var toy = new Gadget();
console.log(toy.getName()); // privileged "own" method
console.log(toy.getBrowser()); // privileged prototype method

暴露一个私有方法作为一个公开方法(Revealing Private Functions As Public Methods)

暴露模式是想将一个私有方法暴露为一个公共方法。这是非常有用的,当一个对象所有的函数对对象的工作方式是很关键的并且你想尽可能的保护它们。
但同时你也想将一些函数提供公共的访问权限,因为这会非常有用。但当你公然地暴露一个方法,你会使它可以被修改,你的公共API用户可能会修改它,即使是无心的。
在ECMAScript 5中你有选项去冻结一个对象,但在之前的版本中是不可以的。
我们来举个例子,首先创建一个私有化模式--对象字面量的私有属性:
var myarray;
(function () {
    var astr = "[object Array]",
        toString = Object.prototype.toString;
    function isArray(a) {
        return toString.call(a) === astr;
    }
    function indexOf(haystack, needle) {
        var i = 0,
            max = haystack.length;
        for (; i < max; i += 1) {
            if (haystack[i] === needle) {
                return i;
            }
        }
        return −1;
    }
myarray = {
        isArray: isArray,
        indexOf: indexOf,
        inArray: indexOf
    };
}());
这里我们拥有两个私有变量和两个私有方法,isArray()和indexOf(),
在立即执行函数的结尾,myarray对象由你想要的暴露的函数组成。
myarray.isArray([1,2]); // true
myarray.isArray({0: 1}); // false
myarray.indexOf(["a", "b", "z"], "z"); // 2
myarray.inArray(["a", "b", "z"], "z"); // 2
现在如果公共indexOf()发生了不在预期之内的事情,私有的indexOf()仍然是安全的并且inArray() 仍然能继续工作。
myarray.indexOf = null;
myarray.inArray(["a", "b", "z"], "z"); // 2
















内容概要:该研究通过在黑龙江省某示范村进行24小时实地测试,比较了燃煤炉具与自动/手动进料生物质炉具的污染物排放特征。结果显示,生物质炉具相比燃煤炉具显著降低了PM2.5、COSO2的排放(自动进料分别降低41.2%、54.3%、40.0%;手动进料降低35.3%、22.1%、20.0%),但NOx排放未降低甚至有所增加。研究还发现,经济性便利性是影响生物质炉具推广的重要因素。该研究不仅提供了实际排放数据支持,还通过Python代码详细复现了排放特征比较、减排效果计算结果可视化,进一步探讨了燃料性质、动态排放特征、碳平衡计算以及政策建议。 适合人群:从事环境科学研究的学者、政府环保部门工作人员、能源政策制定者、关注农村能源转型的社会人士。 使用场景及目标:①评估生物质炉具在农村地区的推广潜力;②为政策制定者提供科学依据,优化补贴政策;③帮助研究人员深入了解生物质炉具的排放特征技术改进方向;④为企业研发更高效的生物质炉具提供参考。 其他说明:该研究通过大量数据分析模拟,揭示了生物质炉具在实际应用中的优点挑战,特别是NOx排放增加的问题。研究还提出了多项具体的技术改进方向政策建议,如优化进料方式、提高热效率、建设本地颗粒厂等,为生物质炉具的广泛推广提供了可行路径。此外,研究还开发了一个智能政策建议生成系统,可以根据不同地区的特征定制化生成政策建议,为农村能源转型提供了有力支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值