在 javascript
中.
除了 String
,null
,undefined
,Number
,Boolean
之外.
剩下的基本全是 Object
类型.
比如
console.log(typeof new Date()) // ==> object
console.log(typeof new RegExp(/\.js$/)) // object
console.log(typeof new Array()) // object
console.log(typeof new Object()) // object
函数对象比较特殊.使用 tyepof 运算符计算出来的结果是
console.log(typeof new Function()) // Function
但是使用 instaceof
表达式计算出来的结果也是 Object
console.log(new Function() instanceof Object)
对于剩下的原始数据类型.使用 instanceof
计算计算出来的结果都是 false
console.log("abc" instanceof Object) // false
console.log(true instanceof Object) // false
console.log(1 instanceof Object) // false
console.log(undefined instanceof Object) // false
console.log(null instanceof Object) // false
上面写了一大堆,只是为了说明一个问题.
在 javascript
中,函数本身也是一个对象.
在某种程度上和一般对象基本也没有太大差别.
JavaScript 中,函数也是对象.
普通对象使用.
const normalObjct = {}
normalObjct.name = '李四' // 给普通对象提供属性
normalObjct.showName = function () { // 给普通对象提供方法
console.log(this.name)
}
normalObjct.showName()
函数也是对象
function NormalFunction () { }
NormalFunction.name = 'NormalFunction' // 给函数对象提供属性
NormalFunction.showName = function () { // 给函数对象提供方法
console.log(this.name)
}
NormalFunction.showName()
[proto] 和 [prototype]
既然函数也是对象,一般对象也是对象.
它们之间有什么区别呢?
区别一:typeof 计算出来的结果不一样
console.log(typeof NormalFunction) // function
console.log(typeof normalObjct) // object
区别二:就是上面的标题属性proto 和 prototype了
函数对象 : NormalFunction
console.log(NormalFunction.__proto__ ? 'true' : 'false') // true
console.log(NormalFunction.prototype ? 'true' : 'false') // true
一般对象 : normalObject
console.log(normalObjct.__proto__ ? 'true' : 'false') // true
console.log(normalObjct.prototype ? 'true' : 'false') // false
可以发现,对于函数对象和一般对象而言.
它们之前那的差异在于:
- 函数对象同时拥有 proto 和 prototype 属性.
- 普通对象只有 proto属性.
函数对象的[proto]和[property]是同一个对象吗?
console.log(NormalFunction.__proto__ === NormalFunction.prototype) //false
它俩并不是同一个属性.
property 属性
现在已经知道了,函数对象有一个专有的属性,那就是 property
补充:
所以,以后在判断一个参数是否是函数的时候,也可以利用
function isFunction (f) {
return 'prototype' in f
}
console.log(isFunction({})) //false
console.log(isFunction(function () { })) //true
当然前提是,不要在对象上动态的添加这个属性
normalObject.prototype = ''
这样会导致,上述那个判断方法失败.
所以最为稳妥的方式还是利用 typeof
算符.
为什么函数对象上要有一个特殊的 prototype 属性呢?
个人理解:
函数虽然作为一个对象.但是和普通对象还是有一个比较大的区别.
它是一个可以重复执行的过程.
且可以使用new关键字.
在 JavaScript
中
我们可以new一个函数(不用管这个函数是否是以构造对象而声明的)
function add (num1, num2) {
return num1 + num2
}
const obj = new add()
add
方法就是做简单计算而声明的.而不是作为一个构造函数声明.
但即使是这样,我们也可以使用 new 关键字调用add
方法,并返回一个对象.
new 一个普通的对象?
const obj = {}
new obj()
obj is not a constructor
所以,函数虽然做为一个对象.但是在js中,可以这么认为.
所有的函数都可以算作是一个constructor构造器.
既然是构造器,就可以构造出新的对象.
js为了实现其他语言的继承,方便构造出来的对象能够继承一些公有的属性.
所以,给函数对象专门设置了一个 prototype
属性.
让这些新构造出来的对象,都可以方便的从 prototype
属性里继承.
比如 obj.toString
所以结论对于prototype的结论
- 在js对象的函数对象里,都会有 prototype这个属性.
- 因为在js中,所有的函数都是潜在的构造器.
- 都可以使用 new 关键字构造出一个新的对象.
- 我们扩展函数对象的
prototype
属性,就可以方便的新构造出来的实例继承这些属性.
换一种说法就是:
函数作为js对象,有两种身份
- 作为构造器存在
- 作为普通对象存在
函数对象的作为构造器存在 .prototype
现在知道了,函数对象看作为构造器存在时
身上有一个 prototype 属性了.
但是这个属性里有什么字段和内容呢?
function F () { }
console.log(F.prototype)
F {}
好像是一个空对象?
实际上,函数对象的prototype属性对象的内部有一个不可被枚举的属性constructor
F.prototype.constructor
这个 constructor 属性指向了此函数对象本身,
F.prototype.constructor === F // true
这个意思表明,函数作为一个特有prototype属性的对象.
在创建这个函数对象本身的时候,这个函数本身同时也会创建一个 prototype 对象属性.
并在此属性内部添加一个叫constructor的属性,执行函数对象自己.
很绕,一张图.
函数对象的[.proto]
当把函数作为普通对象看待时.
它身上就有 proto 属性.
对于一个普通对象来说, __proto__
指向的是 new 出来它的那个函数的 prototype 属性.
const obj = new Object
obj.__proto__ === Object.prototype // true
如果把此时的函数看成是一个对象.
那么这个函数是被谁new出来的呢?
function F () { }
F.__proto__ //?
实际上,函数作为一个对象看待的话,它是被 Function 函数构造出来的.
我们在写对象字面量的时候,说了一个语法糖.
const obj = {}
//
const obj = new Object
其实,当我们使用 function
关键字定义函数的时候,也可以理解为城一种语法糖.
function F () { console.log('f') }
const F2 = new Function('console.log('f')')
所以,函数作为对象存在,实际上是被 Function
构造出来的.
所以,以下等式就是成立的.
F.__proto__ === Function.prototype // true
一个可以加深理解和记忆的公式是:
x.__proto__ === Y.prototype
- Y 必须是一个函数
- x 可以是一个函数或一个普通对象
- 如果 const x = new Y() 成立,那么这个等式就成立.
总结:
- 严格分的话,在js中存在两种对象:
函数对象
和一般对象
- 一般对象,有 proto属性,而没有 prototype属性.因为一般对象不具备构造器的功能.
- 函数对象则有这两个属性.
- 为了方便理解可以这么看待.
- 当函数对象做为构造器看待时.prototype属性就起了很大作用.
- 当函数对象作为普通对象看待时. proto 就是别的普通对象的
__proto__
一样.都是指向构造自己本身的构造器的 prototype 对象.