原型
什么是原型
每个函数都有一个原型对象(prototype),它是一个属性也是一个指针,指向一个对象。
当所有对象有一共有的属性时,就可以用prototype添加,不用一个一个的重复添加。
Person.prototype.name = "Xiaoming";
Person.prototype.age = 18;
function Person(){
}
var person1 = new Person();
原型的属性
- constructor:创建了构造函数后,系统默认其对象有constructor属性。这个属性指向原函数。
Person.prototype.constructor == Person;
相应的,在每一个使用构造函数新建的实例中,也存在一个指针,指向构造函数的原型对象。这个指针可以为[[prototype]]或__proto__。
- isPrototypeOf:确定传入的对象的原型是否是当前的对象。
var result = Person.prototype.isprototypeOf(person1); //true
- getPrototypeOf:ES5新添加的属性,可以返回实例对象的原型。
var result = Object.getPrototypeOf(person1); //Person.prototype
原型中值的更改
虽然我们能用对象实例去访问对象原型中的值,但是不能使用实例去改变原型中已有的属性和值。
Person.prototype.name = "Xiaoming";
Person.prototype.age = 18;
function Person(){
}
var person1 = new Person();
var person2 = new Person();
person1.name = 'Xiaohua';
console.log(person1.name);
当像这样使用实例强制改变原型中的值时,原型中的值会被覆盖,
这种情况被称为属性遮蔽 (property shadowing)
此时再访问 person1 . name,返回值为‘Xiaohua’;
原有的原型值没有并没有改变。
当delete person1.name时,再次访问person1 . name,返回为‘Xiaoming’;
原型的重写
关于创建原型实例和修改原型的问题。
- 当先创建实例,后添加属性值时:
function Person(){
}
var person1 = new Person();
Person.prototype.name = 'Xiaoming';
console.log(person1.name); //'Xiaoming'
尽管原型赋值在后,但是由于实例与原型松散的连接,所以在寻找person1.name中后从实例找到原型中。所以可以随时给原型添加属性和方法。
2. 当改变重写原型对象时。
function Person(){
}
var person1 = new Person();
Person.prototype = {
name : 'Xiaoming', //上面讲到当使用这种方法创建原型时,
age : 18, //会改变原型constructor的指向
job : 'student'
}
console.log(person1.name); //因此现在找不到person1.name,返回undefined
原型链
Grand.prototype.lastName = 'Xiaoming';
function Grand(){
}
var grand = new Grand();
Father.prototype = grand;
function Father(){
this.name = 'Xiaoqiang';
}
var father = new Father();
son.prototype = father;
function Son(){
this.hobbit = 'havana';
}
var son = new Son();
由上可以看出原型链的实现方法。
此时原型对象包含一个指向另一个原型的指针,则另一个原型中也包含着一个指向另一个构造函数的指针。当另一个原型又是另一个原型的实例,如此层层递进,就构成原型与实例的链条。
而原型链的终端则是Object.prototype。
简而言之,上面的代码中:Son继承了Father,Father继承了Grand,Grand继承了Object。
- 虽然绝大多数对象都继承自Object.prototype,但是还是例外。
- 我们可以构建出不继承自Object的对象
- 使用Object.create(null)即可
谨慎定义
在原型链中定义方法时,不能使用对象字面量创建原型方法。
function Father(){
this.name = 'Xiaoming';
}
var father = new Father();
function Son(){
this.hobbit = 'havana';
}
son.prototype = father;
//这时添加方法,会切断son和father之间的连接,原型链被切断了
son.prototype = {
action : function(){
return 'walk';
}
}
var son = new Son();
原型链的问题
含有引用值的原型属性会被所有实例共享,这就造成了原型链的缺陷。
function Father(){
this.TshirtColors = ['red','blue','yellow'];
}
var father = new Father();
function Son(){
this.hobbit = 'havana';
}
son.prototype = father;
var son1 = new Son();
var son2 = new Son();
son1.TshirtColors.push('black');
alert(son1.TshirtColors); //'red','blue','yelllow','black'
alert(son2.TshirtColors); //'red','blue','yelllow','black'