面向对象与原型

实例化方法:

创建一个对象,然后给这个对象新建属性和方法。

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创建对象最基本的方法,但有个缺点,想创建一个类似的对象,就会产生大量的代码。

工厂模式:

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

构造函数模式:

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())

2.直接将属性和方法赋值给this对象;

3.没有renturn语句。

构造函数的方法有一些规范:

1.函数名和实例化构造名相同且大写,(PS:非强制,但这么写有助于区分构造函数和普通函数)

2.通过构造函数创建对象,必须使用new运算符。

 原型:

prototype通过调用构造函数而创建的那个对象的原型对象。使用原型的好处可以让所有对象实例共享它所包含的属性和方法

在原型模式声明中,多了两个属性,这两个属性都是创建对象时自动生成的。__proto__属性是实例指向原型对象的一个指针,它的作用就是指向构造函数的原型属性constructor。通过这两个属性,就可以访问到原型里的属性和方法了。

PSIE浏览器在脚本访问__proto__会不能识别,火狐和谷歌浏览器及其他某些浏览器均能识别。虽然可以输出,但无法获取内部信息。

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

判断一个对象是否指向了该构造函数的原型对象,可以使用isPrototypeOf()方法来测试。

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

in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。

alert('name' in box); //true,存在实例中或原型中

原型模式的执行流程:

1.先查找构造函数实例里的属性或方法,如果有,立刻返回;

2.如果构造函数实例里没有,则去它的原型对象里找,如果有,就返回;

图解

原型模式创建对象也有自己的缺点,它省略了构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一致的。而原型最大的缺点就是它最大的优点,那就是共享。

原型中所有属性是被很多实例共享的,共享对于函数非常合适,对于包含基本值的属性也还可以。但如果属性包含引用类型,就存在一定的问题:

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());

在一些安全的环境中,比如禁止使用thisnew,这里的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:稳妥构造函数和寄生类似。

原文地址:https://www.cnblogs.com/sunnychen/p/6160202.html