构造函数与实例
function Foo() {};
let instance = new Foo();
构造函数其实就是一个函数,只不过我们约定俗成:构造函数首字母大写
在江湖上混,有些黑话还是要遵守的
实例就是用new
操作符执行构造函数返回的对象
prototype
js里面一切皆对象,所以函数也是一种对象
既然是对象就可以有属性,prototype
就是函数的内置属性,而且只有函数才有prototype
属性
为什么只有函数才有prototype
属性?
因为所有的函数都可以充当构造函数,而所有的对象都是构造函数创建的。也就是说对象都是函数创建的
而prototype
属性是为了继承而生的,所有只需要函数有就够了
constructor
构造函数的prototype
属性指向的是一个对象,这个对象就是我们说的原型
我们知道去的路,那也要知道回来的路吧!
constructor
就是回来的路。它指向原型所在的构造函数,和prototype
的方向正好相反
有一个问题
function Foo() {};
let instance = new Foo();
console.log(instance.constructor); // function Foo() {};
为什么实例的constructor
属性也指向构造函数?
其实实例的constructor
属性不是它自己的,是原型继承过来的
proto
__proto__
是实例的属性,因为实例是一个对象,而所有对象都是函数创建的,于是所有对象都有__proto__
属性
实例的__proto__
属性指向它的构造函数的原型
一家人
有了这三个属性,实例、构造函数、原型就有了可以互相找到对方的路,从此这一家人互相之间再也不会失了音信
function Foo() {};
let instance = new Foo();
console.log(Foo.prototype); // 原型
console.log(Foo.prototype.constructor); // 构造函数
console.log(instance.__proto__); // 原型
实例有点可怜,它的联系是单向的,如果实例不主动呼出的话,构造函数和原型是找不到它的
Foo的构造函数
那么构造函数Foo
的构造函数又是谁呢?
console.log(Foo.__proto__.constructor); // function Function() { [native code] }
看到那串[native code]
没?它表示到这里止步,否则,你就知道的太多了
总之,这个function Function() { [native code] }
略屌
Foo的构造函数的原型
console.log(Foo.__proto__); // function() { [native code] }
一样,我们得到一个这样的东西function() { [native code] }
Foo.prototype的构造函数
console.log(Foo.prototype.__proto__.constructor); // function Object() { [native code] }
也带了一串[native code]
,但至少我们知道它是个函数
再往上走两步
console.log(Foo.prototype.__proto__.constructor.__proto__.constructor); // function Function() { [native code] }
这是Foo.prototype的构造函数的构造函数,哟,见到老朋友了!
再再往上走两步
console.log(Foo.prototype.__proto__.constructor.__proto__.constructor.__proto__.constructor); // function Function() { [native code] }
诶,见鬼了!往上怎么还是这哥们?
你要是不晕,其实还可以再往上走两步,仍然是这哥们
自己生自己,是不是有点色即是空,空即是色
的味道?
之前说了,所有对象都是函数创建的,这哥们就是函数始祖,是女娲用泥捏出来的
见到始祖,啥也别说,顶礼膜拜吧
Foo.prototype的构造函数的原型
console.log(Foo.prototype.__proto__); // {...}
打印出来是一个有一堆属性的对象
Foo
的原型是由Object
构造的,所以上面打印出来的就是Object.prototype
再往上走两步
console.log(Foo.prototype.__proto__.__proto__.constructor); // Uncaught TypeError: Cannot read property 'constructor' of null
诡异的事情发生了,怎么会报错呢?
原因在于,Foo.prototype.__proto__.__proto__
指向null,也就是Object
的原型的原型是null
那么,Object
的原型并不是构造函数创建的,因为报错了嘛!难道是石头缝里蹦出来的?
燧人之世,大迹在雷泽,华胥履之,而生伏羲。
是不是感觉越来越像神话了
龙生龙,凤生凤
天灵灵,地灵灵,来一张符咒防身
看到没有,只要是(构造)函数,最终会走向Function
,进而循环;只要是原型(对象),最终会走向Object
,进而走向null
说Function
是爹,说Object
是娘,你有意见吗?
instanceof
console.log(instance instanceof Foo); // true
instanceof
的原理是:沿着instance
的__proto__
上溯,同时沿着Foo
的prototype
上溯,如果能相遇,就返回true,如果始终无法相遇,就返回false
来看一个例子
console.log(Function instanceof Object); // true
console.log(Object instanceof Function); // true
再次说明了,Function
可不是像Array, Number, String, Boolean
一样的小弟,它和Object
有说不清道不明的关系
其实原型链并不长
乍一听原型链,好像总也望不到头,其实走两步就到死胡同了
没那么可怕
继承
原型链是怎么继承的呢?
再回过头去看看符咒
如果一个对象,它既是谁的原型,又是谁的实例,那么它就是原型链上的一个神经元,原型链就是通过这些神经元沟通起来的
A的原型是B的实例,好理解吧!
我们也可以手动继承
function Parent() {
this.name = 'Abby';
}
Parent.prototype.getName = function() {
console.log(this.name);
}
function Child() {
}
Child.prototype = new Parent();
var grandson = new Child();
console.log(grandson.getName()); // Abby
当然继承有很多种模式,这里不展开
github原文地址
嗟乎原型链
github博客