zoukankan      html  css  js  c++  java
  • 【宠粉通道】叫我讲解的一道前端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!

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

    前端技术栈

  • 相关阅读:
    Java实现 LeetCode 69 x的平方根
    Java实现 LeetCode 68 文本左右对齐
    Java实现 LeetCode 68 文本左右对齐
    Java实现 LeetCode 68 文本左右对齐
    Java实现 LeetCode 67 二进制求和
    Java实现 LeetCode 67 二进制求和
    Java实现 LeetCode 67 二进制求和
    Java实现 LeetCode 66 加一
    Java实现 LeetCode 66 加一
    CxSkinButton按钮皮肤类
  • 原文地址:https://www.cnblogs.com/dashucoding/p/12178557.html
Copyright © 2011-2022 走看看