面向对象的程序设计

原型模式

function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true

理解原型

无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype 属性所在函数的指针。

如图显示了Person 构造函数、Person 的原型属性以及Person 现有的两个实例之间的关系。

JavaScript高级程序设计读书笔记(第六章)(三)_读书笔记

person1 和person2 都包含一个内部属性,该属性仅仅指向了Person.prototype;换句话说,它们与构造函数没有直接的关系。person1可以调用sayName方法,这是通过查找对象属性的过程来实现的。
通过Object.getPrototypeOf()方法可以返回对象的原型。

alert(Object.getPrototypeOf(person1) == Person.prototype);//true

在查找对象属性时,解析器会沿着对象本身向原型链搜索。即先搜索对象本身,再搜索对象的原型。

虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。添加这个属性只会阻止我们访问原型中的那个属性,但不会修改那个属性。不过,使用delete 操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性.

function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name = "Greg";
alert(person1.name); //"Greg"——来自实例
alert(person2.name); //"Nicholas"——来自原型

delete person1.name;//删除自定义属性
alert(person1.name);//"Nicholas"——来自原型

使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。这个方法只在给定属性存在于对象实例中时,才会返回true。因此,这个方法可以区分实例属性和原型属性。

function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};
var person1 = new Person();
alert(person1.hasOwnProperty("name")); //false
person1.name = "Greg";
alert(person1.name); //"Greg"——来自实例
alert(person1.hasOwnProperty("name")); //true
delete person1.name;
alert(person1.name); //"Nicholas"——来自原型
alert(person1.hasOwnProperty("name")); //false
alert("name" in person1); //true

由于in 操作符只要通过对象能够访问到属性就返回true,hasOwnProperty()只在属性存在于实例中时才返回true(无论是原型属性还是实例属性),因此只要in 操作符返回true 而hasOwnProperty()返回false,就可以确定属性是原型中的属性。

Object.keys()方法:这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。

function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var keys = Object.keys(Person.prototype);
alert(keys); //"name,age,job,sayName"
var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var p1keys = Object.keys(p1);
alert(p1keys); //"name,age"

Object.getOwnPropertyNames():得到所有实例属性,无论它是否可枚举:

var keys = Object.getOwnPropertyNames(Person.prototype);
alert(keys); //"constructor,name,age,job,sayName"

更简单的写法:

function Person(){
}
Person.prototype = {
    name : "Nicholas",
    age : 29,
    job: "Software Engineer",
    sayName : function () {
        alert(this.name);
    }
};

原型的动态性

由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来——即使是先创建了实例后修改原型也照样如此。

var friend = new Person();
Person.prototype.sayHi = function(){
  alert("hi");
};
friend.sayHi(); //"hi"(没有问题!)

调用构造函数时会为实例添加一个指向最初原型的[[Prototype]]指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。
注意:实例中的指针仅指向原型,而不指向构造函数

function Person(){
}
var friend = new Person();
Person.prototype = {
  constructor: Person,
  name : "Nicholas",
  age : 29,
  job : "Software Engineer",
sayName : function () {
  alert(this.name);
}
};
friend.sayName(); //error

上例中,重写的原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系;它们引用的仍然是最初的原型。

所有原生引用类型(Object、Array、String,等等)都在其构造函数的原型上定义了方法。

自定义原生对象的原型属性:

String.prototype.start = function(text){
            return this.indexOf(text) == 0;
}
var str = "Hello World";
alert(str.start('Hello'));//true

原型模式带来的问题:

原型中定义的引用类型的属性,会出现下面的问题:

function Person(){
}
Person.prototype = {
  constructor: Person,
  name : "Nicholas",
  age : 29,
  job : "Software Engineer",
  friends : ["Shelby", "Court"],
  sayName : function () {
    alert(this.name);
  }
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Court,Van"
alert(person2.friends); //"Shelby,Court,Van"
alert(person1.friends === person2.friends); //true

由于friends属性存在于Person.prototype中,所以当person1修改了此属性会迁移到所有的实例中,这往往不是我们所希望看到的,因为一个实例一般需要有属于自己的全部属性 。


阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: JavaScriptJava