欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JavaScript函数的三种角色

程序员文章站 2022-03-18 13:52:18
...

函数即是函数是一个类,能当作构造函数执行;也是一个普通对象,可以添加属性和方法。所以函数有三种角色:1.函数;2.类;3.普通对象。
函数是一个堆,函数体中堆代码以字符串的形式保存在这个堆中;同时函数也是一个普通对象有一些自己的属性【name、length、prototype、__proto__等属性】;函数还是类,作为构造函数通过new可以得到自己的实例。
函数作为类的时候才有原型prototype,Foo.prototype上的可以设置公共的属性和方法(供new Foo出来的实例调用的,这些实例可以通过原型链__proto__找到Foo.prototype上的公共方法)
由一道经典面试题来解析函数的三种角色:
题目:分析下面代码输出的结果

function Foo() {
    getName = function () {
        console.log(1);
    };
    return this;
}
Foo.getName = function () {
    console.log(2);
};
Foo.prototype.getName = function () {
    console.log(3);
};
var getName = function () {
    console.log(4);
};
function getName() {
    console.log(5);
}
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();

此题的图解:
JavaScript函数的三种角色
Foo.getName获取Foo当作对象层面设置的私有属性方法
Foo.prototype.getName获取foo原型上的方法,原型上的方法也可以通过【实例.getName】获得,函数虽然有三种角色,但是这三种角色之间没有必然的联系。Foo私有的属性和它原型上的属性没有关联。
把函数当作函数执行的时候,就走函数的那套规则;当作对象的时候,就按照对象的特点;当作类(构造函数)的时候,就走类的规则。但是这三中角色有一些间接的关系。

在new之前的输出都可以利用之前学习的堆栈、作用域链、函数原型和原型链等知识解答出来。

在不需要传参时,这两种写法都是创建Foo的实例,但是这两者的优先级不同。在MDN网站的“运算符优先级”中有列表明确表示:

new Foo;//(不带参数列表)-->优先级是18.
new Foo();//(带参数列表)-->优先级是19;
Foo.getName;//成员访问-->优先级19

已经知道运算符优先级后再来分析以下代码

new Foo.getName();
new Foo --> 18
Foo.getName() -- 19
所以优先执行Foo.getName(),然后再new
完整代码表示:new 一个输出2的函数,相当于创建这个输出2的函数的实例。new的时候不仅可以创建此函数的实例,还可以把此函数当作普通函数执行。new执行的时候,构造函数执行和普通函数执行时一样,只是会创建一个此类的实例。
new的时候:
1. 普通函数执行 --> 输出2
2. 构造函数(创建实例)

Foo的实例fo没有getName的私有属性,在构造函数中只有this.xx=xxx才是给实例设置的私有属性

function Foo() {
    getName = function () {
        console.log(1);
    };
    return this;
}
let fo=new Foo();

Foo的实例fo没有getName的私有属性,就会通过原型链找到Foo原型上的getName属性

new Foo().getName();
/*
new Foo()-->19
xx.getName()-->19
所以先new Foo()创建Foo的实例,然后 实例.getName(),此实例可以通过原型链__proto__找到Foo.prototype上的getName方法并且调用此方法。
1. 先执行 new Foo(),Foo当作普通函数执行返回一个window且没有输出,Foo执行完后创建一个Foo的实例;
2. Foo实例.getName() == Foo.prototype.getName()//-->输出3
*/

new new Foo().getName();
/*
1. new Foo() --> Foo执行,得到实例foo
new foo.getName(); --> 
new foo.__proto__.getName() --> 
new Foo.prototype.getName() -->
new funtion f(){console.log(3)}
2.先执行foo.getName()即Foo.prototype.getName()【优先级19】-->输出3
3. 再执行new,因为new foo的优先级是18,所以后执行此步。
*/

总结:通过这道题大家可以很好的理解函数的三种角色以及之间的相互关系。JQ核心源码就是利用函数的三种角色及三种角色的相互关系来实现的。

相关标签: javascript