static以及继续,js面向对象基础

JavaScript落到实处类的private、protected、public、static以及继续

2015/08/24 · JavaScript
· class,
private,
protected,
继承

初稿出处: Yorhom’s Game
Box   

先后1. 创办不难对象并设置其特性的七个例子

复制代码 代码如下:

这一个连串重大探索的是javascript面向对象的编程,前面已经主要介绍了弹指间js的屡次三番,下边想大概的说一下js如何促成封装的特点。

基础知识

 

//使用原型继承,中间使用临时对象作为Child的原型属性,临时对象的原型属性再指向父类的原型,
static以及继续,js面向对象基础。//幸免所有子类和父类原型属性都指向通一个对象.
//那样当修改子类的原型属性,就不会影响其余子类和父类
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.base = Parent.prototype;
}

 

JavaScript中的类

JavaScript实际上是一种弱类型语言,与C++和Java等语言差距。因而,在JavaScript中,没有强调类(class)这一定义,但其实采取中,类如故很关键的,比如写一款游戏,如果我们不停地调用函数来形成创制角色,移动角色的话,这会是何等的吗?可能会现出非常多的重复代码,由此大家需求一个类来归并这一个代码。所谓的类,就是把程序中的代码分类,比如说游戏中的关于角色的代码算作一类,游戏背景正是一类,游戏特效又是一类。那样一来,我们对类进行操作,就不会使代码显得很混乱,冗杂。即使Js是弱类型语言,可是也提供了类这一概率。
定义Js中的类,实际上用的是function,总所周知,那几个语法其实是用来定义函数的。不用于定义函数的是,我们可以在function中通过this.xxx的章程来定义属性和办法。比如说:

JavaScript

function People () { this.name = “Yorhom”; this.getName = function () {
return this.name }; }

1
2
3
4
5
6
7
function People () {
    this.name = "Yorhom";
 
    this.getName = function () {
        return this.name
    };
}

应用的时候利用new

JavaScript

var yorhom = new People(); // “Yorhom” alert(yorhom.getName());

1
2
3
var yorhom = new People();
// "Yorhom"
alert(yorhom.getName());

可以看到,那样就足以运用到大家定义的类和类中的方法了。
莫不你会问this.xxx只得定义公有属性和方式,那私有属性和方法如何是好呢?这些可以用到js闭包的文化来化解:

JavaScript

function People () { this.name = “Yorhom”; var age = 16; this.getName =
function () { return this.name }; this.getAge = function () { return
age; }; } var yorhom = new People(); // undefined alert(yorhom.age); //
16 alert(yorhom.getAge());

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function People () {
    this.name = "Yorhom";
 
    var age = 16;
 
    this.getName = function () {
        return this.name
    };
 
    this.getAge = function () {
        return age;
    };
}
 
var yorhom = new People();
// undefined
alert(yorhom.age);
// 16
alert(yorhom.getAge());

 

可以见见,那里的age就是一个私有属性了。

亚洲必赢官网 1亚洲必赢官网 2代码

function Parent(name)
{
this.aa = 123;
this.getName = function() {return name;}; //使用闭包模拟私有成员
this.setName = function(value){name=value;};
}
Parent.prototype.print = function(){alert(“print!”);};
Parent.prototype.hello = function()
{
alert(this.getName() + “Parent”)
};

俺们领悟面向对象的言语完结封装是把成员变量和办法用一个类包围起来,对类中变量的拜访只可以通过已定义的接口。封装可以算是一种新闻隐藏技能,java通过private修饰符将成员变量私有化,并提供对外修改的接口,以决定类中的成员变量的访问和修改。当然在这个办法中得以对数据举行一些加工。

JavaScript中的prototype

上面的代码美中相差的地方就是,即便一个类有千千万万主意,同时用到那几个类的地方又有诸多(也就是new出来的对象有好多),那么用地方的代码就会冒出内存占用过剩的标题。难点的根本原因在于,每回实例化一个目的,那一个类就会执行协会器里的代码(以People类为例就是function
People ()
{…}执行的代码),由此每当那么些类被实例化的时候,那一个方法和特性就会被拷贝到实例化出来的目的中。那样一来,就会促成“吃”内存的气象。
于是js中的prototype就出生了。prototype的出力常常是给一个类添加一多元常量或者措施。
每当一个类被实例化之后,实例化出来的对象会自动获取类的prototype中定义的点子和总体性。只但是那里的拿走类似于C++里面的引用,不会在内存里对那几个办法和特性举行复制,而是指向那么些点子和属性。示例:

JavaScript

function People () { this.name = “Yorhom”; } People.prototype.getName =
function () { return this.name; }; var yorhom = new People(); //
“Yorhom” alert(yorhom.getName());

1
2
3
4
5
6
7
8
9
10
11
function People () {
    this.name = "Yorhom";
}
 
People.prototype.getName = function () {
    return this.name;
};
 
var yorhom = new People();
// "Yorhom"
alert(yorhom.getName());

 

那种方法固然可以省去内存,但是,美中不足的是,不可能定义私有属性。

 1 //创设一个新目的并将其存放在obj里
 2 var obj = new Object();
 3 //将该对象的片段性质设置成不一样的值
 4 obj.val = 5;
 5 obj.click = function(){
 6 alert( “hello” );
 7 };
 8 //下边是一样的代码,使用了{…}式缩写,
 9 //和概念对象属性的”名称-值”对
10 var obj = {
11 //用名称-值对安装对象属性
12 val: 5,
13 click: function(){
14 alert( “hello” );
15 }
16 };

function Child(name,age)
{
Parent.apply(this, arguments);//调用父类构造函数来持续父类定义的质量
this.age = age;
}
extend(Child,Parent); //继承Parent

 

类的继承

Javascript没有提供后续的函数,所以只有自己写了。那里借用lufylegend.js中的继承方法向我们展现什么兑现再而三:

JavaScript

function base (d, b, a) { var p = null, o = d.constructor.prototype, h =
{}; for (p in o) { h[p] = 1; } for (p in b.prototype) { if (!h[p]) {
o[p] = b.prototype[p]; } } b.apply(d, a); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function base (d, b, a) {
    var p = null, o = d.constructor.prototype, h = {};
 
    for (p in o) {
        h[p] = 1;
    }
    for (p in b.prototype) {
        if (!h[p]) {
            o[p] = b.prototype[p];
        }
    }
 
    b.apply(d, a);
}

那里的base就是后续函数了。继承函数的原理莫过于复制类的艺术和品质。因而,只要形成这一点,就可以已毕类的继续了。可以在上面的代码中看见,大家通过遍历prototype来得到原型链中定义的主意和总体性。通过apply调用父类的构造器举办构造器中属性和艺术的复制。使用示例:

JavaScript

function People () { this.name = “Yorhom”; } People.prototype.getName =
function () { return this.name; }; function Student () { base(this,
People, []); } var yorhom = new Student(); // “Yorhom”
alert(yorhom.getName());

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function People () {
    this.name = "Yorhom";
}
 
People.prototype.getName = function () {
    return this.name;
};
 
function Student () {
    base(this, People, []);
}
 
var yorhom = new Student();
// "Yorhom"
alert(yorhom.getName());

 

 实际上对象就这么回事了。但是,事情变得费劲的地点,在于新指标(尤其是那一个继承其余对象属性的目的)的创办。

Child.prototype.hello = function() //重写父类hello方法
{
alert(this.getName() + “Child”);

在前边文章已经介绍了js的特性难题,那么在js中怎么样落实对类的品质隐藏呢?那里封装有会有何样蔽端。

静态属性和方法的定义

静态属性和章程以及静态类在js中的定义相当不难,先来看静态类:

JavaScript

var StaticClass = {};

1
var StaticClass = {};

这般写不是在概念一个Object呢?是的,不错,但是js中的静态类也是足以那样定义的。即使要添加静态类中的方法和属性,就可以这么写:

JavaScript

var StaticClass = { id : 5, sayHello : function () { alert(“Hello”); }
};

1
2
3
4
5
6
var StaticClass = {
    id : 5,
    sayHello : function () {
        alert("Hello");
    }
};

假诺是要向类中添加静态属性或者措施,能够使用那种写法:

JavaScript

function People () { this.name = “Yorhom”; } People.prototype.getName =
function () { return this.name; }; People.TYPE = “people”;
People.sayHello = function () { alert(“Hello”); };

1
2
3
4
5
6
7
8
9
10
11
12
function People () {
    this.name = "Yorhom";
}
 
People.prototype.getName = function () {
    return this.name;
};
 
People.TYPE = "people";
People.sayHello = function () {
    alert("Hello");
};

 

 

Parent.prototype.hello.apply(this,arguments); //调用父类同名方法
};
//子类方法
Child.prototype.doSomething = function(){ alert(this.age + “Child
doSomething”); };

 

贯彻一个成效丰裕的类

咱俩在上文中涉及了,节省里存和定义私有属性二者不可能兼得,是呀,和“鱼和熊掌不可兼得”是一个道理,在常常的选取进度中,大家须要对那两项举办精选。然而现在那些年代,哪有不行兼得的呢?鱼和熊掌无法而且吃?当然卓殊……因为吃熊掌是违纪的(有待考证)?不过至少鸡和鱼是可以同时吃的呢。
出于js没有兑现个人属性的概念,所以这实际是一个并未头绪的工作,因为在专业的做法中,大家除了闭包可以阻止外部访问,没有其余艺术了。所以那边大家要用点歪门邪道的章程了。

不像大部分其他面向对象的言语,JavaScript实际上并不曾类的定义。在大部分别样的面向对象语言中,你可以初步化一个特定的类的实例,可是在JavaScript中的处境那是那样。在JavaScript中,对象可以创设新的靶子,对象可以继承自别的对象。整个概念被称作”prototypal
inheritance”(原型标本继承) 

var p1 = new Child(“xhan”,22);

上面看一下跌到实处封装的代码:

JavaScript Set/Get访问器

怎么着是set/get访问器呢?借使你熟识python,那么您可以知晓为@property@xxx.setter,然则简陋的js里也有?当然有,只不过是ES5的正规化,可以使用那种写法:

JavaScript

Object.defineProperty(this, “name”, { get : funtion () { return name; },
set : function (v) { name = v; } });

1
2
3
4
5
6
7
8
9
Object.defineProperty(this, "name", {
    get : funtion () {
        return name;
    },
 
    set : function (v) {
        name = v;
    }
});

 

实际有怎么样用啊?大概就是this.name属性在被获取的时候调用get访问器,在被更改值的时候调用set
您可以从上面的代码了然大概的写法,但是若是您想追究,可以参考那篇小说:

注意上述的那种用法会有包容性难点,浏览器协理意况如下:

PC端

Firefox Google Chrome Internet Explorer Opera Safari
4.0 5 9 11.6 5.1

移动端

Firefox Mobile Android IE Mobile Opera Mobile Safari Mobile
4.0 Yes 9 11.5 Yes

来自:
https://developer.mozilla.org/…/defineProperty\#Browser\_compatibility

 

var p2 = new Child(“xxx”,33);

 

哪些“歪门邪道”地做到禁止访问私有和有限支持属性?

那是个相比较头疼的难点,正如本节开篇所说,大家在例行开发下,只可以通过闭包来阻止某变量的拜会。不过假如你利用了prototype,那么闭包那条路就走不通了。在那种状态下,大家的Object.defineProperty就上场了。大家明白,通过这些函数可以设定获取属性时再次来到的值,也得以设定更改属性时设置的值。有了这些函数,大家可以随时跟踪到某个属性是还是不是在被获取,或者是否在被改变。大家还索要一个开关,大家在类内部的艺术调用时,把那些开关打开,声明是在其中运转,方法调用为止后将开关关闭,表明回到表面运行情状。有了那五个状态,大家就足以跟踪privateprotected质量和章程了,一旦他们在开关关闭的时候被选择,就告一段落这些特性或艺术的收获或安装。
于是,大难点就快解决了。

亚洲必赢官网 3亚洲必赢官网 4代码

p1.hello();
p2.hello();

复制代码

开源库件jpp.js

秉着那几个歪门邪道的沉思,我把这几个作用封装到jpp.js那一个库件中,库件的github地址如下:

理所当然这几个库件不限于创建一个类,仍能达成函数的重载等。近年来库件还处在开发阶段,欢迎各位提交指出。

 1 //一个简练的函数,接受一个参数name,
 2 //并将其保存于当下上下文中
 3 function User( name ) {
 4 this.name = name;
 5 }
 6 //用指定的name创制上述函数的新实例
 7 var me = new User( “My Name” );
 8 //我们可以见见name已经被改为目的自我的性质
 9 alert( me.name == “My Name” );
10 //而且它的确是User对象的一个新实例
11 alert( me.constructor == User );
12 //那么,既然User()只是一个函数,
13 //当大家如此处理它的时候,暴发了如何?
14 User( “Test” );
15 //因为this上下文没有被设置,它缺省地指向全局的window对象,
16 //那代表window.name将相当于我们提需要它的万分name
17 alert( window.name == “Test” );

p1.doSomething(); //子类方法
p1.print(); //父类方法

var Person = (function(){

选择jpp.js创设一个类

JavaScript

var People = jpp.class({ extends : null, private : { id : null, hobby :
null }, protected : { money : null, phoneNumber : null }, public : {
firstName : null, lastName : null, age : null, birthday : null,
occupation : null, constructor : function (name, id) { if (name) { var
nameArray = name.split(” “); this.firstName = nameArray[0];
this.lastName = nameArray[1]; } if (id) { this.id = id; } },
setBirthday : function (date) { if (date) { this.birthday = date; } },
getBirthday : function () { return this.birthday; }, askForId : function
() { return this.id; }, findHobby : function () { return this.hobby; }
}, static : { OCCUPATION_PROGRAMMER : “programmer”, OCCUPATION_ARTIST
: “artist”, OCCUPATION_MUSICIAN : “musician”, OCCUPATION_STUDENT :
“student” } }); var peter = new People(“Peter Wong”, 543232123565);
peter.occupation = People.OCCUPATION_PROGRAMMER;
peter.setBirthday(“19980727”); // result: Peter alert(peter.firstName);
// result: 19990727 alert(peter.getBirthday()); // result: 51092028
alert(peter.askForId()); // result: null alert(peter.findHobby()); //
result: programmer alert(peter.occupation); // error alert(peter.id);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
var People = jpp.class({
    extends : null,
    private : {
        id : null,
        hobby : null
    },
    protected : {
        money : null,
        phoneNumber : null
    },
    public : {
        firstName : null,
        lastName : null,
        age : null,
        birthday : null,
        occupation : null,
 
        constructor : function (name, id) {
            if (name) {
                var nameArray = name.split(" ");
 
                this.firstName = nameArray[0];
                this.lastName = nameArray[1];
            }
 
            if (id) {
                this.id = id;
            }
        },
 
        setBirthday : function (date) {
            if (date) {
                this.birthday = date;
            }
        },
 
        getBirthday : function () {
            return this.birthday;
        },
 
        askForId : function () {
            return this.id;
        },
 
        findHobby : function () {
            return this.hobby;
        }
    },
    static : {
        OCCUPATION_PROGRAMMER : "programmer",
        OCCUPATION_ARTIST : "artist",
        OCCUPATION_MUSICIAN : "musician",
        OCCUPATION_STUDENT : "student"
    }
});
 
var peter = new People("Peter Wong", 543232123565);
peter.occupation = People.OCCUPATION_PROGRAMMER;
 
peter.setBirthday("19980727");
 
// result: Peter
alert(peter.firstName);
// result: 19990727
alert(peter.getBirthday());
// result: 51092028
alert(peter.askForId());
// result: null
alert(peter.findHobby());
// result: programmer
alert(peter.occupation);
// error
alert(peter.id);

 

对地点的代码进行剖析:
使用jpp.class函数创制一个类,函数的参数是一个Object,这么些Object可增加的性质如下:

  • extends亚洲必赢官网, 继承时的父类
  • private 装载私有属性,里面定义的分子外部不可动用且不可能再三再四给子类
  • protected
    装载爱慕属性,里面定义的成员外部不可动用但可以一而再给子类
  • public 装载公有属性
  • static 装载静态方法和性质

在创造类的经过中,在public中添加constructor方法早先化构造器,this.super可访问父类构造器。

运行代码,可以见见浏览器正常运作前5个alert,而最后一个周转的时候浏览器报错:

亚洲必赢官网 5

实际的落到实处进程有点复杂,但是原理在上文已经详细讲述了。代码可以在github里参看,欢迎各位研讨。

2 赞 6 收藏
评论

亚洲必赢官网 6

alert(p1 instanceof Child); //true
alert(p1 instanceof Parent);//true

    //定义私有方法,相当于private方法,只好在里面访问

国有方法

您或许感兴趣的篇章:

  • javascript asp教程第三课 new String()
    构造器
  • JavaScript中利用构造器创设对象无需new的意况讲明
  • 详解JavaScript中的构造器Constructor形式
  • JavaScript设计形式之工厂情势和构造器方式
  • javascript设计形式Constructor(构造器)格局
  • JavaScript
    高级篇之闭包、模拟类,继承(五)
  • JavaScript
    模拟类机制及个人变量的办法及思路
  • 在JavaScript中模拟类(class)及类的接续关系
  • JavaScript中拔取构造器函数模拟类的措施

    function hello(){

国有方法可以完全地被对象的前后文中的结尾使用者访问。为了完结那么些对于特定目标的具有实例都可用的国有措施,你需求上学一个名为”prototype”的特性。prototype
不难地包蕴一个对象,为一个父对象的持有新副本充当对基类的引用。本质上,prototype
的其余性质对该对象的所每一个实例都是可用的。创制/引用的进程给了大家一个廉价版的继承 

        alert(‘hello world!’);

 

    }

亚洲必赢官网 7亚洲必赢官网 8代码

    return function(){

 1 //创造一个新的User的构造器
 2 function User( name, age ){
 3 this.name = name;
 4 this.age = age;
 5 }
 6 //为prototype对象添加一个新措施
 7 User.prototype.getName = function(){
 8 return this.name;
 9 };
10 //为prototype对象添加另一个方法
11 //注意此方法的上下文将是被实例化的靶子
12 User.prototype.getAge = function(){
13 return this.age;
14 };
15 //实例化一个新的User对象
16 var user = new User( “Bob”, 44 );
17 //大家得以看看八个艺术被增大到了对象上,有着不错的上下文
18 alert( user.getName() == “Bob” );
19 alert( user.getAge() == 44 );

        //定义私有属性,相当于private属性,只好在国有方法内部访问

民用方法
 

        var name,age;

村办方法和变量只可以被其余的私有方法、私有变量的特权方法访问。那是一种概念只好在内象内部访问的代码的主意。

        //定义公有方法,相当于public方法,能够在类的实例中艺术

 

        this.getName = function(){

亚洲必赢官网 9亚洲必赢官网 10代码

            return name;

 1 //一个意味体育场所的目的构造器
 2 function Classroom( students, teacher ) {
 3 //用来展示体育场地中的所有学生的民用方法
 4 function disp() {
 5 alert( this.names.join(“, “) );
 6 }
 7 //课程的数量存储在国有的对象属性里
 8 this.students = students;
 9 this.teacher = teacher;
10 //调用个人方法展现错误
11 disp();
12 }
13 //创立一新的体育场地对象
14 var class = new Classroom( [ “John”, “Bob” ], “Mr. Smith” );
15 //失败,因为disp不是该目标的公有方法
16 class.disp();

        };

 

        this.setName = function(newName){

 固然很粗略,私有方法却是极度主要的,它可以在保持您的代码免于争辨同时同意对你的用户可见和可用的施以更强有力的支配。接下来,我们来切磋特权方法。它是你的目标中能够使用的村办方法和共有方法的联合。

            name = newName;

 

        };

亚洲必赢官网 11亚洲必赢官网 12代码

        this.getAge = function(){

 1 //创造一个新的User对象构造器
 2 function User( name, age ) {
 3 //统计用户的落地年份
 4 var year = (new Date()).getFullYear() – age;
 5 //创设一个新特权方法,对变量year有访问权,
 6 //但又是公私可访问的
 7 this.getYearBorn = function(){
 8 return year;
 9 };
10 }
11 //创立一个User对象的新实例
12 var user = new User( “Bob”, 44 );
13 //验证再次来到的出生年份是不是正确
14 alert( user.getYearBorn() == 1962 );
15 //并注意我们无法访问对象的私家属性year
16 alert( user.year == null );

            return age;

真相上,特权方法是动态变化的格局,因为它们是在运行时而不是代码初次编译时添加给对象的。那种技能在总括量上要比绑定一个简约的办法到对象的prototype
上显示昂贵,但与此同时也的强硬和灵活得多。 

        }

 

        this.setAge = function(newAge){

亚洲必赢官网 13亚洲必赢官网 14代码

            age = newAge;

 1 <script>
 2 //创设一个新的承受properties对象的靶子
 3 function User( properties ) {
 4 //遍历对象属性,确保它功效域正确(如前所述)
 5 for ( var i in properties ) { (function(which){ var p=i
 6 //为属性创设获取器
 7 which[ “get” + i ] = function() {
 8 return properties[p];
 9 };
10 //为属性创立设置器
11 which[ “set” + i ] = function(val) {
12 properties[p] = val;
13 };
14 })(this); }
15 }
16 //创造一个新user 对象实例,传入一个分包属性的靶子作为种子
17 var user = new User({
18 name: “Bob”,
19 age: 44
20 });
21 //请注意name属性并不设有,因为它在properties 对象中,是私房的
22 alert( user.name == null );
23 //不过,我们能够运用用动态变化的法子getname 来访问它
24 alert( user.getname() == “Bob” );
25 //最终,大家能阅览,通过新生成的动态方法设置和得到age 都是足以的
26 user.setage( 22 );
27 alert( user.getage() == 22 );
28 </script>

        };

静态方法

        this.say = hello;

静态方法背后的前提其实跟任何任何形式是同一的。但是,最重大的分歧在于,这几个格局作为目标的静态属性而留存。作为品质,它们在该对象的实例上下文中不可访问;它们唯有在与主对象本身一样的上下文是可用的。这么些与观念的类继承的相似点,使得他们有点像是静态的类措施。

        this.introduce = function(){

 

            alert(‘my name is :’+this.getName());

1 //附加在User对象上的一个静态方法
2 User.cloneUser = function( user ) {
3 //创造并回到一个新的User对象
4 return new User(
5 //该对象是其他user对象的克隆
6 user.getName(),
7 user.getAge()
8 );
9 };

        }

 

    }

 

})()

 

 

 

var p = new Person();

p.say(); //hello world

p.setName(‘xiaoming’);

p.introduce(); //my name is : xiaoming

复制代码

包装的功利那儿就毫无多说了,但在js里拔取一定得留心,幸免过度包装。下边看一下打包对持续有没有怎么样影响。

 

代码如下:

 

复制代码

var Person = (function(){

    //定义私有方法

    function hello(){

        alert(‘hello world!’);

    }

    return function(){

        //定义私有属性,相当于private属性

        var name,age;

        //定义公有方法,相当于public方法

        this.getName = function(){

            return name;

        };

        this.setName = function(newName){

            name = newName;

        };

        this.getAge = function(){

            return age;

        }

        this.setAge = function(newAge){

            age = newAge;

        };

        this.say = hello;

        this.introduce = function(){

            alert(‘my name is :’+this.getName());

        }

    }

})()

 

Person.prototype.call = function(){

    this.say();

}

 

var Student = function(){

    Person.call(this);

}

for(var i in Person.prototype){Student.prototype[i] =
Person.prototype[i]}

 

var s = new Student();

s.setName(‘xiaoli’);

s.getName();//xiaoli

s.call();//helloworld

复制代码

可以看到Student类继承了Person类,得到了Person类的国有方法和共有方法即prototype定义的方法。由于JavaScript并不原生支持封装,所以在JavaScript中贯彻封装一定要专注复杂性的难题。

大家知道…

网站地图xml地图