最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 原型链深入:对函数充当多种角色的理解

    正文概述 掘金(mttt)   2021-04-05   430

    函数的函数角色和对象角色

    数据类型分为原始值和对象类型,函数属于对象类型的值。他是一种特殊的对象。那么,所有的对象都有__proto__,函数又是怎么回事呢?

    举例理解:

    function Fn() {
        this.x = 100;
    }
    Fn.prototype.getX = function getX() {
        console.log(this.x);};
    let f1 = new Fn;
    let f2 = new Fn;
    

    我们dir(Fn),发现函数中有[[scope]] (作用域),代码字符串,和各种属性。

    属性中有 prototypename(属性名),length:0(形参个数)

    原型链深入:对函数充当多种角色的理解

    看一下Object,Object内置构造函数 每一个对象都是其实例

    dir(Object.prototype)原型链深入:对函数充当多种角色的理解

    dir(Object)原型链深入:对函数充当多种角色的理解

    原型链深入:对函数充当多种角色的理解

    通过以上结构可以知道,平常应这样使用:实例.hasOwnProperty(),Object.assign()

    所以,函数是具备多种角色的:

    • 函数
      • 普通函数(拥有上下文/作用域)
      • 构造函数(作用:类/实例/原型/原型链)
    • 普通对象(作用:拥有键值对,进行成员访问)

    前面的文章说过,不考虑函数为对象的情况下,__proto__的指向是这样的

    原型链深入:对函数充当多种角色的理解

    考虑函数也是一个实例对象,也拥有自己的__proto__,也应该指向他所属的类的实例原型,这个类就是 Function

    所有的函数(普通函数/构造函数)都是他的一个实例。那么既然是类,Function也应该有自己的prototype

    dir(Function)

    原型链深入:对函数充当多种角色的理解

    dir(Function.prototype)原型链深入:对函数充当多种角色的理解

    原型链深入:对函数充当多种角色的理解

    以下是不考虑函数的构造函数的__proto__,仅考虑对象时,__proto__的指向 原型链深入:对函数充当多种角色的理解

    那么我们知道,所有的构造函数都是Function构造函数的实例,那么所有函数的__proto__应该指向Function.prototype

    如下红线:

    原型链深入:对函数充当多种角色的理解

    基于这张图,我们来进行一些打印有趣的代码

    Function.prototype===Function.__proto__//true 
    Fn.call===Function.prototype.call//true 去构造函数的 原型上找方法
    
    Object.apply===Function.prototype.apply//true 去构造函数的 原型上找方法
    

    所有对象(包含函数对象) 都是Object的实例,都应该可以调取Object.prototype上的方法

    例如Fn.hasOwnProperty,实际上是依据原型链,找到Fn.__proto__.__proto__.hasOwnProperty

    原型链深入:对函数充当多种角色的理解

    所以

    Object.__proto__===Function.prototype//true
    Object.__proto__.__proto__===Object.prototype//true
    

    FunctionObject到底谁大?

    如果非要钻牛角尖去解释这个问题,那么我们要从不同的角度去理解

    1. Object构造函数本身是函数的角度去理解的话,Object本身就是一种函数,他一定是Function构造函数的一个实例

      Object instanceof Function // true
      
    2. 对于任何一个函数来说,所有函数都是对象,即使是构造函数,也属于对象,那么Function构造函数又是Object的实例

      Function instanceof Object // true
      

    所以这个问题是鸡生蛋蛋生鸡的问题。

    如果是从是对象的角度理解,那么最终所有的__proto__都指向Object.prototype,所有的值(排除原始值)都是对象数据类型的(函数也是一种特殊的对象),所有的值都是Object的实例,即所谓的万物皆对象,xxx instanceof Object==>true

    如果从函数角度理解,函数比较特殊(普通函数/箭头函数/构造函数/内置函数/自定义函数/生成器函数)既是对象又是函数,作为函数来讲,所有函数都是Function的一个实例

    到底谁大,那就要看从什么样的身份,什么样的角度,充当什么样的角色相对的去理解。其实不用刻意的钻牛角尖非要区分谁大。知道有这样的特性即可

    这里面只有一个比较特殊的:Function.prototype

    Object.prototypeFn.prototype都是一个对象,按理来说Function.prototype也应该是一个对象,但是打印可以看到: 原型链深入:对函数充当多种角色的理解

    Function.prototype指向的是一个函数,这个函数叫做 匿名空函数,执行后什么事都不干

    打印后后发现他也虽然是个函数,但是他没有prototype,跟对象很相似,虽然是个函数,但反而更像对象 原型链深入:对函数充当多种角色的理解

    举例子加深理解

    例1

    function C1(name) {
        if (name) {
            this.name = name;
        }
    }
    function C2(name) {
        this.name = name;
    }
    
    function C3(name) {
        this.name = name || 'join';
    }
    C1.prototype.name = 'Tom';
    C2.prototype.name = 'Tom';
    C3.prototype.name = 'Tom';
    alert((new C1().name) + (new C2().name) + (new C3().name));
    // "Tomundefinedjoin" 
    

    比较简单,第一个没有name就是原型上的name,第二个没传就是undefined,第三个||返回的是后面的join,然后字符串拼接

    例2

    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();//2
    getName();//4
    Foo().getName();//1
    getName();//1
    new Foo.getName();//2
    new Foo().getName();//3
    new new Foo().getName(); 
    

    前四个输出考察函数执行,变量提升,this,函数作为对象角色。

    原型链深入:对函数充当多种角色的理解

    分别输出 2 4 1 1

    后面考察函数的多种角色问题,优先级问题

    new Foonew Foo()都是执行new,区别在于运算符优先级问题

    原型链深入:对函数充当多种角色的理解

    • xxx.xxx 成员访问 20
    • new xxx() 20
    • new xxx 19

    处理时按照优先级来

    new Foo.getName()这句话有两个运算,new FooFoo.getName,后者成员访问的优先级更高,所以先运算Foo.getName,其返回值假设为一个函数Fg然后再执行new Fg(),而new Fg()会把Fg函数执行一遍,输出2,至于创造出来什么对象,就不用管了

    注意,new Foo.getName()并不是newFoo.getName()的返回结果 undefined,而是将后面的函数当做一个整体来new,假设Foo.getNameFg那么其实运行的是new Fg(),我们可以将Foo.getName修改如下,确实验证了说法 原型链深入:对函数充当多种角色的理解

    所以所以new Foo.getName()的执行最终相当于new (Foo.getName)(),打印2

    new Foo().getName() 这句代码new Foo()Foo().getName都是20,按照文档,应该从左往右运算,所以应该先执行new Foo(),执行完的结果得到后再执行结果.getName() new Foo()时,其中的this不再指向window,而指向实例对象,所以最终返回this,返回的仍然是实例对象,而不是window。因为私有的没有gitName()方法,所以会去其原型上寻找getName()最终输出3 原型链深入:对函数充当多种角色的理解

    new new Foo().getName()这句话,首先执行new Foo(),假设返回一个实例对象0x003,那么就转化为new 0x003.getName()那么就是和第一个一样,先进行成员访问了,即 new (0x003.getName)()0x003.getName私有上没有getName,所以要去其原型上找,最后是输出3这个函数,假设这个函数是0x004,那么相当于new 0x004(),执行函数,输出3

    下面是执行的过程

    原型链深入:对函数充当多种角色的理解


    起源地下载网 » 原型链深入:对函数充当多种角色的理解

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元