TDD测试驱动的javascript开发(2) ---- javascript面向对象 ~~ 深入学习javascript中prototype

1. 原型模式

1.1 我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。

简单的解释上面的话的意思就是:首先,我们要知道,面向对象的语言中类的存在,而javascript也是一门面向对象的语言(这句话说的可能有一些毛病,但是不影响),在javascript中定义一个类函数的时候,就默认创建了一个prototype属性,在这个prototype属性里的所有的属性和方法将被这个类的所有实例共享。看代码:

//创建一个Person类
function Person() {
}
//Person类prototype里包含了name、age属性和sayName()方法
Person.prototype.name = "defaultName";
Person.prototype.age = 29;
Person.prototype.sayName = function() {
	return this.name;
};

var person = new Person();
var person3 = new Person();
TestCase("test extends",{
	"test person.sayName() should be equals person3.sayName()" : function() {
		assertEquals(person.sayName(),person3.sayName());
	}
});

1.2 在默认的情况下,所有的原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。

Person.prototype.constructor  指向  Person

当为对象实例添加一个属性时,这个属性就会 屏蔽 原型对象中保存的同名属性(它只会阻止我们访问原型中的那个属性,并不会修改那个属性),不过我们可以使用delete操作符删除实例属性,让我们可以继续访问原型中的属性,看代码:

<span style="font-size:14px;">//创建一个Person类
function Person() {
}
//Person类prototype里包含了name、age属性和sayName()方法
Person.prototype.name = "defaultName";
Person.prototype.age = 29;
Person.prototype.sayName = function() {
	alert(this.name);
};

var person = new Person();
person.sayName();         // defaultName

var person2 = new Person();
person2.name = "person2";
person2.sayName();           //person2
//删除实例中的name属性
delete person2.name;
person2.sayName();          //defaultName</span>

1.3 使用hasOwnProperty()方法可以检测一个属性是存在于实例中还是存在于原型中,只有在给定的属性是存在于对象的实例中才会返回true

function Person() {
}
//Person类prototype里包含了name、age属性和sayName()方法
Person.prototype.name = "defaultName";
Person.prototype.age = 29;
Person.prototype.sayName = function() {
	return this.name;
};

var person = new Person();

var person2 = new Person();
person2.name = "person2";

TestCase("test extends",{
	"test person should not be hasOwnProperty name" : function() {
		assertEquals(false,person.hasOwnProperty("name"));
	},
	"test person2 should not be hasOwnProperty name" : function() {
		assertEquals(true,person2.hasOwnProperty("name"));
	},
});

1.4 使用 in 操作符确定一个属性是原型中的属

in操作符只要通过对象能够访问到的属性就返回true,(hasOwnProperty()只在属性存在于实例中才返回true),因此,只要in操作符返回true,hasOwnProperty()返回false,就证明该属性是原型中的属性,看代码:

<span style="font-size:14px;"><span style="font-size:12px;">//创建一个Person类
function Person() {
}
//Person类prototype里包含了name、age属性和sayName()方法
Person.prototype.name = "defaultName";
Person.prototype.age = 29;
Person.prototype.sayName = function() {
	alert(this.name);
};

var person = new Person();

var person2 = new Person();
person2.name = "person2";
person2.color = "blue";

TestCase("test extends",{
	"test Person  person.hasOwnProperty(name)" : function() {
		assertEquals(false,person.hasOwnProperty("name")); //name是原型中的属性,实例中不存在
	},
	"test Person  name in person" : function() {
		assertEquals(true,"name" in person);
	},
	"test Person  person2.hasOwnProperty(name)" : function() {
		assertEquals(true,person2.hasOwnProperty("name"));//name是实例对象的属性
	},
	"test Person  Person2 name in person2" : function() {
		assertEquals(true,"name" in person2);
	},
	"test Person  person2.hasOwnProperty(color)" : function() {
		assertEquals(true,person2.hasOwnProperty("color"));   //color是实例对象的属性
	},
	"test Person  color property in Person2" : function() {
		assertEquals(true,"color" in person2);		//color是实例对象的属性,in也同样可以访问到
	}

});</span>
</span>

1.5枚举出实例的全部属性---- Object.keys()

function Person() {
}
//Person类prototype里包含了name、age属性和sayName()方法
Person.prototype.name = "defaultName";
Person.prototype.age = 29;
Person.prototype.sayName = function() {
	return this.name;
};

var person = new Person();

var person2 = new Person();
person2.name = "person2";

var keys = Object.keys(Person.prototype); 

var keys2 = Object.keys(person2);

TestCase("test extends",{
	"test keys should be an array" : function() {
		assertArray("keys is not an array",keys);
	},
	"test keys[0] should be equals name" : function() {
		assertEquals("name",keys[0]);
	},
	"test keys[1] should be equals age" : function() {
		assertEquals("age",keys[1]);
	},
	"test keys[2] should be equals age" : function() {
		assertEquals("sayName",keys[2]);
	},
	"test keys2 should be an array" : function() {
		assertArray("keys is not an array",keys2);
	},
	"test keys2[0] should be equals age" : function() {
		assertEquals("name",keys2[0]);
	}
});

1.6 更简单的原型语法-----字面量创建对象

function Person() {

}
Person.prototype = {
	name : "defaultName",
	age : 20,
	sayName : function() {
		return this.name;
	}

};

在之前创建函数的时候,我们知道,每创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor。而我们在这里使用的这种语法(字面量),本质上完全重写了默认的prototype对象,因此constructor对象也就变成了新对象的constructor的属性(指向Object构造函数),不再指向Person函数。

function Person() {

}
Person.prototype = {
	name : "defaultName",
	age : 20,
	sayName : function() {
		return this.name;
	}
};

var con = new Person();

TestCase("test extends",{
    "test con should be instance of Object" : function() {
        assertInstanceOf("con shoud be instance of Obejct", Object, con);
    },
    "test con should be instance of Person" : function() {
        assertInstanceOf("con shoud be instance of Person", Person, con);
    },
    "test con constructor should be equals Person" : function() {
        assertNotEquals(Person,con.constructor);                    //不再等于Person
    },
    "test con constructor should not be equals Object" : function() {
        assertEquals(Object,con.constructor);  
    },
    "test con constructor should be same as Person" : function() {
        assertNotSame(Person,con.constructor);
    },
    "test con constructor should not be same as Object" : function() {
        assertSame(Object,con.constructor);  
    }
});

1.7如何保留1.6中的constructor,在constructor的值比较重要的时候,需要我们来保存这个constructor的值,这个时候,我们可以在Person.prototype中重写constructor属性,并设置其值为Person。 -------但是注意:原生的constructor属性是不可以枚举的,如果这样设置了之后,constructor将变成可以枚举的。

function Person() {

}
Person.prototype = {
	constructor : Person,
	name : "defaultName",
	age : 20,
	sayName : function() {
		return this.name;
	}
};

var con = new Person();

TestCase("test extends",{
	"test con should be instance of Object" : function() {
		assertInstanceOf("con shoud be instance of Obejct", Object, con);
	},
	"test con should be instance of Person" : function() {
		assertInstanceOf("con shoud be instance of Person", Person, con);
	},
	"test con constructor should be equals Person" : function() {
		assertEquals(Person,con.constructor);               //完全重写了constructor,所以不再等于Object,但是还是Object的实例哦
	},
	"test con constructor should not be equals Object" : function() {
		assertNotEquals(Object,con.constructor);
	},
	"test con constructor should be same as Person" : function() {
		assertSame(Person,con.constructor);
	},
	"test con constructor should not be same as Object" : function() {
		assertNotSame(Object,con.constructor);
	}
});

补充:如果你的浏览器是兼容ECMAScript5,就可以使用defineProperty

//重设构造函数
Object.defineProperty(Person.prototype,"constructor",{
	enumerable:false,
	value:Persons
});

1.8原型对象的问题

原型中的属性是被所有实例所共享的,当然,这种共享对于函数非常合适,可是对于一些引用类型的属性来说,问题就出现了,因为有些时候我们是不想我们的引用类型的属性被共享:

function Person() {

}
Person.prototype = {
	name : "defaultName",
	age : 20,
	friends : ["fengfeng","tongtong"],
	sayName : function() {
		return this.name;
	}
};
var person = new Person();
person.friends.push("ty");

var person2 = new Person();

TestCase("test property",{
	"test person friends.length should be 3 " : function() {
		assertEquals(person.friends.length,3);
	},
	"test person friends[2] should be ty" : function() {
		assertEquals("ty",person.friends[2]);
	},
	"test person2 friends.length should be became 3 " : function() {
		assertEquals(person2.friends.length,3);      //person2的长度也变成了3
	},
	"test person2 friends should be equals person friends " : function() {
		assertEquals(person.friends,person2.friends); //person2的friends属性也发生了变化
	}
});

2.0 javascript中的对象

2.1 原型模式创建对象:   参见1.1  -   1.8

2.2 构造函数模式创建对象

function Person(name,age) {
	this.name = name;
	this.age = age;
	this.sayName() {
		return this.name;
	};
};
var person1 = new Person("tong",24);
var person2 = new Person("feng",24);

2.3组合使用构造函数模式和原型模式   ------  解决引用类型的属性不被共享

创建自定义类型的最常见方式,就是组合使用构造函数模式和原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性。

function Person(name,age) {
	this.name = name;
	this.age = age;
	this.friends = ['tong','feng'];
}
Person.prototype = {
		constructor : Person,
		sayName : function() {
			return this.name;
		}
};

var person = new Person("tongtong",25);

var person2 = new Person("fengfeng",26);

person.friends.push("ty");

TestCase("test property",{
	"test person friends.length should be 3 " : function() {
		assertEquals(person.friends.length,3);
	},
	"test person friends[2] should be ty" : function() {
		assertEquals("ty",person.friends[2]);
	},
	"test person2 friends.length should be 2 " : function() {
		assertEquals(person2.friends.length,2);      //person2的长度还是2
	},
	"test person2 friends should not be equals person friends " : function() {
		assertNotEquals(person.friends,person2.friends); //person2的friends属性没有发生变化
	},
	"test person2 sayName should be equals person sayName" : function() {
		assertEquals(person.sayName,person2.sayName); //person.sayName===person2.sayName
	}
});

2.4  动态原型模式创建对象

function Person(name,age) {
	this.name = name;
	this.age = age;
	this.friends = ['tong','feng'];
	//method
	if (typeof this.sayName != "function") {
		Person.prototype.sayName = function() {
			return this.name;
		};
	}
}

只有在sayName()方法不存在的时候,才会将它添加到原型中,这段代码只会在初次调用构造函数的时候执行。这里对原型的修改,能够立即在所有实例中得到反映。

使用动态原型模式时,不能使用对象字面量重写原型,如果在已经创建了实例的情况下重写原型,那么就会切断现有实例和新原型之间的联系。

2.5 稳妥构造函数模式创建对象

所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象,稳妥对象最适合使用在一些安全环境中(这些环境会禁止使用this和new),或者在防止数据被其他应用程序改动时使用。

稳妥构造函数的2个特点:

新创建对象的实例方法不引用this,      不使用new操作符调用构造函数

使用稳妥对构造函数模式创建的对象与构造函数之间没什么关系,因此instanceof操作符对这种对象没有意义。

function Person(name,age) {
	var o = new Object();
	o.sayName = function() {
		return name;
	};
	return o;
};

var person = Person("tongtong","25");

TestCase("test property", {
	"test person sayName should be tongtong " : function() {
		assertEquals(person.sayName(),"tongtong");
	},
	"test person is not instanceof Person " : function() {
		assertNotInstanceOf(Person,person);
	}
});

关于安全模式的2个平台

http://www.adsafe.org/

http://code.google.com/p/google-caja/

时间: 2024-11-08 21:26:25

TDD测试驱动的javascript开发(2) ---- javascript面向对象 ~~ 深入学习javascript中prototype的相关文章

TDD测试驱动的javascript开发(3) ------ javascript的继承

说起面向对象,人们就会想到继承,常见的继承分为2种:接口继承和实现继承.接口继承只继承方法签名,实现继承则继承实际的方法. 由于函数没有签名,在ECMAScript中无法实现接口继承,只支持实现继承. 1. 原型链 1.1 原型链将作为实现继承的主要方法,基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法. 构造函数---原型---实例 之间的关系: 每一个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针. function Sup

5常见的JavaScript开发错误避免

JavaScript是网络的动态语言,它被全球开发人员广泛接受.事实上,JavaScript的普及为其伟大的社区做出了贡献. <> 目前,新的图书馆,框架和工具经常被发布,使JavaScript更强大,在有能力的开发人员手中是非常有用的,而其已建立的资源随着时间的推移不断改进. JavaScript是活动存储库中名为GitHub的第一种语言. 在LiveEdu.tv中也可以看到同样的趋势,其中有48,567个JavaScript相关视频是由热心学习者和工程师的用户群创造的,他们希望提高自己的职

《测试驱动数据库开发》—第2章2.1节TDD中类的角色

第 2 章 建立数据库的类 测试驱动数据库开发 开始测试驱动数据库时,需要做的第一件事是定义数据库的类,并且不用过多地担心特定的数据库实例.读完本书后,读者将有可能开始从允许任意的手工修改,转变到允许保持任意有意义的数据库实例.为了帮读者达到这个目的,本章将深入讨论什么是类以及类如何能够提供帮助,还将深入探讨在数据库开发中的影响力是如何不同于应用程序开发的影响力的. 在调和了类的本质与在数据库开发中出现的新的影响力之后,本章展现了一个数据库的类的需求,并展示了如何实现该需求.希望能为开发者提供与

《测试驱动数据库开发》——2.1 TDD中类的角色

2.1 TDD中类的角色 测试驱动数据库开发 在测试驱动开发中,一个类的主要作用是提供一种机制,以便许多具有相同行为的对象能够被创建.这一点非常重要,因为测试软件的方式就是通过检查一个单独对象的行为,并据此来预知从该对象的类生成的所有其他实例的行为. 当没有类时,测试仅仅告诉开发者有关某个特定对象的情况.当有了类时,测试会告诉开发者有关对象将如何被创建的情况,并进一步告诉开发者所有其他对象将如何被创建的情况. 2.1.1 可靠的实例化过程 当人们说"我写了一个对象来做X事情"时,事实上

测试驱动开发TDD(3)

上一篇我剩下的To-Do-List: 猜测数字 输入验证 生成答案 输入次数 输出猜测结果 今天争取全部搞定. 现在我们Guesser.生成答案.输入验证都有了.把它们组装成一起摇身一变成一个Game! 用一个类把这些职责单一的小模块组合起来.我暂且称它为GameManager. 分析剩下的需求.(1)输入6次GameOver.(2)输入合法数字返回猜测结果.(3)游戏结束提示重新开始游戏.(4)中途输入exit 退出游戏.(5)输入正确答案,GameOver. 先把之前写的Guesser提出一

测试驱动开发TDD(1)

TDD 今儿接到一需求如下: 比如一个给定的数字2975,让你去猜.6次机会.如果第一次输入2509,系统会提示 1A2B:其中数字"2"位置猜对&&数字也猜对.称为1A,而"9"和"5"数字猜对了但是位置没有猜对.称为2B..如果输入2975那么就是4个数字都猜对了并且位置也是对的系统提示4A0B.民间俗称猜数字游戏:百度百科传送门:http://baike.baidu.com/view/358630.htm. 做个简单分析.客

《测试驱动数据库开发》—第1章1.2节谁是目标读者

1.2 谁是目标读者测试驱动数据库开发在讨论问题真正的本质是什么和如何解决问题之前,先谈一下关于本书的目标读者.任何读者都可能从本书获取价值,但在使用这本书之前,读者需要具备一些必备的技能. 1.2.1 TDD和OOP为了运用好本书中涉及的技术,读者需要理解测试驱动开发和面向对象编程的好处,但不必是这两方面的专家,只需要知道实现上述两个方面之后所带来的好处是什么.下面这些论断是有意义的. TDD通过让开发者保持在正确的轨道上,以使你能够快速地开发. OOP通过把不相关的事物进行封装并彼此分离,以

《测试驱动数据库开发》——1.2 谁是目标读者

1.2 谁是目标读者 测试驱动数据库开发在讨论问题真正的本质是什么和如何解决问题之前,先谈一下关于本书的目标读者.任何读者都可能从本书获取价值,但在使用这本书之前,读者需要具备一些必备的技能. 1.2.1 TDD和OOP 为了运用好本书中涉及的技术,读者需要理解测试驱动开发和面向对象编程的好处,但不必是这两方面的专家,只需要知道实现上述两个方面之后所带来的好处是什么.下面这些论断是有意义的. TDD通过让开发者保持在正确的轨道上,以使你能够快速地开发. OOP通过把不相关的事物进行封装并彼此分离

为何说 JavaScript 开发很疯狂

[编者按]本文作者为 Sean Fioritto,主要阐述了 JavaScript 开发为何让人有些无从下手的根本原因.文章系国内 ITOM 管理平台 OneAPM 编译呈现. 网络开发乐趣多多!Javascript 却--让人望而却步. 网页开发的其他所有东西都很配合,唯独到了 Javascript,你会感觉好像比别人少了一大块基础知识,完全搞不懂它. 事实就是,没错,你的确缺了几块拼图. 不过,前端开发的现状其实也很疯狂. 并不是只有你感到抓狂. 拉把椅子坐下来.该写个 Javascript