• 【宠粉通道】叫我讲解的一道前端JS面试题-灵魂三问:prototype?类的prototype是什么?对象的proto是什么


    file

    作者 | Jeskson

    来源 | 达达前端小酒馆

    1

    究竟是什么样的题目让我徒弟疑惑呢?让我们看看截图先,来源于wx群:

    file

    看看我是怎么回答的:

    file

    file

    file

    file

    file

    function Foo() {
        getName = function () { alert (1); };
        return this;
    }
    var getName;
    //只提升变量声明
    
    function getName() { alert (5);}
    //提升函数声明,覆盖var的声明
    
    Foo.getName = function () { alert (2);};
    
    Foo.prototype.getName = function () { alert (3);};
    
    getName = function () { alert (4);};
    //最终的赋值再次覆盖function getName声明
    
    getName();
    //最终输出4

    看懂没呢?没看懂也别急哈:

    先让我们了解函数的定义:

    函数声明,可以先调用再声明

    dadaFn();
    
    function dadaFn(){
     console.log(dadaFn);
    }

    file

    / 返回 f -> function
    function dadaFn() {
     console.log(dadaFn);
    }

    函数表达式,先声明再调用:

    let daDafn = function() {
     console.log("dada");
    };
    
    daDafn();

    file

    let fn = function() {
        console.log(111);
    }
    fn(); //111
    VM1188:2 111
    undefined
    let daDafn = function() {
     console.log("dada");
    }
    
    daDafn();
    VM1197:2 dada
    undefined

    箭头函数:

    let da = () => {};
    
    console.log( da.prototype );

    file

    JavaScript prototype属性,可以让你向对象添加属性和方法:

    格式:

    object.prototype.name = value;

    使用prototype属性向对象添加属性:

    function dada (name,age) {
     this.name = name;
     this.age = age;
    }
    
    var dashu = new dada("dashucoding", 13);
    
    dada.prototype.job = null;
    
    dashu.job = it;
    
    console.log(dashu.jog);

    输出结果:

    it

    构造函数:

    let da1 = new Function('a', 'b');
    
    da1(1,2);

    file

    let da1 = new Function('a', 'b');
    
    da1(1,2);
    
    VM65:3 Uncaught ReferenceError: b is not defined
        at eval (eval at <anonymous> (local-ntp.html:1), <anonymous>:3:1)
        at <anonymous>:3:1
    (anonymous) @ VM65:3
    (anonymous) @ VM64:3
    
    let fn1 = new Function('a', 'b',  'console.log(a * b)');
    fn1(1, 2); //2
    VM69:3 2
    undefined

    预解析,变量提升的概念:

    file

    面试题

    file

    
    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();
    
    VM243:5 2
    VM243:7 4
    VM243:2 1
    VM243:2 1
    VM243:5 2
    VM243:6 3
    VM243:6 3

    //答案:
    Foo.getName();//2
    getName();//4
    Foo().getName();//1
    getName();//1
    new Foo.getName();//2
    new Foo().getName();//3
    new new Foo().getName();//3

    变量定义提升,this指针指向,运算符优先级

    原型、继承、全局变量污染、对象属性及原型属性优先级

    灵魂三问:

    prototype?类的prototype是什么?对象的proto是什么?

    在JavaScript中,prototype对象是实现面向对象的一个重要机制。

    每个函数就是一个对象(Function),函数对象都有一个子对象 prototype对象,类是以函数的形式来定义的。prototype表示该函数的原型,也表示一个类的成员的集合。

    实现面向对象机制

    通过new创建一个类的实例对象的时候,prototype对象的成员都成为实例化对象的成员。

    1、该对象被类所引用,只有函数对象才可引用;2、在new实例化后,其成员被实例化,实例对象方可调用。

    同时,函数是一个对象,函数对象若直接声明成员,不用被实例化即可调用。

    定义和用法

    prototype 属性使您有能力向对象添加属性和方法。

    构造函数的简单介绍

    file

    function Person(){
     this.name = 'dada';
    }
     var boy = new Person();
    console.log(boy.name); //'dada'
    VM254:5 dada
    undefined

    file

    prototype属性的作用

    为了解决构造函数的对象实例之间无法共享属性的缺点,js提供了prototype属性。

    js中每个数据类型都是对象(除了null和undefined)。

    每个对象都继承自另外一个对象,后者称为“原型”(prototype)对象,只有null除外,它没有自己的原型对象。

    原型对象上的所有属性和方法,都会被对象实例所共享。

    通过构造函数生成对象实例时,会将对象实例的原型指向构造函数的prototype属性。

    每一个构造函数都有一个prototype属性,这个属性就是对象实例的原型对象。

    原型对象的属性不是对象实例的属性。

    prototype是作为构造函数的属性

    对于对象实例来说,prototype是对象实例的原型对象。

    所以prototype即是属性,又是对象。

    file

    原型链:原型链主要用于继承,每个对象都有一个proto属性指向其构造函数的原型对象,当访问对象的属性和方法时,会先在对象自身进行查找,如果不存在,则沿着proto属性向上一级查找,直到没找到返回 undefined,这样的查找过程,称之为原型链。

    原型链,请找我文章:一篇文章带你了解JavaScript中的变量,作用域,和内存问题。

    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

    看代码,先定义一个Foo()的函数,之后为Foo创建一个叫getName的静态属性,用来存储一个匿名的函数,之后为Foo的原型对象,新创建了一个叫getName的匿名函数。

    var getName = function () { console.log (4);};
    function getName() { console.log (5);}

    然后通过函数变量表达式,创建一个getName的匿名函数,然后由通过函数变量表达式,创建一个getName的函数。

    最后一个是声明一个叫getName的函数。

    Foo.getName();  // 2

    这个不用说就是访问Foo()函数上存储的静态属性,答为2。

    下一个问:

    getName();
    
    Foo().getName();
    
    getName();
    
    new Foo.getName();
    
    new Foo().getName();
    
    new new Foo().getName();

    getName(); 的答案呢? // 5
    
    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);}

    预解析/变量提升

    file

    function Foo() {
        getName = function () { alert (1); };
        return this;
    }
    var getName;
    //只提升变量声明
    
    function getName() { alert (5);}
    //提升函数声明,覆盖var的声明
    
    Foo.getName = function () { alert (2);};

    getName = function () { alert (4);};
    //最终的赋值再次覆盖function getName声明
    
    getName();
    //最终输出4

    var getName 只提升变量声明

    function getName 函数声明,覆盖var的声明

    第二个答案

    直接调用getName()函数,就是访问当前上文作用域内的叫getName的函数

    file

    这样var getName提升,函数整体提升到上面了,那么就剩下getName结果为4的那个了。

    第三个问是:// 1

    一个是变量作用域问题,一个是this指向问题。

    Foo().getName();

    第一个Foo()函数:

    function Foo() {
     getName = function() {
      console.log('1');
     }
    }

    即Foo().getName()中

    file

    先执行Foo()函数,然后调用Foo函数返回的返回值对象的getName属性函数。

    file

    第四个:

    getName(); // 函数 1

    相当于window.getName(),因为这个变量被Foo函数执行时修改了,结果同样为1。

    file

    五问:

    new Foo.getName();

    file

    点 . 的优先级高于new操作,等价于:

    new (Foo.getName)();
    // 将getName函数作为了构造函数来执行
    // 2

    六的答案:

    file

    new Foo().getName(); // 3
    // 运算符优先级括号高于new
    
    等价于
    (new Foo()).getName()

    先执行Foo函数,此时作为构造函数,有返回值。

    file

    若有返回值则检查其返回值是否为引用类型

    file

    若返回值是引用类型,则实际返回值为这个引用类型。

    file

    价于
    (new Foo()).getName()
    # ```
    ![file](https://img-blog.csdnimg.cn/20191226013248472.jpeg)
    

    new new Foo().getName();

    new ((new Foo()).getName)();

    // 3`

    ❤️ 不要忘记留下你学习的脚印 [点赞 收藏 评论]

    作者Info:

    【作者】:Jeskson

    【原创公众号】:达达前端小酒馆。

    【福利】:公众号回复 “资料” 送自学资料大礼包(进群分享,想要啥就说哈,看我有没有)!

    【转载说明】:转载请说明出处,谢谢合作!~

    大前端开发,定位前端开发技术栈博客,PHP后台知识点,web全栈技术领域,数据结构与算法、网络原理等通俗易懂的呈现给小伙伴。谢谢支持,承蒙厚爱!!!

    若本号内容有做得不到位的地方(比如:涉及版权或其他问题),请及时联系我们进行整改即可,会在第一时间进行处理。

    请点赞!因为你们的赞同/鼓励是我写作的最大动力!

    欢迎关注达达的CSDN!

    这是一个有质量,有态度的博客

    前端技术栈

  • 相关阅读:
    cf3b(贪心)
    cf4b
    poj 1037(经典dp)
    网络流之SAP算法学习
    cf3d
    hdu 1572(dfs+最短路)
    hdu 1735(贪心)
    Elementary Methods in Number Theory Exercise 1.5.10
    Elementary Methods in Number Theory Exercise 1.5.12
    Elementary Methods in Number Theory Exercise 1.5.10
  • 原文地址:https://www.cnblogs.com/dashucoding/p/12178557.html
Copyright © 2020-2023  润新知