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/


目录
相关文章
|
16天前
|
Web App开发 JavaScript 前端开发
Node.js 是一种基于 Chrome V8 引擎的后端开发技术,以其高效、灵活著称。本文将介绍 Node.js 的基础概念
Node.js 是一种基于 Chrome V8 引擎的后端开发技术,以其高效、灵活著称。本文将介绍 Node.js 的基础概念,包括事件驱动、单线程模型和模块系统;探讨其安装配置、核心模块使用、实战应用如搭建 Web 服务器、文件操作及实时通信;分析项目结构与开发流程,讨论其优势与挑战,并通过案例展示 Node.js 在实际项目中的应用,旨在帮助开发者更好地掌握这一强大工具。
38 1
|
16天前
|
开发框架 JavaScript 前端开发
TypeScript 是一种静态类型的编程语言,它扩展了 JavaScript,为 Web 开发带来了强大的类型系统、组件化开发支持、与主流框架的无缝集成、大型项目管理能力和提升开发体验等多方面优势
TypeScript 是一种静态类型的编程语言,它扩展了 JavaScript,为 Web 开发带来了强大的类型系统、组件化开发支持、与主流框架的无缝集成、大型项目管理能力和提升开发体验等多方面优势。通过明确的类型定义,TypeScript 能够在编码阶段发现潜在错误,提高代码质量;支持组件的清晰定义与复用,增强代码的可维护性;与 React、Vue 等框架结合,提供更佳的开发体验;适用于大型项目,优化代码结构和性能。随着 Web 技术的发展,TypeScript 的应用前景广阔,将继续引领 Web 开发的新趋势。
29 2
|
22天前
|
JavaScript 前端开发 测试技术
探索现代JavaScript开发的最佳实践
本文探讨了现代JavaScript开发中的最佳实践,涵盖ES6+特性、现代框架使用、模块化与代码分割、测试驱动开发、代码质量与性能优化、异步编程、SPA与MPA架构选择、服务端渲染和静态站点生成等内容,旨在帮助开发者提升代码质量和开发效率。
|
1月前
|
Web App开发 存储 JavaScript
深入浅出Node.js后端开发
【10月更文挑战第31天】本文将引导你进入Node.js的奇妙世界,探索其如何革新后端开发。通过浅显易懂的语言和实际代码示例,我们将一起学习Node.js的核心概念、搭建开发环境,以及实现一个简单但完整的Web应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇通往高效后端开发的大门。
|
26天前
|
Web App开发 JavaScript 前端开发
深入浅出Node.js后端开发
【10月更文挑战第36天】本文将引导您探索Node.js的世界,通过实际案例揭示其背后的原理和实践方法。从基础的安装到高级的异步处理,我们将一起构建一个简单的后端服务,并讨论如何优化性能。无论您是新手还是有经验的开发者,这篇文章都将为您提供新的视角和深入的理解。
|
27天前
|
运维 监控 JavaScript
鸿蒙next版开发:分析JS Crash(进程崩溃)
在HarmonyOS 5.0中,JS Crash指未处理的JavaScript异常导致应用意外退出。本文详细介绍如何分析JS Crash,包括异常捕获、日志分析和典型案例,帮助开发者定位问题、修复错误,提升应用稳定性。通过DevEco Studio收集日志,结合HiChecker工具,有效解决JS Crash问题。
43 4
|
1月前
|
机器学习/深度学习 自然语言处理 前端开发
前端神经网络入门:Brain.js - 详细介绍和对比不同的实现 - CNN、RNN、DNN、FFNN -无需准备环境打开浏览器即可测试运行-支持WebGPU加速
本文介绍了如何使用 JavaScript 神经网络库 **Brain.js** 实现不同类型的神经网络,包括前馈神经网络(FFNN)、深度神经网络(DNN)和循环神经网络(RNN)。通过简单的示例和代码,帮助前端开发者快速入门并理解神经网络的基本概念。文章还对比了各类神经网络的特点和适用场景,并简要介绍了卷积神经网络(CNN)的替代方案。
|
29天前
|
Web App开发 JavaScript 前端开发
探索后端开发:Node.js与Express的完美结合
【10月更文挑战第33天】本文将带领读者深入了解Node.js和Express的强强联手,通过实际案例揭示它们如何简化后端开发流程,提升应用性能。我们将一起探索这两个技术的核心概念、优势以及它们如何共同作用于现代Web开发中。准备好,让我们一起开启这场技术之旅!
40 0
|
JavaScript 前端开发 测试技术
JavaScript测试和调试工具:学习使用测试框架(如Jasmine、Mocha)对JavaScript代码进行单元测试
JavaScript测试和调试工具:学习使用测试框架(如Jasmine、Mocha)对JavaScript代码进行单元测试
|
JavaScript 前端开发 C++
Node.js:Jest测试框架测试test代码
Node.js:Jest测试框架测试test代码
182 0
Node.js:Jest测试框架测试test代码