前言
JavaScript的原型有好好的研究过,现在重新捡起来整理下。
简介
严格意义上说JavaScript并不是一个面对对象语言(存在争议),而是基于原型的语言,通过原型实现继承等。
1、proto
JS的原始数据类型有五种:undefined、null、boolean、string、number。
引用类型通常叫做类,常见有:array、object等。
引用类型object的每个实例称之为对象,每个对象都拥有一个原型对象,而指向该原型对象的内部指针则是proto,
通过它可以从中继承原型对象的属性,原型是JavaScript中的基因链接,有了这个,才能知道这个对象的祖祖辈辈。从
对象中的proto可以访问到他所继承的原型对象。
var a = new Array();
console.log('prototype', a.__proto__ === Array.prototype); // true
Array.prototype本身也是一个对象,也有继承的原型:
a.__proto__.__proto__ === Object.prototype // true
// 等同于
Array.prototype.__proto__ === Object.prototype // true
这就说明,array本身也是继承自object,而object的原型则是指向原始类型null。
a.__proto__.__proto__.__proto__ === null // true
// 等同于
Object.prototype.__proto__ === null // true
除了使用proto方法访问对象的原型,还可以通过Object.getPrototypeOf方法来获取对象的原型,以及通过
Object.setPrototypeOf方法来重写对象的原型。
值得注意的是,按照语言的标准,proto属性只有浏览器才能部署,其他环境可以没有这个属性,而且前后的两
根下划线表示它本地是一个内部属性,不应该对使用者暴露。因此,应该尽量少用这个属性,而是用Object.getPrototypeOf
和Object.setPrototypeOf,进行原型对象的读写操作。
这里用prototype属性来描述对象中的原型,是因为这样来的更加形象,而且容易理解。
2、prototype
函数作为JavaScript中的一等公民,它既是函数又是对象,函数的原型指向Function.prototype。
var Foo = function() {}
Foo.__proto__ === Function.prototype // true
函数实例除了拥有prototype属性之外,还拥有prototype属性。通过该函数构造的实例对象,其原型指针prototype
会指向该函数的prototype属性。
var a = new Foo();
a.__proto__ === Foo.prototype; // true
而函数的prototype属性,本身是一个由object构造的实例对象。
Foo.prototype.__proto__ === Object.prototype; // true
//prototype属性很特殊,它还有一个隐式的constructor,指向了构造函数本身。
Foo.prototype.constructor === Foo; // true
a.constructor === Foo; // true
a.constructor === Foo.prototype.constructor; // true
3、原型链
原型链作为实现继承的主要方法,其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。假如我们让原型对象等
于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针,如此就构造了原型链的基本概念。
“原型链”的作用在于,当读取对象的某个属性时,JavaScript引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果
还是找不到,就到原型的原型去找。以此类推,如果直到最顶层的Object.prototype还是找不到,则返回undefined。
4、判断方法
原始数据类型一般使用typeof来判断(两种情况下回返回undefined:1、变量没有声明;2、变量为undefined)。
typeof null返回object,其他引用类型均返回object。
instanceof判断是否由某个构造函数创建,返回boolean类型值。
Object.isPrototypeOf() 只要某个对象处在原型链上,isProtypeOf都返回true
var Bar = function() {}
var b = new Bar();
b instanceof Bar // true
Bar.prototype.isPrototypeOf(b) // true
Object.prototype.isPrototypeOf(Bar) // true
要注意,实例b的原型是Bar.prototype而不是Bar。
从上图中,能看到一个有趣的地方。
Function.prototype.proto指向了Object.prototype,这说明Function.prototype是一个Object实例,那么应当是先有的
Object再有Function。但是Object.prototype.constructor.proto又指向了Function.prototype。这样看来,没有
Function,Object也不能创建实例。
这就产生了一种类「先有鸡还是先有蛋」的经典问题,到底是先有的Object还是先有的Function呢?
5、Object.create
function Point(){};
var Circle = Object.create(Point);
console.log(Circle.__proto__ === Point); // true
console.log(Circle.__proto__ === Point.prototype); // false
使用指定的原型对象和其属性创建了一个新的对象,在例子中实例Circle的原型指向Point。