使用闭包在原型中使方法成为私有

hgu*_*ser 0 javascript closures prototype-programming

我在我们的项目中使用原型1.4,我曾经以这种方式创建类:

1)方式1

var Person=Class.create();
Person.prototype={
    initialize:function(name,age){
        this._check(name,age);
        this.name=name;
        this.age=age;
    },
    say:function(){
        console.log(this.name+','+this.age);
    }
    _check:function(name,age){
        if(typeof(name)!='string' || typeof(age)!='number')
            throw new Error("Bad format...");
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,在上面的代码中,Person"_check"的方法可以在外部调用,这不是我期望的.

在我以前的帖子中,感谢'TJ Crowder',他告诉我一个解决方案,使该方法完全私密:

2)方式2

var Person=(function(){
    var person_real=Class.create();
    person_real.prototype={
        initialize:function(name,age){
            _check(name,age);
            this.name=name;
            this.age=age;
        },
        say:function(){
            console.log(this.name+','+this.age);
        }
    }

    //some private method
    function _check(name,age){
        //....the check code here
    }
    return person_real;
})();
Run Code Online (Sandbox Code Playgroud)

现在,"_ check"不能暴露在外面.

但我现在感到困惑的是,这种方式会导致性能问题还是最好的实践?

由于我们创建类(蓝图)的原因之一是减少重复代码,可以在任何地方多次重复使用.

现在看看"Manner1":

我们创建一个Class"Person",然后我们将所有实例方法放到Person类的原型对象中.然后每次我们打电话给

var obj=new Person('xx',21);
Run Code Online (Sandbox Code Playgroud)

对象"obj"将拥有Person.prototype中的方法."obj"本身并没有任何代码.

但是在"Manner2"中:每次我们打电话:

var obj=new Person('xx',21);
Run Code Online (Sandbox Code Playgroud)

将创建一个新蓝图,每次也会创建诸如"_check"之类的私有方法.是浪费记忆吗?

注意:也许我错了.但我真的很困惑.任何人都可以给我解释一下吗?

小智 6

你会遇到一些人们使用Javascript的常见问题.

第二个问题首先,因为它更容易回答.通过原型继承,您只描述了两个相关对象之间的差异.如果在对象的原型上创建一个函数然后生成10000个克隆,那么仍然只有一个函数.每个单独的对象都将调用该方法,并且由于"this"在Javascript中的工作方式,这些函数调用中的"this"将指向单个对象,尽管函数存在于单数原型中.

当然,当你有唯一的每项属性,每个对象的唯一值,那么是的,你可以解决性能问题(我犹豫使用术语"实例",因为它与原型继承相比并不意味着与类相比基础继承).由于您不再受益于单个共享功能/数据集,因此您将失去这一优势.了解如何最大限度地降低这些低效率是Javascript(以及任何基于原型继承的语言)中智能,高效编程的关键部分.有很多非显而易见的技巧可以通过最少的重复来共享功能和数据.

第一个问题是Javascript中常见的一类混淆,因为Javascript中函数几乎无所不能.Javascript中的函数单独执行其他语言中许多不同构造的工作.类似地,Javascript中的对象充当了许多其他语言的数据结构的角色.这种集中的责任为表格带来了许多强大的选项,但是使Javascript变成了一种boogieman:它看起来非常简单,而且非常简单.但它真的很简单,就像扑克一样简单.这是一小部分动人的作品,但他们互动的方式会产生一个更加深刻的元游戏,很快就会变得扑朔迷离.

最好理解Javascript应用程序/脚本中的对象/函数/数据/不管是不断发展的系统,因为这样可以更好地捕获原型继承.也许就像一个足球(美国)游戏,如果不知道有多少下降,剩下多少超时,你进入的季度等等,你无法真正捕获游戏的当前状态.你必须移动代码而不是像大多数语言一样看待它,甚至像php这样的动态语言.

在你的第一个例子中,你所做的一切就是使用Class.create来initialize()自动调用函数并自动处理祖父文本构造函数,所以其他方面类似于:

var Person = function(){ this.initialize.call(this, arguments) };
Person.prototype = { ... }
Run Code Online (Sandbox Code Playgroud)

没有祖父母的东西.直接在对象上设置的任何属性都将是公共的(除非您使用ES5 defineProperty来专门控制它),因此添加_到名称的开头不会执行约定之外的任何操作.在javascript中没有私有或受保护成员这样的东西.只有公共,所以如果你在一个对象上定义一个属性,它是公共的.(再次对ES5进行了折扣,它确实为此添加了一些控制,但它并没有真正设计为与其他语言中的私有变量相同,并且不应该以相同的方式使用或依赖它).

我们必须使用范围来创建隐私,而不是私人成员.重要的是要记住Javascript 只有函数范围.如果你想在一个Object中拥有私有成员,那么你需要将整个对象包装在一个更大的范围(函数)中,并有选择地公开你想要的东西.这是一个重要的基本规则,由于某种原因,我很少看到succincly解释.(其他方案存在,例如提供公共接口来注册/添加功能到私有上下文中,但它们都以共享的所有内容结束,可以通过函数构建私有上下文,并且通常比需要的更复杂).

这样想吧.您希望在对象内共享这些成员,因此所有对象成员都需要在同一范围内.但是你不希望它在外面共享,所以它们必须在自己的范围内定义.你不能把它们作为成员放在公共对象上,因为Javascript除了公共变量之外没有任何概念(除了ES5之外,还可以搞砸),你只剩下功能范围作为实现隐私的唯一方法.

幸运的是,这并不痛苦,因为你有很多有效,甚至是顺畅的方法来做到这一点.这里有Javascript作为函数式语言的强大功能.通过从函数(或包含函数/数据/引用的对象)返回函数,您可以轻松地控制返回到世界的内容.由于Javascript具有词法范围,因此那些新释放的公共接口仍然有一个管道回到它们最初创建的私有范围,其中隐藏的兄弟仍然存在.

立即执行的函数函数在此处扮演网守的角色.在运行函数之前,不会创建对象和作用域,并且容器函数的唯一目的通常是提供作用域,因此匿名的立即执行函数符合条件(虽然不需要匿名或立即执行).重要的是要认识到Javascript在构造"对象"或其他方面的灵活性.在大多数语言中,您需要仔细构建您的类结构以及每个事物的成员以及依次开启的内容.在Javascript中,"如果你梦想它,你可以制作它"本质上是游戏的名称.具体来说,我可以使用20个不同的对象/类或其中内置的任何内容创建一个匿名函数,然后ad-hoc cherry从每个中选择一个或两个方法并将它们作为单个公共对象返回,然后是对象/接口即使它在我做之前不到两秒钟return { ...20 functions from 20 different objects... }.

所以在你的第二个例子中,你看到了这种使用的策略.在这种情况下,创建原型,然后在同一范围内创建单独的函数.像往常一样,无论有多少孩子来自原型,原型只会存在一份.并且只存在该函数的一个副本,因为包含函数(范围)的函数只被调用一次.从原型创建的子代将无法调用该函数,但看起来他们会这样做,因为他们通过原型上的函数获得后门访问.如果一个孩子initialize在那个例子中去实现它自己,它将无法再使用_check它,因为它从来没有直接访问过.

而不是把它看作是孩子做的东西,把它看作是父/原型趋向于生活在其上的一组功能,就像一个中央电话操作员,一切都通过,孩子作为瘦客户端.孩子们没有运行这些功能,孩子们通过this指向呼叫者的孩子来ping原型来运行它们.

这就是原型类的东西变得有用的原因.如果你正在实现多级继承,你就会开始遇到漏洞,其中链上的原型错过了某些功能.这是因为虽然原型链中的属性/函数自动显示在子节点上,但构造函数不会同样级联; 构造函数属性与其他任何东西都继承相同,但它们都被命名,constructor所以你只能免费获得直接原型的构造函数.还需要执行构造函数以获得特殊的酱,而原型只是一个属性.构造函数通常拥有设置对象自身私有数据的关键功能,这通常是其他功能工作所必需的.如果您不手动运行构造函数链(或使用类似的类)来设置,那么从原型链继承一系列函数也没有任何好处.

这也是为什么你通常不会在Javascript中看到深度继承树的原因的一部分,并且通常是人们对像GWT这样的东西抱怨那些深度类似Java的继承模式.它对Javascript和原型继承并不是很好.你想要浅而宽,几层深或更少.重要的是要很好地掌握物体流动的位置以及物体的位置.Javascript很好地创建了一个外观,使得它看起来像X和Y功能在一个对象上实现,但当你在后面时你意识到(理想情况下)大多数对象大多是空壳,a.)保持几个位唯一数据和b.)打包指向适当的原型对象及其功能,以利用该数据.如果你正在进行大量的功能和冗余数据的复制,那么你做错了(尽管不要感觉不好,因为它更常见).

这里是浅拷贝和深拷贝之间的区别.jQuery的$ .extend默认为浅(我假设大多数/全部都这样做).浅拷贝基本上只是传递引用而不复制函数.这允许您构建像legos这样的对象并获得更直接的控制以允许合并多个对象的部分.浅色副本应该类似于使用从原型构建的10000个对象,不会对性能或内存造成伤害.这也是一个很好的理由,要非常小心地随意地做一个深层复制,并且要理解浅层和深层之间存在很大的差异(特别是当涉及到DOM时).这也是Javascript库关心一些繁重的地方之一.当有浏览器错误导致浏览器在处理应该做的事情应该是便宜的情况时失败,因为它应该使用引用和有效的垃圾收集.当发生这种情况时,往往需要创造性或恼人的解决方法,以确保您不会泄露副本.