JavaScript深入理解之原型
JavaScript中所有的对象都有一个特殊的内置属性prototype,该属性是对其它对象的引用,我们通常所说的原型指的就是原型属性prototype,下面以函数对象为例,进行说明。
1.原型属性
* 函数的初始prototype属性值为一个‘空’对象 *
function Foo() {
return 123;
}
console.dir(typeof Foo.prototype); // => 'object'
foo.prototype = {a: 123};
console.log(Foo.prototype.a) // => '123'
* 也可以设置prototype属性,这个不会对foo函数本身造成影响,因为只有当foo()作为构造器使用时,这些属性才会起作用。 *
2.利用原型添加方法和属性
* 构造函数.prototype.属性名 = 属性值 *
function Foo() {
this.a = 123;
this.b = 456;
}
Foo.prototype.c = 789;
3.使用原型的方法和属性
function Foo() {
this.a = 123;
this.b = 456;
}
Foo.prototype.c = 789;
let AA = new Foo(); // a、b是AA的自身属性,c是其原型属性
Foo.prototype.d = 135; // d是原型属性
let BB = new Foo();
console.log(AA.a); // => 123
console.log(AA.c); // => 789
console.log(AA.d); // => 135
console.log(BB.d) // => 135
console.log(AA.constructor.prototype); // => {c:789, d:135}
console.log(AA.constructor.prototype === BB.constructor.prototype)// true
* 上例中:开始给构造函数Foo增加了原型属性c,新建对象AA,aa有a、b、c三个属性,之后给Foo再次增加原型属性d,又新建一个对象BB,AA和BB都具有d属性,因为原型具有‘实时’性,又AA和BB是基于同一个构造对象创建的,所以AA和BB都能够访问到d属性。访问对象的某个属性时,JS引擎会先查找自身属性,如果没有,会查找构造器函数的原型属性 *
4.判断当前对象是否是另一个对象的原型 —– isPrototypeOf()
let aa = {a: 123};
function Foo() {
}
Foo.prototype= aa;
let AA = new Foo();
let rest = aa.isPrototypeOf(AA);
console.log(rest); // => true
5.关于__proto__
function Foo() {
this.a = 123;
this.b = 456;
}
Foo.prototype.c = 789;
let AA = new Foo();
let bb = AA.__proto__;
let cc = AA.constructor.prototype;
let dd = AA.constructor;
console.log(bb); // => {c: 789}
console.log(cc); // => {c: 789}
console.log(bb === cc); // => true
console.log(dd); // => Foo
- __proto__是实例对象的属性
- prototype是构造函数的属性
- constructor是实例对象的属性
- __proto__指向该实例对象基于的构造函数的原型属性
- prototype是构造函数的原型属性
- constructor指向该实例对象基于的构造函数
6.扩展内建对象属性
String.prototype.resver = function() {
return 123;
}
console.log('456'.resver()); // => 123
* 利用prototype来扩展内建对象属性 *
7.原型陷阱
function Foo() {
this.a = 123;
}
Foo.prototype.b = 456;
let AA = new Foo();
console.dir(AA.constructor); // => Foo
console.dir(AA.__proto__); // => {b: 456}
Foo.prototype = { //重写构造函数原型属性
c: 789,
d: 135
}
// 重写构造函数原型属性后,重写之前的对象__prototype__指向的依旧是旧的原型属性
console.dir(AA.c); // => undefined
console.dir(AA.b); // => 456
console.dir(AA.__proto__); // => {b: 456}
let BB = new Foo();
/* 重写构造函数原型属性后,新建的对象实际上不是基于原来的构造函数了,看下面第三个打印,新建对象的__proto__属性指向的是新的原型属性,即是新的构造函数(新建对象基于该构造函数)的原型属性 */
console.dir(BB.c); // => 789
console.dir(BB.b); // => undefined
console.dir(BB.constructor); // => Object
console.dir(BB.__proto__); // => {c: 789, d: 135}
}
* 重写某个对象的prototype时,需要重置相应的constructor属性 *