一、函数的三种角色
1)作为普通函数
2)作为对象
3)作为类
ps:可以同时存在,之间没有任何冲突
二、怎么理解函数的三种角色
这三种角色可以同时存在,没有任何冲突,举个例子
// 这三种角色是没有冲突的,看下面的例子
function Fn1() { // 这时候,Fn就是一个普通函数,形参赋值,预解释,代码执行
var num = 500
this.x = 100;
}
Fn.prototype.getX = function () {
console.log(this.x);
};
Fn.aaa = 1000; // 这时候,Fn就是作为一个对象(有私有属性,有方法,还有原型)
var f = new Fn; // 这时候,Fn就是一个类,可以通过new创建对象 ,this指向的是f!!
console.log(f.num); //->undefined 这个时候,Fn就是一个类,num是Fn作为普通函数才有用,num跟类完全没关系
console.log(f.aaa); //->undefined 这个时候,Fn就是一个类,aaa是Fn作为对象才有用,aaa跟类完全没有关系
var res = Fn();
console.log(res); //->undefined 这个时候,Fn就是一个普通函数,函数里没返回值,this指向的是window!!!!!!!!
上面的例子完美的解释了,函数三种角色真的没有任何联系。
感觉函数真的是模拟了真实人生,就好比我带着我爸妈,带着我闺女,我作为人类、作为父亲、作为儿子,没有任何冲突,该干嘛干嘛!!
上面的代码,可以打印一下Fn函数里面到底有什么东西。
dir(Fn)
输出信息如下:
可以看到,Fn作为对象,有自己的属性值(arguments、caller、length、name、prototype、__proto__)
三、深入理解原型
其实,上面的一二两点,都是总结,你有想过,为什么函数会存在三种角色吗?其实,想要理解函数的三种角色,就必须深入探索一个完整的原型链的工作方式。
先看一个例子,帮助我们理解函数的三种角色
function Fn() {
this.x = 100;
}
Fn.prototype.getX = function () {
console.log(this.x);
};
var f = new Fn;
console.log(f instanceof Fn); //->true
console.log(f instanceof Object); //->true
简单说明一下上面的代码,使用的原型继承的方式,首先声明定义一个Fn函数类,然后再Fn函数类的原型上添加一个公有的方法getX,之后,使用new关键字创建一个Fn函数类的一个实例,从而f继承了Fn的私有+公有的方法。
很明显,f instanceof Fn 是true的,因为f是通过new Fn来创建出来的,但是为什么f instanceof Object 也是为true呢?要回答这个问题,得理解下面的话。
1)Function是浏览器内置的函数类(Function也是对象),所有的函数都是Function类的一个实例
2)Object是浏览器内置的对象类,所有对象都是Object类的一个实例
3)所有实例,都是对象数据类型
4)根据1、2两点,可以知道,上面定义的Fn函数,是Function类的一个实例,那么Fn函数其实就是一个实例,根据第3点,Fn函数就是一个对象数据类型;
同理,因为Object对象类是一个类,类其实也是就函数,所以Object对象类是Function类的一个实例。
5)根据第3、4点,可以知道,内置类Function、内置类Object、函数Fn它们都是对象,那么是对象会拥有__proto__属性,所以它们都拥有__proto__属性。
四、第三点中代码的完整原型链
值得注意的是,上图中,Function.prototype其实并不是对象,是函数数据类型,非常坑爹!其实它叫做anonymous(匿名函数),没有实际意义的一个函数,但是操作起来跟对象一模一样。
打印Function.prototype,样子如下所示:
五、总结
函数在整个JS中是最复杂也是最重要的知识,一个函数存在多面性,而且相互不冲突:
-> “函数”:它本身是一个函数,执行的时候形成私有作用域(闭包),形参赋值、预解释、代码执行、执行完后栈内存销毁/不销毁;
-> “类”: 它有自己的实例,也有一个叫做prototoype属性是自己的原型;
-> “普通对象”:和var obj = {} 中的obj一样,就是一个普通的对象,它作为对象有自己的私有属性,也可以通过__proto__找到Function.ptototype。