ECMAScriptjavaScript 面向对象与原型

面向对象与原型

 

念要:

1.读标准

2.创建对象

3.原型

4.继承

 

ECMAScript
有星星点点种出模式:1.函数式(过程化),2.面向对象(OOP)。面向对象的语言有一个标志,那就是看似的概念,而由此类似可以创建任意多单拥有相同属性与方的目标。但是,ECMAScript
没有看似的定义,因此它们的靶子也跟因类的语言中的对象有所不同。

 

一.学条件

 

JavaScript 课程需要大量的基础。这里,我们详细探索一下:

 

1.xhtml 基础:JavaScript 方方面面需要为此到。

2.面向对象基础:JS 的面向对象是非正统且怪异的,必须来专业面向对象基础。

 

二.创建对象

 

开创一个对象,然后于这目标新建属性和艺术。

 

var box = new Object(); //创建一个Object 对象

box.name = ‘Lee’; //创建一个name 属性并赋值

box.age = 100; //创建一个age 属性并赋值

box.run = function () { //创建一个run()方法并返回回值

return this.name + this.age + ‘运行中…’;

};

alert(box.run()); //输出属性与道的价值

 

点创建了一个对象,并且创造属性和办法,在run()方法里之this,就是象征box
对象自我。这种是JavaScript
创建对象最中心的点子,但产生个短,想创立一个接近的靶子,就见面时有发生大量的代码。

 

var box2 = box; //得到box 的引用

box2.name = ‘Jack’; //直接改变了name 属性

alert(box2.run()); //用box.run()发现name 也改变了

var box2 = new Object();

box2.name = ‘Jack’;

box2.age = 200;

box2.run = function () {

return this.name + this.age + ‘运行中…’;

};

alert(box2.run()); //这样才免与box 混淆,从而保障独立

 

为解决多单近乎对象声明的题目,我们得以应用同样栽叫做工厂模式的办法,这种艺术就是为着解决实例化对象有大量又的题目。

 

function createObject(name, age) { //集中实例化的函数

var obj = new Object();

obj.name = name;

obj.age = age;

obj.run = function () {

return this.name + this.age + ‘运行中…’;

};

return obj;

}

var box1 = createObject(‘Lee’, 100); //第一个实例

var box2 = createObject(‘Jack’, 200); //第二只实例

alert(box1.run());

alert(box2.run()); //保持单身

 

工厂模式解决了再也实例化的题目,但还有一个问题,那便是甄别问题,因为根本无法搞懂他俩到底是哪位目标的实例。

 

alert(typeof box1); //Object

alert(box1 instanceof Object); //true

 

ECMAScript
中得动用构造函数(构造方法)可用来创造特定的目标。类型于Object 对象。

 

function Box(name, age) { //构造函数模式

this.name = name;

this.age = age;

this.run = function () {

return this.name + this.age + ‘运行中…’;

};

}

var box1 = new Box(‘Lee’, 100); //new Box()即可

var box2 = new Box(‘Jack’, 200);

alert(box1.run());

alert(box1 instanceof Box); //很清楚的辨识他于属于Box

 

以构造函数的计,即解决了又实例化的问题,又解决了对象识别的问题,但问题是,这里并没new
Object(),为什么可以实例化Box(),这个是哪里来的啊?

运用了构造函数的主意,和动工厂模式之法门他们不同之处如下:

1.构造函数方法无展示的创建对象(new
Object()),后台自动,this就相当给obj;

2.直接将性能和办法赋值给this 对象;

3.从未renturn 语句子,不欲返回对象,后台自动回到。

 

构造函数的方式来局部正式:

1.函往往称呼及实例化构造名相同且大写,(PS:非强制,但这样形容推进区分构造函数和一般性函数);

2.通过构造函数创建对象,不能不利用new 运算符。

 

既然经过构造函数可以创建对象,那么是目标是哪来的,new
Object()在什么地方

实施了?执行的进程如下:

1.当下了构造函数,并且new 构造函数(),那么尽管后台执行了new Object();

2.以构造函数的作用域给新目标,(即new
Object()创建有底靶子),而函数体内的this 就意味着new Object()出来的目标。

3.实施构造函数内之代码;

4.返回新对象(后台一直回)。

至于this 的使,this
其实就是意味着时作用域对象的援。如果当全局范围this 就意味着window
对象,如果以构造函数体内,就表示时的构造函数所声明的目标。

 

var box = 2;

alert(this.box); //全局,代表window

 

构造函数和一般性函数的绝无仅有区别,就是他们调用的法子不同。只不过,构造函数也是函数,必须用new
运算符来调用,否则就是惯常函数。

 

var box = new Box(‘Lee’, 100); //构造模式调用

alert(box.run());

Box(‘Lee’, 20); //普通模式调用,无效

var o = new Object();

Box.call(o, ‘Jack’, 200) //对象冒充调用

alert(o.run());

 

探索构造函数内部的计(或函数)的问题,首先看下零星个实例化后底属性或方法是否当。

 

var box1 = new Box(‘Lee’, 100); //传递一致

var box2 = new Box(‘Lee’, 100); //同上

alert(box1.name == box2.name); //true,属性之价当

alert(box1.run == box2.run); //false,方法其实呢是同等种植引用地址

alert(box1.run() == box2.run()); //true,方法的价值相当,因为传参一致

 

可管构造函数里之道(或函数)用new
Function()方法来取代,得到同的功用,更加证实,他们最后判的凡引用地址,唯一性。

 

function Box(name, age) { //new Function()唯一性

this.name = name;

this.age = age;

this.run = new Function(“return this.name + this.age + ‘运行中…'”);

}

 

咱俩可通过构造函数外面绑定同一个函数的法子来保证引用地址之一致性,但这种做法没什么必要,只是强化学习了解:

 

function Box(name, age) {

this.name = name;

this.age = age;

this.run = run;

}

 

function run() { //通过外面调用,保证引用地址一样

return this.name + this.age + ‘运行中…’;

}

 

尽管如此使了大局的函数run()来解决了保管引用地址一样的题目,但这种方法以带来了一个新的题材,全局中之this
在对象调用的下是Box 本身,而作普通函数调用的时段,this
又表示window。

 

三.原型

 

我们创建的每个函数都产生一个prototype(原型)性,这个特性是一个目标,它的用途是富含可以由特定项目的有着实例共享的性质和措施。逻辑上得这么明白:prototype
透过调用构造函数而创立的可怜目标的原型对象。使用原型的裨益可以吃有目标实例共享它所富含的习性与道。也就是说,不必在构造函数中定义对象信息,而是可以一直将这些信息补充加到原型中。

 

function Box() {} //声明一个构造函数

Box.prototype.name = ‘Lee’; //在原型里上加属性

Box.prototype.age = 100;

Box.prototype.run = function () { //在原型里补充加计

return this.name + this.age + ‘运行中…’;

};

 

较一下原型内的方地址是否一致:

 

var box1 = new Box();

var box2 = new Box();

alert(box1.run == box2.run); //true,方法的援地址保持一致

 

为重新进一步询问构造函数的宣示方式跟原型模式之宣示方式,我们通过图示来打探一下:

构造函数方式

ECMAScript 1

原型模式方式

ECMAScript 2

于原型模式声明中,多了简单单特性,这简单只属性都是创建对象时自动生成的。__proto__属性凡是实例指向原型对象的一个指针,它的意图就是是靠于构造函数的原型属性constructor。通过这点儿个属性,就可拜到原型里的性能和道了。

PS:IE
浏览器在剧本访问__proto__会无克鉴别,火狐和谷歌浏览器和其它一些浏览器

备能够辨别。虽然可输出,但无法获取内部信息。

 

alert(box1.__proto__); //[object Object]

 

判断一个对象是不是对准了拖欠构造函数的原型对象,可以利用isPrototypeOf()方法来测试。

alert(Box.prototype.isPrototypeOf(box)); //只要实例化对象,即都见面指向

 

原型模式的施行流程:

1.先找构造函数实例里的特性或方法,如果来,立刻回去

2.若果构造函数实例里不曾,则去其的原型对象里寻找,如果发生,就归

虽我们得经对象实例访问保存于原型中之价值,但可休克看通过对象实例更写原型中之值。

 

var box1 = new Box();

alert(box1.name); //Lee,原型里之价

box1.name = ‘Jack’;

alert(box1.name); //Jack,就近原则,

var box2 = new Box();

alert(box2.name); //Lee,原型里的价,没有让box1 修改

 

比方想如果box1
也能于后面继续走访到原型里的值,可以管构造函数里之属性删除即可,具体如下:

 

delete box1.name; //删除实例中之性

alert(box1.name);

 

何以判断属性是当构造函数的实例里,还是以原型里?可以以hasOwnProperty()函数来验证:

 

alert(box.hasOwnProperty(‘name’)); //实例里来返true,否则回false

 

构造函数实例属性与原型属性示意图

ECMAScript 3

in操作符会在经过对象会访问为定属性时返回
true,无论该属性在为实例中尚是原型中。

 

alert(‘name’ in box); //true,存在实例中要原型中

 

我们可以通过 hasOwnProperty()方式检测属性是否有实例中,也足以经
in来判断实例或原型中是否存在属性。那么做这片种植方法,可以判断原型中是否有属性。

 

function isProperty(object, property) { //判断原型中是否留存属性

return !object.hasOwnProperty(property) && (property in object);

}

var box = new Box();

alert(isProperty(box, ‘name’)) //true,如果原型有

 

为吃性和方重新好之反映封装的职能,并且减少非必要的输入,原型的创造好使字面量的法子

function Box() {};

Box.prototype = { //使用字面量的办法

name : ‘Lee’,

age : 100,

run : function () {

return this.name + this.age + ‘运行中…’;

}

};

 

采用构造函数创建原型对象以及动字面量创建对象在动上基本相同,但要来一部分分,字面量创建的章程采取constructor
属性不见面对实例,而会指向Object,构造函数创建的法尽管反。

 

var box = new Box();

alert(box instanceof Box);

alert(box instanceof Object);

alert(box.constructor == Box); //字面量方式,返回false,否则,true

alert(box.constructor == Object); //字面量方式,返回true,否则,false

 

假设想吃许面量方式的constructor 指向实例对象,那么可以这样做:

 

Box.prototype = {

constructor : Box, //直接强制指向即可

};

 

PS:字面量方式为什么constructor
会指向Object?因为Box.prototype={};这种写法其实就算是开创了一个新目标。而每创建一个函数,就会以创建它prototype,这个目标啊会自动获得constructor
属性。所以,新对象的constructor 重写了Box
原来的constructor,因此会指向新对象,那个新对象没点名构造函数,那么就默认为Object。

原型的宣示是发生先后顺序的,所以,重写的原型会切断之前的原型

 

function Box() {};

Box.prototype = { //原型被重新写了

constructor : Box,

name : ‘Lee’,

age : 100,

run : function () {

return this.name + this.age + ‘运行中…’;

}

};

Box.prototype = {

age = 200

};

var box = new Box(); //在此地声明

alert(box.run()); //box 只是前期声称的原型

 

原型对象不仅可以在从定义对象的场面下以,而ECMAScript
内置的援类型且得采用这种措施,并且放置的援类型我为运用了原型。

 

alert(Array.prototype.sort); //sort 就是Array 类型的原型方法

alert(String.prototype.substring); //substring 就是String 类型的原型方法

String.prototype.addstring = function () { //给String 类型添加一个艺术

return this + ‘,被上加了!’; //this 代表调用的字符串

};

alert(‘Lee’.addstring()); //使用这主意

 

PS:尽管给原生的放引用类型丰富方法动起来特别便利,但我们免推荐应用这种办法。因为她或许会见招致命名冲突,不便民代码维护。原型模式创建对象也发和好之毛病,它概括了构造函数传参初始化这同样进程,带来的通病就是是初始化的值都是平的。而原型最要命之弱项就是是它极其可怜之助益,那就是共享。原型中有所属性是为广大实例共享的,共享于函数非常确切,对于含基本值的特性为尚得。但要属性包含引用类型,就有必然的题目:

 

function Box() {};

Box.prototype = {

constructor : Box,

name : ‘Lee’,

age : 100,

family : [‘父亲’, ‘母亲’, ‘妹妹’], //添加了一个数组属性

run : function () {

return this.name + this.age + this.family;

}

};

var box1 = new Box();

box1.family.push(‘哥哥’); //在实例中上加’哥哥’

alert(box1.run());

var box2 = new Box();

alert(box2.run()); //共享带来的辛苦,也生’哥哥’了

 

PS:数据共享的由,导致群开发者放弃行使原型,因为老是实例化出底数量要保留好之性状,而休能够共享。为了化解组织传参和共享问题,可以整合构造函数+原型模式:

 

function Box(name, age) { //不共享的采用构造函数

this.name = name;

this.age = age;

this. family = [‘父亲’, ‘母亲’, ‘妹妹’];

};

Box.prototype = { //共享的施用原型模式

constructor : Box,

run : function () {

return this.name + this.age + this.family;

}

};

 

PS:这种混合模式非常好的化解了传参和援共享的很难题。是创建对象比较好的点子。

原型模式,不管你是否调用了原型中的共享方法,它还见面初始化原型中之法,并且在宣称一个对象时,构造函数+原型部分被人感觉又异常新奇,最好就是管构造函数和原型封装到一头。为了解决之题目,我们好运用动态原型模式

 

function Box(name ,age) { //将所有消息打包到函数体内

this.name = name;

this.age = age;

if (typeof this.run != ‘function’) { //仅在第一次等调用的初始化

Box.prototype.run = function () {

return this.name + this.age + ‘运行中…’;

};

}

}

var box = new Box(‘Lee’, 100);

alert(box.run());

 

当第一坏调用构造函数时,run()方法发现不有,然后初始化原型。当次软调用,就无见面初始化,并且第二浅创新目标,原型为不见面重新初始化了。这样跟获得了打包,又实现了原型方法共享,并且属性都维持单身。

 

if (typeof this.run != ‘function’) {

alert(‘第一不好初始化’); //测试用

Box.prototype.run = function () {

return this.name + this.age + ‘运行中…’;

};

}

var box = new Box(‘Lee’, 100); //第一不行创建对象

alert(box.run()); //第一不善调用

alert(box.run()); //第二蹩脚调用

var box2 = new Box(‘Jack’, 200); //第二涂鸦创建对象

alert(box2.run());

alert(box2.run());

 

PS:使用动态原型模式,要注意一点,不得以又采取字面量的计重写原型,因为见面切断实例和初原型之间的沟通。以上讲解了各种艺术对象创建的方式,如果这几种植方法还不能够满足要求,可以应用同样上马那种模式:寄生构造函数

 

function Box(name, age) {

var obj = new Object();

obj.name = name;

obj.age = age;

obj.run = function () {

return this.name + this.age + ‘运行中…’;

};

return obj;

}

 

寄生构造函数,其实就算是厂模式+构造函数模式。这种模式于通用,但未可知确定目标关联,所以,在足使前所说的模式时,不建议下是模式。在啊情形下采取寄生构造函数比较恰当吧?假设要创建一个怀有额外措施的援类型。由于前说明非建议直接String.prototype.addstring,可以经过寄生组织的法子丰富。

 

function myString(string) {

var str = new String(string);

str.addstring = function () {

return this + ‘,被填补加了!’;

};

return str;

}

var box = new myString(‘Lee’); //比直接当援原型添加要麻烦好多

alert(box.addstring());

 

当有的安之条件遭到,比如禁止采取this 和new,这里的this
是构造函数里无以this,这里的new
是于外部实例化构造函数时未使new。这种创建方式叫妥善构造函数

 

function Box(name , age) {

var obj = new Object();

obj.run = function () {

return name + age + ‘运行中…’; //直接打印参数即可

};

return obj;

}

var box = Box(‘Lee’, 100); //直接调用函数

alert(box.run());

 

PS:稳妥构造函数和寄生类似。

 

四.继承

累是面向对象中一个比较基本之概念。其他标准面向对象语言都见面为此简单栽方式贯彻持续:一个凡是接口实现,一个凡是继续。而ECMAScript
单支持继承,不支持接口实现,而落实持续的道指原型链完成

function Box() { //Box 构造

this.name = ‘Lee’;

}

function Desk() { //Desk 构造

this.age = 100;

}

Desk.prototype = new Box(); //Desc 继承了Box,通过原型,形成链条

var desk = new Desk();

alert(desk.age);

alert(desk.name); //得到给连续的特性

function Table() { //Table 构造

this.level = ‘AAAAA’;

}

Table.prototype = new Desk(); //继续原型链继承

var table = new Table();

alert(table.name); //继承了Box 和Desk

 

原型链继承流程图

ECMAScript 4

苟只要实例化table,那么Desk
实例中发生age=100,原型中增相同之习性age=200,最后结果是稍微吧?

 

Desk.prototype.age = 200; //实例和原型中均包含age

 

PS:以上原型链继承还缺少一绕,那就算是Obejct,所有的构造函数都继承自Obejct。而继承Object
是活动就的,并不需要程序员手动继承。经过连续后底实例,他们之专属关系会怎样呢?

 

alert(table instanceof Object); //true

alert(desk instanceof Table); //false,desk 是table 的超类

alert(table instanceof Desk); //true

alert(table instanceof Box); //true

 

以JavaScript
里,被连续的函数称为超类型(父类,基类也行,其他语言叫法),继承的函数称为子类型(子类,派生类)。继承也有前问题,比如字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法为超类型传递参数。为了缓解引用共享以及超类型无法传参的题材,我们以相同种让借用构造函数的艺,或者成为对象冒充(伪造对象、经典延续)的技巧来缓解当时有限栽问题。

 

function Box(age) {

this.name = [‘Lee’, ‘Jack’, ‘Hello’]

this.age = age;

}

function Desk(age) {

Box.call(this, age); //对象冒充,给超类型传参

}

var desk = new Desk(200);

alert(desk.age);

alert(desk.name);

desk.name.push(‘AAA’); //添加的初数据,只于desk

alert(desk.name);

 

假构造函数虽然缓解了才少种植问题(传参、共享),但从不原型,复用则无从谈起。所以,我们用原型链+借用构造函数的模式,这种模式成为整合继承

 

function Box(age) {

this.name = [‘Lee’, ‘Jack’, ‘Hello’]

this.age = age;

}

Box.prototype.run = function () {

return this.name + this.age;

};

function Desk(age) {

Box.call(this, age); //对象冒充

}

Desk.prototype = new Box(); //原型链继承

var desk = new Desk(100);

alert(desk.run());

 

还有平等种持续模式叫做:原型式继承;这种持续借助原型并因已有些对象创建新对象,同时还不用为此创造于定义类型。

 

function obj(o) { //传递一个字面量函数

function F() {} //创建一个构造函数

F.prototype = o; //把字面量函数赋值给构造函数的原型

return new F(); //最终回出实例化的构造函数

}

var box = { //字面量对象

name : ‘Lee’,

arr : [‘哥哥’,’妹妹’,’姐姐’]

};

var box1 = obj(box); //传递

alert(box1.name);

box1.name = ‘Jack’;

alert(box1.name);

alert(box1.arr);

box1.arr.push(‘父母’);

alert(box1.arr);

var box2 = obj(box); //传递

alert(box2.name);

alert(box2.arr); //引用类型共享了

 

寄生式继承把原型式+工厂模式做而来,目的是为封装创建对象的进程。

function create(o) { //封装创建过程

var f= obj(o);

f.run = function () {

return this.arr; //同样,会共享引用

};

return f;

}

 

组合式继承是JavaScript
最常用的持续模式;但,组合式继承也有同一接触多少题目,就是超类型在用过程遭到会受调用两涂鸦:一涂鸦是创立子类型的时节,另一样次等是当子类型构造函数的中间。

 

function Box(name) {

this.name = name;

this.arr = [‘哥哥’,’妹妹’,’父母’];

}

Box.prototype.run = function () {

return this.name;

};

function Desk(name, age) {

Box.call(this, name); //第二次于调整用Box

this.age = age;

}

Desk.prototype = new Box(); //第一差调整用Box

 

以上代码是之前的结缘继承,那么寄生组合继承,解决了一定量破调用的题材。

function obj(o) {

function F() {}

F.prototype = o;

return new F();

}

function create(box, desk) {

var f = obj(box.prototype);

f.constructor = desk;

desk.prototype = f;

}

function Box(name) {

this.name = name;

this.arr = [‘哥哥’,’妹妹’,’父母’];

}

Box.prototype.run = function () {

return this.name;

};

function Desk(name, age) {

Box.call(this, name);

this.age = age;

}

inPrototype(Box, Desk); //通过这里实现持续

var desk = new Desk(‘Lee’,100);

desk.arr.push(‘姐姐’);

alert(desk.arr);

alert(desk.run()); //只共享了艺术

var desk2 = new Desk(‘Jack’, 200);

alert(desk2.arr); //引用问题化解