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

简介: 1. 原型模式 1.1 我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。 简单的解释上面的话的意思就是:首先,我们要知道,面向对象的语言中类的存在,而javascript也是一门面向对象的语言(这句话说的可能有一些毛病,但是不影响),在javascript中定义一个类函数的时候,就默认创

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/


目录
相关文章
|
22天前
|
SQL 安全 测试技术
软件测试的艺术:从错误中学习,提升产品质量
本文将探讨软件测试的重要性和如何通过测试来提高产品质量。我们将从软件测试的基本概念入手,逐步深入到不同的测试方法、工具和技术。同时,我们还将讨论如何建立有效的测试策略以及如何处理测试过程中发现的问题。
|
2月前
|
缓存 运维 数据库
【测试人员兼职指南】利用专业技能:如何从测试转向开发赚钱
本文分享了作者作为测试人员如何利用专业技能转向开发来兼职赚钱的经验,包括分析和解决登录页面跳转、避免重复账号注册、用户登录后首页显示用户名以及添加退出功能等问题,并提供了Django项目中使用sqlite3数据库和后台管理的扩展技巧。
50 1
【测试人员兼职指南】利用专业技能:如何从测试转向开发赚钱
|
2月前
|
Java 测试技术 开发者
在软件开发中,测试至关重要,尤以单元测试和集成测试为然
在软件开发中,测试至关重要,尤以单元测试和集成测试为然。单元测试聚焦于Java中的类或方法等最小单元,确保其独立功能正确无误,及早发现问题。集成测试则着眼于模块间的交互,验证整体协作效能。为实现高效测试,需编写可测性强的代码,并选用JUnit等合适框架。同时,合理规划测试场景与利用Spring等工具也必不可少。遵循最佳实践,可提升测试质量,保障Java应用稳健前行。
39 1
|
12天前
|
测试技术 数据安全/隐私保护 UED
软件测试的艺术:从错误中学习,提升用户体验
在数字化时代,软件已成为我们生活不可或缺的一部分。然而,软件的错误或缺陷往往会导致用户体验的下降,甚至引发严重的后果。因此,软件测试的重要性不言而喻。本文将探讨软件测试的核心价值,如何通过有效的测试策略和工具,从错误中学习,持续优化产品质量,提升用户体验。
28 2
|
12天前
|
测试技术 持续交付 UED
软件测试的艺术与科学:平衡创新与质量的探索在软件开发的波澜壮阔中,软件测试如同灯塔,指引着产品质量的方向。本文旨在深入探讨软件测试的核心价值,通过分析其在现代软件工程中的应用,揭示其背后的艺术性与科学性,并探讨如何在追求技术创新的同时确保产品的高质量标准。
软件测试不仅仅是技术活动,它融合了创造力和方法论,是软件开发过程中不可或缺的一环。本文首先概述了软件测试的重要性及其在项目生命周期中的角色,随后详细讨论了测试用例设计的创新方法、自动化测试的策略与挑战,以及如何通过持续集成/持续部署(CI/CD)流程优化产品质量。最后,文章强调了团队间沟通在确保测试有效性中的关键作用,并通过案例分析展示了这些原则在实践中的应用。
35 1
|
10天前
|
测试技术 UED 开发者
软件测试的艺术:从代码审查到用户反馈的全景探索在软件开发的宇宙中,测试是那颗确保星系正常运转的暗物质。它或许不总是站在聚光灯下,但无疑是支撑整个系统稳定性与可靠性的基石。《软件测试的艺术:从代码审查到用户反馈的全景探索》一文,旨在揭开软件测试这一神秘面纱,通过深入浅出的方式,引领读者穿梭于测试的各个环节,从细微处着眼,至宏观视角俯瞰,全方位解析如何打造无懈可击的软件产品。
本文以“软件测试的艺术”为核心,创新性地将技术深度与通俗易懂的语言风格相结合,绘制了一幅从代码审查到用户反馈全过程的测试蓝图。不同于常规摘要的枯燥概述,这里更像是一段旅程的预告片,承诺带领读者经历一场从微观世界到宏观视野的探索之旅,揭示每一个测试环节背后的哲学与实践智慧,让即便是非专业人士也能领略到软件测试的魅力所在,并从中获取实用的启示。
|
1月前
|
Android开发
Android学习 —— 测试init.rc中的条件触发的处理顺序
Android学习 —— 测试init.rc中的条件触发的处理顺序
|
2月前
|
测试技术 API
软件测试:Postman 工具的使用。开发及测试均需要掌握的测试工具
这篇文章详细介绍了Postman工具的各个模块功能,包括创建请求、集合、环境、自动化测试等,并解释了如何使用Postman进行GET、POST、PUT和DELETE等常见HTTP请求的测试。
|
2月前
|
运维 Kubernetes 监控
|
2月前
|
Java 测试技术 开发者
提高代码质量:深入实践测试驱动开发(TDD)
【8月更文挑战第14天】测试驱动开发是一种强大的软件开发方法,它通过先写测试再编写代码的方式,显著提高了代码质量。通过实践TDD,开发者可以编写出更可靠、更易于维护的代码,并加速开发进程。虽然TDD需要一定的学习和适应过程,但其带来的长期收益是不可估量的。如果你还没有尝试过TDD,现在就开始吧!
下一篇
无影云桌面