原型属性
- 通过字面量创建的对象使用
Object.prototype
作为他们的原型- 通过new创建的对象使用构造函数的prototype属性作为他们的原型
- 通过
Object.create
创建的对象使用第一个参数作为他们的原型
获取原型
对象的原型属性是用来继承属性的,通过 Object.getPrototypeOf()
可以查询一个对象的原型。
几乎所有对象都有原型。
在多数浏览器中,对象的 __proto__
属性指向其原型,而其原型的 constructor
属性则指向这个对象的构造函数,然而构造函数的 prototype
属性则指向通过此构造函数创建的所有对象的原型,代码概括即:
function func(){} // 构造函数
var ins = new func() // 创建的对象
// 1. 对象 ins 的 __proto__ 即为原型
ins.__proto__ === func.prototype
/*可以推测出: ins = new func()*/
// 2. 对象 ins 的构造函数即 func
ins.__proto__.constructor === func
// 3. 既然js中函数也是对象,那么就有
func.__proto__ === Function.prototype
/*可以推测出: func = new Function()*/
// 4. 继续追溯,发现
Function.__proto__ === Function.prototype
Boolean.__proto__ === Function.prototype
Array.__proto__ === Function.prototype
// 5. 可以发现,Function.prototype是所有构造函数的原型
Function.prototype.__proto__ === Object.prototype
/*可以推测出: 所有构造函数的原型 = new Object()*/
/*上面一句话,下文有相关分析*/
- 总之,记住一句话,一个函数对象的
prototype
属性不是该函数对象自己的原型(除了比如Function这类比较特殊的),而是在此函数作为构造函数时生成的所有普通对象的原型,也即上面代码的第一点。 - 构造函数的原型永远是
Function.prototype
,就连Function
自己也是如此:Function.__proto__ === Function.prototype
- 构造函数的
prototype
属性就是一个普通的对象,即func.prototype.__proto__ === Object.prototype
,可以推测出func.prototype = new Object()
关于所有构造函数的原型是Object对象
为什么不是 所有构造函数 = new Object()
呢?
因为相对于普通的对象,函数对象需要继承一些特殊的属性(方法),比如 call
,apply
,caller
等,这些就多是定义在 Function.prototype
属性中的属性(方法),然而此Function.prototype
属性必然也是一个 Object, 所有有了 所有构造函数的原型 = new Object()
的说法
构造函数的属性
有时候我发现像是 toString
,valueOf
这类函数可以通过对象自身调用,但有的函数比如 Object.create
,Object.assign
这类却无法调用。这就是因为有的函数是构造函数自身的属性,而非构造函数 prototype
的属性,所以无法继承。也即:无法继承构造函数自身的属性。
function func(){} // 构造函数
func.test = () => Math.floor(Math.random() * 10)
var ins = new func() // 创建的对象
// ins.test() //报错
这样一来,就了解了有些方法是 Object
自身的属性,而非 Object.prototype
的属性,所以想要改动某构造函数生成的所有对象的属性,需要对该构造函数的 prototype
属性下手,而非构造函数本身。
根据以往编写es6代码使用Babel转为兼容代码的观察,这一个特性正好被用作 class
的静态函数实现方法