开发者社区> dragonwang> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

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/


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
《JavaScript 面向对象精要》 阅读摘要(下)
高程面向对象这块内容介绍的比较浅显,个人觉得这本小书是高程的补充,看完之后觉得收获匪浅,所以做了个笔记,以备后询 Js中两种基本数据类型:原始类型(基本数据类型)和引用类型; 原始类型保存为简单数据值,引用类型则保存为对象,其本质是指向内存位置的应用。 其它编程语言用栈存储原始类型,用堆存储引用类型,而js则不同:它使用一个变量对象追踪变量的生存期。原始值被直接保存在变量对象里,而引用值则作为一个指针保存在变量对象内,该指针指向实际对象在内存中的存储位置。
26 0
《JavaScript 面向对象精要》 阅读摘要(上)
高程面向对象这块内容介绍的比较浅显,个人觉得这本小书是高程的补充,看完之后觉得收获匪浅,所以做了个笔记,以备后询 原始类型和引用类型Js中两种基本数据类型:原始类型(基本数据类型)和引用类型; 原始类型保存为简单数据值,引用类型则保存为对象,其本质是指向内存位置的应用。 其它编程语言用栈存储原始类型,用堆存储引用类型,而js则不同:它使用一个变量对象追踪变量的生存期。原始值被直接保存在变量对象里,而引用值则作为一个指针保存在变量对象内,该指针指向实际对象在内存中的存储位置。
38 0
《JavaScript面向对象精要》——1.3 引用类型
因为引用类型不在变量中直接保存对象,所以本例中的object变量实际上并不包含对象的实例,而是一个指向内存中实际对象所在位置的指针(或者说引用)。这是对象和原始值之间的一个基本差别,原始值是直接保存在变量中的。
1298 0
《JavaScript面向对象精要》——导读
大多数开发者将面向对象的编程联想为那些在学校中学到的基于类的语言,比如C++和Java。在用这些语言完成任务之前,必须先创建类,哪怕只是写一个简单的命令行程序。目前业界常用的设计模式也强调了基于类的概念。
1379 0
《JavaScript面向对象精要》——导读
JavaScript拥有上述全部特性,因为语言本身没有类的概念,所以某些特性可能不是以你所期望的方式实现的。乍一看,一个JavaScript程序可能像是一个用C来编写的面向过程的程序。如果写一个函数并传递一些参数,就有了一个看上去没有对象也可工作的脚本。
1137 0
+关注
77
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载