JavaScript是一门基于原型继承的语言
——Douglas Crockford
函数的prototype属性
JS中的函数有一个属性prototype
,指向其原型对象,其它类型的变量不存在该属性。
var a = 1, str = "中国", flag = true, obj = {}, func = function() {};
function add() {}
var test = new Function;
console.log(a.prototype);//undefined
console.log(str.prototype);//undefined
console.log(flag.prototype);//undefined
console.log(obj.prototype);//undefined
console.log(func.prototype);//Object{}
console.log(add.prototype);//Object{}
console.log(test.prototype);//Object{}
对象的__proto__属性
任何对象(除了undefined
和null
)都拥有一个属性,指向其原型对象,在大多数浏览器中,这个属性的名字是__proto__
var a = 1, str = "中国", flag = true, obj = {}, func = function() {};
function add() {}
var test = new Function;
console.log(a.__proto__ === Number.prototype);
console.log(str.__proto__ === String.prototype);
console.log(flag.__proto__ === Boolean.prototype);
console.log(obj.__proto__ === Object.prototype);
console.log(func.__proto__ === Function.prototype);
console.log(add.__proto__ === Function.prototype);
console.log(test.__proto__ === Function.prototype);
console.log(NaN.__proto__ === Number.prototype);
console.log(undefined.__proto__);//Uncaught TypeError: Cannot read property '__proto__' of undefined
console.log(null.__proto__);//Uncaught TypeError: Cannot read property '__proto__' of null
也就是说:
对象实例的
__proto__
值 和 其构造函数的prototype
值 指向了同一块内存区域,这两个属性完全相等
原型链
JS是基于原型继承的面向对象语言,这是它和C++、Java等其他面向对象语言的根本区别。网上流传的这张图很好地画出了JS家族的“族谱”。下面我结合这张图,谈谈我对图中每一根“链条”的理解~
注意:为了便于理解,建议把“函数”和“函数的原型”想象成内存中独立的两个块,函数的prototype
指针,指向了“函数的原型”这个对象
f1
对象是构造函数Foo
的“实例”,因此f1.__proto__ =<mark> Foo.prototype
- 函数
Foo
的prototype
属性指向了“函数的原型”,即Foo.prototype
- 函数的原型有一个
constructor
属性,指向函数本身,也就是说Foo.prototype.constructor </mark>= Foo
。另外f1
对象也有一个constructor
属性,表示对象的构造函数,因此f1.constructor === Foo
function Foo()
的另一种声明方式是var Foo = new Function()
,所以Foo.__proto__ === Function.prototype
var o1 = new Object()
,因此o1.__proto__ === Object.prototype
- 同2.
- 同3.
Object
是JS内置的用于生成“对象类型的对象”的构造函数,因此function Object()
可以理解为var Object = new Function()
,也就是说Object
是Function
构造出来的实例对象,所以Object.__proto__ === Function.prototype
- 这里有点难以理解,
Function
是JS内置的用于生成“函数类型的对象”的构造函数,**8.**中的Object
函数就是通过它new
出来的。而我们知道,在JS的语法中,function AAA()
可以改写为var AAA = new Function()
,那么,function Function()
可以改写成var Function = new Function()
吗?答案是:可以!也就是说:如果把Function
理解为一个对象,它的__proto__
属性等于Function.prototype
;如果把Function
理解为一个函数,它的prototype
属性当然也指向Function.prototype
。所以,Function.__proto__ =<mark> Function.prototype
! - 同2.
- 同3.
- 除了(构造)函数
Object
以外,JS中任何(构造)函数的原型都有一个__proto__
属性指向Object.prototype
这个原型。而(构造)函数Object
的原型直接指向Object.prototype
,中间不需要通过__proto__
属性相连。因此Foo.prototype.__proto__ </mark>= Object.prototype
、Function.prototype.__proto =<mark> Object.protype
、Object.prototype </mark>= Object.prototype
(废话!) - 同12.
Object.prototype
可以被认为是JS世界中一切的老祖宗,因为一切对象都继承自她,一切对象往上追溯若干个__proto__
都会到达Object.prototype
这里。那么问题是,Object.prototype
也是一个对象,她的__proto__
属性应该是她的“构造函数的原型”,那么这个原型是什么呢?我们试一下:
Object.prototype.__proto__ === null
**WTF?!(黑人问号脸)**老祖宗是特么从石头缝里蹦出来的?!不过这到让JS蒙上了一层神(装)秘(逼)色彩。
我们翻过了一座又一座山头,到头来发现山的那边还是一座山。
我们以为故事的结尾有一只精灵藏匿在这语言的最核心,最后发现,这语言的核心竟是一场空。
–END–