- 浏览: 44851 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
map521:
很详细,讲的很明白了,感谢楼主的无私。
Spring中集成Quartz的简单配置 -
化蝶自在飞:
留记号,正在用,谢谢
ECShop 2.7.2 邮件服务器设置及“Access is denied”的解决方法 -
mefly:
目前看没什么错,我在用你的,谢谢
jQuery Validate 扩展IP校验 -
xplazy:
<div class="quote_title ...
-
xplazy:
<div class="quote_title ...
OOP in JS, Part 1 : Public/Private Variables and Methods
This page shows how to create private variables and methods in classes in Javascript through the rather simple example of a person. Part 2 covers inheritance.
Summary
- private variables are declared with the 'var' keyword inside the object, and can only be accessed by private functions and privileged methods.
- private functions
are declared inline inside the object's constructor (or alternatively may be defined via
var functionName=function(){...}
) and may only be called by privileged methods (including the object's constructor). - privileged methods
are declared with
this.methodName=function(){...}
and may invoked by code external to the object. - public properties
are declared with
this.variableName
and may be read/written from outside the object. - public methods
are defined by
Classname.prototype.methodName = function(){...}
and may be called from outside the object. - prototype properties
are defined by
Classname.prototype.propertyName = someValue
- static properties
are defined by
Classname.propertyName = someValue
Example
In this example, a person's name and race are set at birth and may never be changed. When created, a person starts out at year 1 and a hidden maximum age is determined for that person. The person has a weight which is modified by eating (tripling their weight) or exercising (halfing it). Every time the person eats or exercises, they grow a year older. The person object has a publicly accessible 'clothing' property which anyone can modify, as well as a dirtFactor which can be modified manually (throwing dirt on or scrubbing it off), but which increases every time the person eats or exercises, and is reduced by the use of the shower() method.
Run Gavin's Life <- click to run the example code below (the code block being run appears after the Person constructor).
The Example Code
function Person(n,race){ this.constructor.population++; // ************************************************************************ // PRIVATE VARIABLES AND FUNCTIONS // ONLY PRIVELEGED METHODS MAY VIEW/EDIT/INVOKE // *********************************************************************** var alive=true, age=1; var maxAge=70+Math.round(Math.random()*15)+Math.round(Math.random()*15); function makeOlder(){ return alive = (++age <= maxAge) } var myName=n?n:"John Doe"; var weight=1; // ************************************************************************ // PRIVILEGED METHODS // MAY BE INVOKED PUBLICLY AND MAY ACCESS PRIVATE ITEMS // MAY NOT BE CHANGED; MAY BE REPLACED WITH PUBLIC FLAVORS // ************************************************************************ this.toString=this.getName=function(){ return myName } this.eat=function(){ if (makeOlder()){ this.dirtFactor++; return weight*=3; } else alert(myName+" can't eat, he's dead!"); } this.exercise=function(){ if (makeOlder()){ this.dirtFactor++; return weight/=2; } else alert(myName+" can't exercise, he's dead!"); } this.weigh=function(){ return weight } this.getRace=function(){ return race } this.getAge=function(){ return age } this.muchTimePasses=function(){ age+=50; this.dirtFactor=10; } // ************************************************************************ // PUBLIC PROPERTIES -- ANYONE MAY READ/WRITE // ************************************************************************ this.clothing="nothing/naked"; this.dirtFactor=0; } // ************************************************************************ // PUBLIC METHODS -- ANYONE MAY READ/WRITE // ************************************************************************ Person.prototype.beCool = function(){ this.clothing="khakis and black shirt" } Person.prototype.shower = function(){ this.dirtFactor=2 } Person.prototype.showLegs = function(){ alert(this+" has "+this.legs+" legs") } Person.prototype.amputate = function(){ this.legs-- } // ************************************************************************ // PROTOTYOPE PROERTIES -- ANYONE MAY READ/WRITE (but may be overridden) // ************************************************************************ Person.prototype.legs=2; // ************************************************************************ // STATIC PROPERTIES -- ANYONE MAY READ/WRITE // ************************************************************************ Person.population = 0; // Here is the code that uses the Person class function RunGavinsLife(){ var gk=new Person("Gavin","caucasian"); //New instance of the Person object created. var lk=new Person("Lisa","caucasian"); //New instance of the Person object created. alert("There are now "+Person.population+" people"); gk.showLegs(); lk.showLegs(); //Both share the common 'Person.prototype.legs' variable when looking at 'this.legs' gk.race = "hispanic"; //Sets a public variable, but does not overwrite private 'race' variable. alert(gk+"'s real race is "+gk.getRace()); //Returns 'caucasian' from private 'race' variable set at create time. gk.eat(); gk.eat(); gk.eat(); //weight is 3...then 9...then 27 alert(gk+" weighs "+gk.weigh()+" pounds and has a dirt factor of "+gk.dirtFactor); gk.exercise(); //weight is now 13.5 gk.beCool(); //clothing has been update to current fashionable levels gk.clothing="Pimp Outfit"; //clothing is a public variable that can be updated to any funky value gk.shower(); alert("Existing shower technology has gotten "+gk+" to a dirt factor of "+gk.dirtFactor); gk.muchTimePasses(); //50 Years Pass Person.prototype.shower=function(){ //Shower technology improves for everyone this.dirtFactor=0; } gk.beCool=function(){ //Gavin alone gets new fashion ideas this.clothing="tinfoil"; }; gk.beCool(); gk.shower(); alert("Fashionable "+gk+" at " +gk.getAge()+" years old is now wearing " +gk.clothing+" with dirt factor " +gk.dirtFactor); gk.amputate(); //Uses the prototype property and makes a public property gk.showLegs(); lk.showLegs(); //Lisa still has the prototype property gk.muchTimePasses(); //50 Years Pass...Gavin is now over 100 years old. gk.eat(); //Complains about extreme age, death, and inability to eat. }
Notes
maxAge
is a private variable with no privileged accessor method; as such, there is no way to publicly get or set it.race
is a private variable defined only as an argument to the contructor. Variables passed into the constructor are available to the object as private variables.- The 'tinfoil'
beCool()
fashion method was applied only to thegk
object, not the entirePerson
class. Other people created and set tobeCool()
would still use the original 'khakis and black shirt' clothing that Gavin eschewed later in life. - Note the implicit call to the
gk.toString()
method when using string concatenation. It is this which allows the codealert(gk+' is so cool.')
to put the word 'Gavin' in there, and is equivalent toalert(gk.toString()+' is so cool.')
. Every object of every type in JS has a.toString()
method, but you can override it with your own. - You cannot (to my knowledge) assign public methods of a class inside the main object constructor...you must use the
prototype
property externally, as above with thebeCool()
andshower()
methods. - As I attempted to show with the
Person.prototype.legs
property and theamputate()
function, prototype properties are shared by all object instances. Asking forlk.legs
yields '2' by looking at the single prototype property. However, attempting to change this value using eithergk.legs=1
or (in the Person object)this.legs=1
ends up making a new public property of the object specific to that instance. (This is why callinggk.amputate()
only removed a leg from Gavin, but not Lisa.) To modify a prototype property, you must usePerson.prototype.legs=1
or something likethis.constructor.prototype.legs=1
. (I say 'something like' because I discovered thatthis.constructor
is not available inside private functions of the object, sincethis
refers to the window object in that scope.) - Wherever an anonymous function is declared inline with
foo = function(p1,p2){ some code }
thenew Function()
constructor is NOT equivalent, e.g.
foo = new Function('p1','p2','code');
since the latter runs in the global scope--instead of inheriting the scope of the constructor function--thus preventing it from accessing the private variables. - As noted above in the code comments, the act of setting
gk.race
to some value did NOT overwrite the privaterace
variable. Although it would be a dumb idea, you can have both private and public variables with the same name. For example, theyell()
method in the following class will yield different values forfoo
andthis.foo
:function StupidClass(){ var foo = "internal"; this.foo = "external"; this.yell=function(){ alert("Internal foo is "+foo+"\nExternal foo is "+this.foo) } }
- Private
functions and privileged methods, like private variables and public
properties, are instantiated with each new object created. So each time
new Person()
is called, new copies ofmakeOlder()
,toString()
,getName()
,eat()
,exercise()
,weigh()
,getRace()
,getAge()
, andmuchTimePasses()
are created. For every Person, each time. Contrast this with public methods (only one copy ofbeCool()
andshower()
exist no matter how many Person objects are created) and you can see that for memory/performance reasons it can be preferable to give up some degree of object protection and instead use only public methods.
Note that doing so requires making private variables public (since without privileged accessor methods there would be no way to use them) so the public methods can get at them...and which also allows external code to see/destroy these variables. The memory/performance optimization of using only public properties and methods has consequences which may make your code less robust.
For example, in the aboveage
andmaxAge
are private variables;age
can only be accessed externally throughgetAge()
(it cannot be set) andmaxAge
cannot be read or set externally. Changing those to be public properties would allow any code to do something likegk.maxAge=1; gk.age=200;
which not only does it not make sense (you shouldn't be able to manipulate someone's age or lifespan directly), but by setting those values directly thealive
variable wouldn't properly be updated, leaving your Person object in a broken state.
<script type="text/javascript"><!----></script>
OOP in JS, Part 2 : Inheritance
In Part 1 we saw how to create classes in JS, including private, privileged, and public properties and methods. This section discusses inheritance in Javascript.
Summary
- You cause a class to inherit using
ChildClassName.prototype = new ParentClass();
. - You need to remember to reset the
constructor
property for the class usingChildClassName.prototype.constructor=ChildClassName
. - You can call ancestor class methods which your child class has overridden using the
Function.call()
method. - Javascript does not support protected methods.
Example
To jump right into it, following is a sample showing inheritance between two classes:
function Mammal(name){ this.name=name; this.offspring=[]; } Mammal.prototype.haveABaby=function(){ var newBaby=new Mammal("Baby "+this.name); this.offspring.push(newBaby); return newBaby; } Mammal.prototype.toString=function(){ return '[Mammal "'+this.name+'"]'; } Cat.prototype = new Mammal(); // Here's where the inheritance occurs Cat.prototype.constructor=Cat; // Otherwise instances of Cat would have a constructor of Mammal function Cat(name){ this.name=name; } Cat.prototype.toString=function(){ return '[Cat "'+this.name+'"]'; } var someAnimal = new Mammal('Mr. Biggles'); var myPet = new Cat('Felix'); alert('someAnimal is '+someAnimal); // results in 'someAnimal is [Mammal "Mr. Biggles"]' alert('myPet is '+myPet); // results in 'myPet is [Cat "Felix"]' myPet.haveABaby(); // calls a method inherited from Mammal alert(myPet.offspring.length); // shows that the cat has one baby now alert(myPet.offspring[0]); // results in '[Mammal "Baby Felix"]'
Using the .constructor
property
Look at the last line in the above example. The baby of a Cat should be a Cat, right? While the haveABaby()
method worked, that method specifically asks to create a new Mammal
. While we could
make a new haveABaby()
method for the Cat
subclass like this.offspring.push(new Cat("Baby "+this.name))
, it would be better to have the ancestor class make an object of the correct type.
Every object instance in JS has a property named constructor
that points to its parent class. For example, someAnimal.constructor==Mammmal
is true. Armed with this knowledge, we can remake the haveABaby()
method like this:
Mammal.prototype.haveABaby=function(){ var newBaby=new this.constructor("Baby "+this.name); this.offspring.push(newBaby); return newBaby; } ... myPet.haveABaby(); // Same as before: calls the method inherited from Mammal alert(myPet.offspring[0]); // Now results in '[Cat "Baby Felix"]'
Calling 'super' methods
Let's extend the example now so that when baby kittens are created,
they 'mew' right after being born. To do this, we want to write our own
custom Cat.prototype.haveABaby()
method, which is able to call the original Mammal.prototype.haveABaby()
method:
Cat.prototype.haveABaby=function(){ Mammal.prototype.haveABaby.call(this); alert("mew!"); }
The above may look a little bit bizarre. Javascript does not have
any sort of 'super' property, which would point to its parent class.
Instead, you use the call()
method of a Function
object, which allows you to run a function using a different object as
context for it. If you needed to pass parameters to this function, they
would go after the 'this'. For more information on the Function.call()
method, see the MSDN docs for call()
.
Making your own 'super' property
Rather than having to know that Cat
inherits from Mammal
, and having to type in Mammal.prototype
each time you wanted to call an ancestor method, wouldn't it be nice to
have your own property of the cat pointing to its ancestor class? Those
familiar with other OOP languages may be tempted to call this property
'super', but JS reserves this word
for future use. The word 'parent', while used in some DOM items, is free for the JS language itself, so let's call it parent
in this example:
Cat.prototype = new Mammal(); Cat.prototype.constructor=Cat; Cat.prototype.parent = Mammal.prototype; ... Cat.prototype.haveABaby=function(){ var theKitten = this.parent.haveABaby.call(this); alert("mew!"); return theKitten; }
Spoofing pure virtual classes
Some OOP languages have the concept of a pure virtual class...one
which cannot be instantiated itself, but only inherited from. For
example, you might have a LivingThing
class which Mammal
inherited from, but you didn't want someone to be able to make a LivingThing
without specifying what type of thing it was. You can do this in JS by
making the virtual class an object instead of a function.
The following example shows how this could be used to simulate a pure virtual ancestor:
LivingThing = { beBorn : function(){ this.alive=true; } } ... Mammal.prototype = LivingThing; Mammal.prototype.parent = LivingThing; //Note: not 'LivingThing.prototype' Mammal.prototype.haveABaby=function(){ this.parent.beBorn.call(this); var newBaby=new this.constructor("Baby "+this.name); this.offspring.push(newBaby); return newBaby; }
With the above, doing something like var spirit = new LivingThing()
would
result in an error, since LivingThing
is not a function, and hence can't be used as a constructor.
Convenient Inheritance
Rather than writing 3 lines every time you want to inherit one class from another, it's convenient to extend the Function object to do it for you:
Function.prototype.inheritsFrom = function( parentClassOrObject ){ if ( parentClassOrObject.constructor == Function ) { //Normal Inheritance this.prototype = new parentClassOrObject; this.prototype.constructor = this; this.prototype.parent = parentClassOrObject.prototype; } else { //Pure Virtual Inheritance this.prototype = parentClassOrObject; this.prototype.constructor = this; this.prototype.parent = parentClassOrObject; } return this; } // // LivingThing = { beBorn : function(){ this.alive = true; } } // // function Mammal(name){ this.name=name; this.offspring=[]; } Mammal.inheritsFrom( LivingThing ); Mammal.prototype.haveABaby=function(){ this.parent.beBorn.call(this); var newBaby = new this.constructor( "Baby " + this.name ); this.offspring.push(newBaby); return newBaby; } // // function Cat( name ){ this.name=name; } Cat.inheritsFrom( Mammal ); Cat.prototype.haveABaby=function(){ var theKitten = this.parent.haveABaby.call(this); alert("mew!"); return theKitten; } Cat.prototype.toString=function(){ return '[Cat "'+this.name+'"]'; } // // var felix = new Cat( "Felix" ); var kitten = felix.haveABaby( ); // mew! alert( kitten ); // [Cat "Baby Felix"]
Just make sure you call this method immediately after your constructor, before you extend the prototype for the object.
Protected methods?
Some OOP languages have the concept of 'protected' methods—methods that exist in a parent or ancestor class that can only be called by descendants of the object (on each other), but not by external objects. These are not supported in JS. If you need such, you will have to write your own framework, ensuring that each class has a 'parent' or some such property, and walking up the tree to find ancestors and checking whether or not the calling object is the same type. Doable, but not enjoyable.
相关推荐
Find out how the module design pattern is used in OOP in JavaScript Design and augment modules using both tight augmentation and loose augmentation Extend the capabilities of modules by creating sub-...
同时,Ruby支持模块(Module),可以用来实现类似于JavaScript原型的混入(Mix-in)行为: ```ruby module Sayable def say_hello puts 'Hello, ' + self.name end end class Person attr_accessor :name end ...
本书《Object-Oriented Programming in Javascript》是为了解决如何在JavaScript中正确应用面向对象编程(OOP)问题而撰写的,作者是曾任职于雅虎的前端工程师范 Nicholas C. Zakas。全书将涵盖JavaScript中面向对象...
一个基于OOP风格的基于Express.js的Nodejs框架声明! 什么是mkbug.js 一个基于Express.js的OOP风格的Restful Api框架,使Node.js的开发既轻松又漂亮。 Mkbug.js VS Egg.js VS Think.js 项目 Mkbug.js Egg.js ...
### 面向对象编程(OOP)在JavaScript中的原则 #### 学习目标与概述 本书《面向对象编程在JavaScript中的原则》旨在帮助读者深入理解并掌握如何在JavaScript环境中运用面向对象编程(OOP)的概念和技术。对于那些...
JavaScript,作为一种广泛应用于Web开发的动态脚本语言,其面向对象编程(Object-Oriented Programming...在"js-oop"的压缩包中,可能包含了一些示例代码或教程,可以帮助你进一步学习和实践JavaScript的面向对象编程。
"Battle City Game in JavaScript with Source Code.zip"是一个利用JavaScript编写的“坦克大战”游戏项目,其中包含了完整的源代码,为开发者提供了一个学习和实践JavaScript游戏开发的宝贵资源。 首先,我们要...
6. **面向对象编程**:在高级的JavaScript开发中,面向对象编程(OOP)是常见的设计模式。在这个游戏里,可能有Mouse类表示地鼠,Game类管理整个游戏流程,这些类封装了各自的功能,提高了代码的可维护性和复用性。 ...
总的来说,"Simple Tower Block Game in JavaScript"利用了JavaScript的灵活性和交互性,展示了如何在浏览器环境中构建一款简单的游戏。通过学习和分析这款游戏的源代码,开发者不仅可以掌握JavaScript的基本语法,...
Learn to build scalable server application in JavaScript using Node.js Generate instances in three programming languages: Python, JavaScript, and C# Work with a combination of access modifiers, ...
将函数命名为askUser() 这些是用户可以选择的命令• log in• sign up• exit• search• log out• follow除了可以对每个选项执行if语句外,您还可以进行切换,并且组织得更好。 阅读: : ) 如果用户选择程序...
**面向对象JavaScript(OOP in JavaScript):** 面向对象编程是一种软件设计模式,它将数据和操作数据的方法封装在对象中。JavaScript虽然是一种基于原型的语言,但同样支持类的概念,可以创建构造函数和实例,...
JavaScript’s object programming (not inheritance) is what separates it from classical OOP languages like C++ and Java. Most important, basing inheritance on JavaScript’s prototypal chain is possible...
Java是一种广泛使用的面向对象编程语言(JavaOOP),其特性包括封装、继承、多态和抽象。JavaOOP允许开发者创建可复用的代码模块,提高效率。面试时,面试官可能会询问关于类、对象、接口、异常处理和集合框架等方面...