下面小编就为大家带来一篇 javaScript 中的原型解析【推荐】。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
最近在学习 javaScript,学习到 js 面向对象中的原型时,感悟颇多。若有不对的地方,希望可以指正。
js 作为一门面向对象的语言,自然也拥有了继承这一概念,但 js 中没有类的概念,也就没有了类似于 java 中的 extends,所以,我觉得 js 中的继承主要依赖于 js 中的原型(链)。
那么,原型是什么呢?我们知道 js 中函数亦是一种对象,当我们创建一个函数时,其实这个函数也就默认的拥有了一个属性叫做 prototype, 这个属型叫做原型属性,他是一个指针,指向了这个函数的原型对象,这个原型对象有一个默认的属性叫做 constructor, 这个属型指向了拥有 protptype 属型的函数。
- function Person() {}
- Person.prototype = { // constructor:Person;
- first_name: "guo",
- hair_color: "black",
- city: "zhengzhou",
- act: function() {
- alert("eatting");
- }
- };
以这个为例,我们先创建了一个函数 Person, 这个函数默认的有一个属性 prototype, 指向 Person.propttype 这个对象, 这个对象有一个默认的属性 constructor(),Person.prototype.constructor--->Person.(其实此处默认的是指向 Object, 后面会做指正)
当我们通过构造函数创建了实例后会怎么样呢?
- function Person() {}
- Person.prototype = {
- first_name: "guo",
- hair_color: "black",
- city: "zhengzhou",
- act: function() {
- alert("eatting");
- }
- };
- var boy = new Person();
- var girl = new Person();
在这时,我们要知道,js 中的构造函数与函数的区别便是这个 new 关键字,使用 new 操作符的函数便是一个构造函数。当我们创建了 Person 的实例对象把它保存在 boy,girl 时,这两个实例对象会生成一个默认的属性叫做_proto_(在 ECMAScript5 中可用 [[prototype]] 表示),这个属型指向了构造函数的原型对象,也就是 boy._proto_--->Person.prototype(与构造函数毫无关系)。此时, boy 或者 girl 可以通过点来调用原型对象中的属型,此时要知道,boy,girl 共享了原型对象的属型。我们可以通过 isProtptypeOf()或者 object.getPrototypeOf()(这个函数的返回值为原型对象,也就是_proto_的值)来验证上述结论。
- alert(Person.prototype.isPrototypeOf(boy)); //true
- alert(Object.getPrototypeOf(boy).first_name); //"guo"
此时,我们可以再做进一步验证,若在实例中创建了一个与原型对象属性重名的属性会怎么样呢?
- var boy=new Person();
- var girl=new Person();
- boy.hair_color="red";
- alert(boy.hair_color); //red
- alert(girl.hair_color); //black
- alert(Object.getPrototypeOf(boy).hair_color); //black
由此可见,实例中声明的重名属性会屏蔽的原型对象中的属性,但也仅仅时覆盖,不会对原型对象的属型造成影响(Object.getPrototypeOf(boy).hair_color==black),也不会对其他共享原型对象属型的实例对象产生影响 (girl.hair_color==black)。与此同时,可以使用 delete 操作符删除实例对象声明的属性来撤销掉屏蔽效果。我们可以使用 hasOwnProperty() 来验证一个属型是存在于实例的(true),还是存在于原型对象的(false)。
- alert(boy.hasOwnProperty("hair_color")); //true
可以使用 Object.keys() 来枚举属性。
- var key=Object.keys(Person.prototype);
- alert(key);
学习了这些,我们会发现,使用上面的写法来声明原型对象会出现一个问题,constructor 不再指向 Person 了,这与我们说的原型对象的 constructor 属性默认指向含有 prototype 属性的函数背道而驰,这是因为:每创建一个函数会自动创建一个 prototype 对象,这个对象会默认创建 constructor。所以,此处我们的本质是对默认的 prototype 进行了重写,因此新的 consrtuctor 也变成了指向 Object 函数,不再指向 Person 函数。若 constructor 真的很重要,那么需要写上 constructor:Person.
之后,我们需要知道原型的动态性,改变原型对象中的属性会反应到实例中,不管实例的创建在原型对象的属型改变前面或者后面
- function Person() {}
- Person.prototype = {
- first_name: "guo",
- hair_color: "black",
- city: "zhengzhou",
- act: function() {
- alert("eatting");
- }
- };
- var boy = new Person();
- Person.prototype.hobby = "basketball";
- var girl = new Person();
- alert(boy.hobby); //basketball
上面这段代码可见,即使对原型对象属性的修改发生在实例创建的后面,boy 实例亦然共享了 Person.prototype.hobby.
但是,这种情况仅仅发生在原型对象属型的修改,当对原型对象属性进行完全重写时,实例的创建必须放在原型对象属性重写的后面,否则会出错。
- function Person() {}
- var girl = new Person();
- Person.prototype = {
- first_name: "guo",
- hair_color: "black",
- city: "zhengzhou",
- act: function() {
- alert("eatting");
- }
- };
- var boy = new Person();
- Person.prototype.hobby = "basketball";
- alert(boy.hobby); //basketball
- alert(girl.first_name); //undefined
再回到 "屏蔽" 这一问题上,我们前面了解到了创建实例对象的属性(与原型对象中的某一属性重名)会屏蔽掉原型对象的该属性,但不影响其他实例对象。这里有一个错误,这个情况只适用于基本数据类型,当属性的值引用类型时,会出现一个大问题,看如下代码。
- function Person() {}
- Person.prototype = {
- first_name: "guo",
- hair_color: "black",
- friends: ["Nick", "John"],
- city: "zhengzhou",
- act: function() {
- alert("eatting");
- }
- };
- var boy = new Person();
- boy.friends.push("Mike");
- var girl = new Person();
- alert(boy.friends); //Nick,John,Mike
- alert(girl.friends); //Nick,John,MIke
可见,上面这句话不适用了,原因是 friends 是存在于原型对象中的,而不是 boy 中,所以他的修改会影响到这个环境。(我们可以通过 boy.frindes=[] 来创建一个 boy 实例的属性)那么,我们就需要引入组合使用构造函数模式与原型模式。
- function Person(hair_color,city){
- this.hair_color=hair_color;
- this.city=city;
- this.friends=["John","Nick"];
- }
- Person.prototype={
- constructor:Person,
- first_name:"guo",
- act:function() {
- alert("eatting");
- }
- };
- var boy=new Person("black","zhengzhou");
- var girl=new Person("red","shenyang");
- boy.friends.push("Nick");
- alert(girl.friends);
- alert(boy.friends);
该模式是目前 ECMAScript 中使用最广泛,认同最高的创建自定义类型的方法,甚至可以作为一种默认模式。
但是对于从事其他面向对象语言的程序员来说,这样的模式显得很怪异,为了将所有的信息都封装到构造函数中,动态原型模式出现了。动态模式主要是通过一个 if 语句来判断是否需要对原型对象进行初始化,以达到节省资源的目的。
此外还有稳妥构造模式,是为了适应没有共享属性和不使用 this 的情况。
以上这篇 javaScript 中的原型解析【推荐】就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持 phperz。
来源: http://www.phperz.com/article/17/0301/265994.html