这些js原型及原型链面试题你能做对几道

news2024/11/24 15:18:10

一、前言

在面试过程中,频频被原型相关知识问住,每次回答都支支吾吾。后来有家非常心仪的公司,在二面时,果不其然,又问原型了!

我痛下决心用了两天时间钻研了下原型,弄明白后发现世界都明亮了,原来这么简单 ~

有些理解还比较浅薄,随着时间的推移和理解的深入,以后还会补充。如果大家发现我理解的有问题,欢迎大家在评论中指正。话不多说,切入正题。

二、构造函数

讲原型则离不开构造函数,让我们先来认识下构造函数。

2.1 构造函数分为 实例成员 和 静态成员

让我们先来看看他们分别是什么样子的。

实例成员: 实例成员就是在构造函数内部,通过this添加的成员。实例成员只能通过实例化的对象来访问。

静态成员: 在构造函数本身上添加的成员,只能通过构造函数来访问

    function Star(name,age) {
        //实例成员
        this.name = name;
        this.age = age;
    }
    //静态成员
    Star.sex = '女';

    let stars = new Star('小红',18);
    console.log(stars);      // Star {name: "小红", age: 18}
    console.log(stars.sex);  // undefined     实例无法访问sex属性

    console.log(Star.name); //Star     通过构造函数无法直接访问实例成员
    console.log(Star.sex);  //女       通过构造函数可直接访问静态成员

2.2 通过构造函数创建对象

该过程也称作实例化

2.2.1 如何通过构造函数创建一个对象?

 function Father(name) {
     this.name = name;
 }
 let son = new Father('Lisa');
 console.log(son); //Father {name: "Lisa"}

此时,son就是一个新对象。

2.2.2 new一个新对象的过程,发生了什么?

(1) 创建一个空对象 son {}
(2) 为 son 准备原型链连接 son.__proto__ = Father.prototype
(3) 重新绑定this,使构造函数的this指向新对象 Father.call(this)
(4) 为新对象属性赋值 son.name
(5) 返回this return this,此时的新对象就拥有了构造函数的方法和属性了

2.2.3 每个实例的方法是共享的吗?

这要看我们如何定义该方法了,分为两种情况。

方法1:在构造函数上直接定义方法(不共享)
    function Star() {
        this.sing = function () {
            console.log('我爱唱歌');
        }
    }
    let stu1 = new Star();
    let stu2 = new Star();
    stu1.sing();//我爱唱歌
    stu2.sing();//我爱唱歌
    console.log(stu1.sing === stu2.sing);//false

很明显,stu1 和 stu2 指向的不是一个地方。
所以 在构造函数上通过this来添加方法的方式来生成实例,每次生成实例,都是新开辟一个内存空间存方法。这样会导致内存的极大浪费,从而影响性能。

方法2:通过原型添加方法(共享)

构造函数通过原型分配的函数,是所有对象共享的。

    function Star(name) {
        this.name = name;
    }
    Star.prototype.sing = function () {
        console.log('我爱唱歌', this.name);
    };
    let stu1 = new Star('小红');
    let stu2 = new Star('小蓝');
    stu1.sing();//我爱唱歌 小红
    stu2.sing();//我爱唱歌 小蓝
    console.log(stu1.sing === stu2.sing);//true

2.2.4 实例的属性为基本类型是,它们是共享的吗?

属性存储的是如果存储的是基本类型,不存在共享问题,是否相同要看值内容。

    let stu1 = new Star('小红');
    let stu2 = new Star('小红');
    console.log(stu1.name === stu2.name);//true

    let stu1 = new Star('小红');
    let stu2 = new Star('小蓝');
    console.log(stu1.name === stu2.name);//false

参考 前端进阶面试题详细解答

2.2.5 定义构造函数的规则

公共属性定义到构造函数里面,公共方法我们放到原型对象身上。

三、原型

前面我们在 实例化 和 实例共享方法 时,都提到了原型。那么现在聊聊这个神秘的原型到底是什么?

3.1 什么是原型?

Father.prototype 就是原型,它是一个对象,我们也称它为原型对象。

3.2 原型的作用是什么?

原型的作用,就是共享方法。
我们通过 Father.prototype.method 可以共享方法,不会反应开辟空间存储方法。

3.3 原型中this的指向是什么?

原型中this的指向是实例。

四、原型链

4.1 什么是原型链?

原型与原型层层相链接的过程即为原型链。

4.2 原型链应用

对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在
每个对象都有__proto__原型的存在

function Star(name,age) {
    this.name = name;
    this.age = age;
}
Star.prototype.dance = function(){
    console.log('我在跳舞',this.name);
};
let obj = new Star('张萌',18);
console.log(obj.__proto__ === Star.prototype);//true

4.3 原型链图

4.4 原型查找方式

例如:查找obj的dance方法

    function Star(name) {
        this.name = name;

        (1)首先看obj对象身上是否有dance方法,如果有,则执行对象身上的方法
        this.dance = function () {
            console.log(this.name + '1');
        }
    }

    (2)如果没有dance方法,就去构造函数原型对象prototype身上去查找dance这个方法。
    Star.prototype.dance = function () {
        console.log(this.name + '2');
    };

    (3)如果再没有dance方法,就去Object原型对象prototype身上去查找dance这个方法。
    Object.prototype.dance = function () {
        console.log(this.name + '3');
    };
    (4)如果再没有,则会报错。
    let obj = new Star('小红');
    obj.dance();

(1)首先看obj对象身上是否有dance方法,如果有,则执行对象身上的方法。

(2)如果没有dance方法,就去构造函数原型对象prototype身上去查找dance这个方法。

(3)如果再没有dance方法,就去Object原型对象prototype身上去查找dance这个方法。

(4)如果再没有,则会报错。

4.5 原型的构造器

原型的构造器指向构造函数。

    function Star(name) {
        this.name = name;
    }
    let obj = new Star('小红');
    console.log(Star.prototype.constructor === Star);//true
    console.log(obj.__proto__.constructor === Star); //true

4.5.1 在原型上添加方法需要注意的地方

方法1:构造函数.prototype.方法在原型对象上直接添加方法,此时的原型对象是有constructor构造器的,构造器指向构造函数本身

    function Star(name) {
        this.name = name;
    }
    Star.prototype.dance = function () {
        console.log(this.name);
    };
    let obj = new Star('小红');
    console.log(obj.__proto__);  //{dance: ƒ, constructor: ƒ}
    console.log(obj.__proto__.constructor);  // Star

方法2:Star.prototype = {}给原型重新赋值,此时会丢失构造器,我们需要手动定义构造器,指回构造函数本身

    function Star(name) {
        this.name = name;
    }
    Star.prototype = {
        dance: function () {
            console.log(this.name);
        }
    };
    let obj = new Star('小红');
    console.log(obj.__proto__); //{dance: ƒ}
    console.log(obj.__proto__.constructor); //  ƒ Object() { [native code] }
    Star.prototype.constructor = Star;

4.5.2 一般不允许直接改变原型prototype指向

改变原型指向,会使原生的方法都没了,所以Array、String这些内置的方法是不允许改变原型指向的。如果改变了,就会报错。

    Array.prototype.getSum = function (arr) {
        let sum = 0;
        for (let i = 0; i < this.length; ++i) {
            sum += this[i];
        }
        return sum;
    };
    let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
    console.log(arr.getSum());//45

如果改变prototype指向,会报错!

    Array.prototype = {
        getSum: function (arr) {
            let sum = 0;
            for (let i = 0; i < this.length; ++i) {
                sum += this[i];
            }
            return sum;
        }
    };
    let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
    console.log(arr.getSum());//45

五、继承 - ES5方法

ES6之前并没有给我们提供extends继承,我们可以通过构造函数+原型对象模拟实现继承。

继承属性,利用call改变this指向。但该方法只可以继承属性,实例不可以使用父类的方法。

    function Father(name) {
        this.name = name;
    }
    Father.prototype.dance = function () {
      console.log('I am dancing');
    };
    function Son(name, age) {
        Father.call(this, name);
        this.age = age;
    }
    let son = new Son('小红', 100);
    son.dance();   //报错

如何继承父类的方法呢?

解决方法1:利用Son.prototype = Father.prototype改变原型指向,但此时我们给子类增加原型方法,同样会影响到父类。

    function Father(name) {
        this.name = name;
    }
    Father.prototype.dance = function () {
        console.log('I am dancing');
    };
    function Son(name, age) {
        Father.call(this, name);
        this.age = age;
    }
    Son.prototype = Father.prototype;
    //为子类添加方法
    Son.prototype.sing = function () {
        console.log('I am singing');
    };
    let son = new Son('小红', 100);
    //此时父类也被影响了
    console.log(Father.prototype) //{dance: ƒ, sing: ƒ, constructor: ƒ}

解决方法2:子类的原型指向父类的实例,这样就可以顺着原型链共享父类的方法了。并且为子类添加原型方法的时候,不会影响父类。

    function Father(name) {
        this.name = name;
    }
    Father.prototype.dance = function () {
        console.log('I am dancing');
    };
    function Son(name, age) {
        Father.call(this, name);
        this.age = age;
    }
    Son.prototype = new Father();
    Son.prototype.sing = function () {
        console.log('I am singing');
    };
    let son = new Son('小红', 100);
    console.log(Father.prototype) //{dance: ƒ, constructor: ƒ}

七、类

什么是类?

类的本质还是一个函数,类就是构造函数的另一种写法。

function Star(){}
console.log(typeof Star); //function

class Star {}
console.log(typeof Star); //function

ES6中类没有变量提升

通过构造函数创建实例,是可以变量提升的。
es6中的类,必须先有类,才可以实例化。

类的所有方法都定义在类的prototype属性上面

让我们来测试一下。

    class Father{
        constructor(name){
            this.name = name;
        }
        sing(){
            return this.name;
        }
    }
    let red = new Father('小红');
    let green = new Father('小绿');
    console.log(red.sing === green.sing); //true

向类中添加方法

通过Object.assign,在原型上追加方法。

    class Father{
        constructor(name){
            this.name = name;
        }
        sing(){
            return this.name;
        }
    }
    //在原型上追加方法
    Object.assign(Father.prototype,{
        dance(){
            return '我爱跳舞';
        }
    });
    let red = new Father('小红');
    let green = new Father('小绿');
    console.log(red.dance());//我爱跳舞
    console.log(red.dance === green.dance); //true

constructor方法

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

八、继承 - ES6方法

    class Father {
        constructor(name){
            this.name = name;
        }
        dance(){
            return '我在跳舞';
        }
    }
    class Son extends Father{
        constructor(name,score){
            super(name);
            this.score = score;
        }
        sing(){
            return this.name +','+this.dance();
        }
    }
    let obj = new Son('小红',100);

九、类和构造函数的区别

(1) 类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。

(2) 类的所有实例共享一个原型对象。

(3) 类的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。

十、总结

构造函数特点:

1.构造函数有原型对象prototype。

2.构造函数原型对象prototype里面有constructor,指向构造函数本身。

3.构造函数可以通过原型对象添加方法。

4.构造函数创建的实例对象有__proto__原型,指向构造函数的原型对象。

类:

1.class本质还是function

2.类的所有方法都定义在类的prototype属性上

3.类创建的实例,里面也有__proto__指向类的prototype原型对象

4.新的class写法,只是让对象原型的写法更加清晰,更像面向对象编程的语法而已。

5.ES6的类其实就是语法糖。

十一、什么是语法糖

什么是语法糖?加糖后的代码功能与加糖前保持一致,糖在不改变其所在位置的语法结构的前提下,实现了运行时的等价。

语法糖没有改变语言功能,但增加了程序员的可读性。

十二、面试题分享

面试题1

Object.prototype.__proto__    //null
Function.prototype.__proto__  //Object.prototype
Object.__proto__              //Function.prototype

讲解:
这里涉及到Function的原型问题,附一张图,这图是一个面试官发给我的,我也不知道原作者在哪里~

面试题2

给大家分享那道我被卡住的面试题,希望大家在学习完知识后,可以回顾一下。

按照如下要求实现Person 和 Student 对象
 a)Student 继承Person 
 b)Person 包含一个实例变量 name, 包含一个方法 printName
 c)Student 包含一个实例变量 score, 包含一个实例方法printScore
 d)所有Person和Student对象之间共享一个方法

es6类写法

    class Person {
        constructor(name) {
            this.name = name;
        }
        printName() {
            console.log('This is printName');
        }
        commonMethods(){
            console.log('我是共享方法');
        }
    }

    class Student extends Person {
        constructor(name, score) {
            super(name);
            this.score = score;
        }
        printScore() {
            console.log('This is printScore');
        }
    }

    let stu = new Student('小红');
    let person = new Person('小紫');
    console.log(stu.commonMethods===person.commonMethods);//true

原生写法

    function Person (name){
        this.name = name;
        this.printName=function() {
            console.log('This is printName');
        };
    }
    Person.prototype.commonMethods=function(){
        console.log('我是共享方法');
    };

    function Student(name, score) {
        Person.call(this,name);
        this.score = score;
        this.printScore=function() {
            console.log('This is printScore');
        }
    }
    Student.prototype = new Person();
    let person = new Person('小紫',80);
    let stu = new Student('小红',100);
    console.log(stu.printName===person.printName);//false
    console.log(stu.commonMethods===person.commonMethods);//true

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/144203.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spark 在 KaiwuDB 中的应用与实践

线上沙龙-技术流第 24 期营业啦01月12日&#xff08;周四&#xff09;19:30开务数据库 - B站直播间当数据库面对大量数据复杂 OLAP 查询时&#xff0c;性能出现局限性&#xff0c;无法满足用户 AP 方面的高性能要求。为此&#xff0c;KaiwuDB 推出了此项解决方案&#xff1a;借…

【NI Multisim 14.0原理图环境设置——电路图属性设置】

目录 序言 一、电路图属性设置 &#x1f34a;1.设置对象可见性 &#x1f34a;2.设置图纸颜色 &#x1f34a;3.设置图纸尺寸 &#x1f34a;4.设置图纸方向 &#x1f34a;5.设置图纸单位 &#x1f34a;6.设置图纸网格点 &#x1f34a;7.设置图纸边框 &#x1f34a;8. 设…

ELK安装使用

太久没用了&#xff0c;熟悉一下。 JDK1.8以上环境 下载地址 elasticsearch&#xff1a;https://www.elastic.co/cn/downloads/elasticsearch kibana: https://www.elastic.co/cn/downloads/kibana logstash &#xff1a; https://www.elastic.co/cn/downloads/logstash…

社招前端二面必会手写面试题总结

字符串查找 请使用最基本的遍历来实现判断字符串 a 是否被包含在字符串 b 中&#xff0c;并返回第一次出现的位置&#xff08;找不到返回 -1&#xff09;。 a34;b1234567; // 返回 2 a35;b1234567; // 返回 -1 a355;b12354355; // 返回 5 isContain(a,b);function isContain(a,…

RS—|下载Landsat8/9数据并进行ENVI大气校正(FLAASH模型)

文章目录1、 数据的下载。2、 辐射定标3、大气校正1、 数据的下载。 下载网址&#xff1a;链接: GloVis (usgs.gov)。下载的数据为2022年8月1日湖南省北部的遥感影像数据。该数据为L1级产品&#xff0c;只经过了几何校正&#xff0c;没有经过辐射定标和大气校正。 图1-1.下载…

富而喜悦2023直播盛典 唐苓馨主题演说“特别的礼物”!

网讯2023年1月1日19:30&#xff0c;富而喜悦一年一渡“特别的礼物”新年主题直播盛典晚会如约而至。富而喜悦品牌创始人唐苓馨女士&#xff0c;用自己与身边人的真实故事&#xff0c;为您讲述了“遇见生活中特别的礼物”。以下是富而喜悦品牌创始人唐苓馨女士在2023富而喜悦一年…

手写RPC框架05-通过SPI机制增加框架的扩展性的设计与实现

源码地址&#xff1a;https://github.com/lhj502819/IRpc/tree/v6 系列文章&#xff1a; 注册中心模块实现路由模块实现序列化模块实现过滤器模块实现自定义SPI机制增加框架的扩展性的设计与实现 现有的问题 在上一章节末尾我们提到了&#xff0c;目前我们的RPC框架可扩展性…

发表计算机SCI论文需要注意什么? - 易智编译EaseEditing

一篇SCI&#xff0c;除了能让审稿人浅显易懂的了解你的表达之外&#xff0c;我们还需要在内容上做好&#xff1a; 1、SCI论文标题创新、简洁 创新是因为写科技文章的目的在于报道新的科技进展&#xff0c;缺乏创新因素就会失去发表的意义。 但运用创新要建立在已有的科研成果…

QT部件透明阴影效果与不规则窗体

透明效果原始效果设置整个窗体透明&#xff0c;调用setWindowOpacity( )方法&#xff0c;传入一个0~1之间的值来表示透明度&#xff1b;1表示不透明&#xff0c;0表示完全透明setWindowOpacity(0.5);//0~1之间设置窗体透明&#xff0c;部件不透明setWindowFlags(Qt::FramelessW…

MATLAB | 赠书 | 如何从热图中提取数据

gzh上这篇文章正在抽奖赠书&#xff1a;截止日期2023年1月9日12&#xff1a;00&#xff08;周一&#xff09; MATLAB | 文末赠书 | 如何从热图中提取数据 赠送3本由北京大小出版社提供的《SPSS统计分析大全》 这期做了个可能有用的小工具&#xff0c;一般论文中热图很少给出…

十.指针进阶(对指针的深度理解)

目录 一. 字符指针 1.字符指针的定义 2.字符指针的用法 3.字符指针练习 二. 数组指针 1.指针数组的定义 2.指针数组的用法 三. 指针数组 1.数组指针的定义 2.数组名和&数组名的区别 3.数组指针的用法 4.练习 四. 数组传参和指针传参 1.一维数组传参 2.二维数…

十、MyBatisX插件

文章目录十、MyBatisX插件1 安装MyBatisX插件2 MybatisX代码速成3 在mapper接口中实现自定义功能【尚硅谷】MyBatisPlus教程-讲师&#xff1a;杨博超 失败&#xff0c;是正因你在距成功一步之遥的时候停住了脚步。 十、MyBatisX插件 MyBatis-Plus为我们提供了强大的mapper和ser…

Jdbc配置文件连接mysql8.0——通过拼接字符串进行批量增删改操作

目录 一、基类BaseDao 二、对dog表的批量增删改操作 (一)Dog类 (二)DogDao接口 (三)DogDaoImpl实现类 1.批量新增 2.批量删除 3.批量修改 (四)Test测试 1.新增 2.删除 3.修改 三、对master表进行批量增删改 (一)Master类 (二)MasterDao接口 (三)MasterDaoImpl实…

RK3588平台开发系列讲解(内核调试篇)oops分析

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、oops简介二、oops分析2.1、实验代码2.2、oops信息2.3、oops分析沉淀、分享、成长,让自己和他人都能有所收获!😄 📢当系统内核发生kernel panic的时候,系统会打印出oops信息,本篇主要介绍如何根据oops定位问…

2022/1/6总结

今天学习了KMP算法。 KMP算法 这是一个字符串查找的算法&#xff0c;我们之前学习的字符串查找都是暴力穷举&#xff0c;然而这个效率太低&#xff0c;于是有三位大佬发明了线性的KMP算法。 算法说难不难&#xff0c;说简单也不简单。 算法的核心思想是找到最长的相等的前…

Struts2框架之Action配置

Struts2框架之Action配置Action配置1、访问Action的三种方式1.1、method属性访问1.2、Action动态方法调用1.3、通配符调用2、配置默认的ActionAction配置 Action控制器在Struts2框架中至关重要&#xff0c;主要作用如下&#xff1a; 封装工作单元数据转移的场所返回结果字符串…

(黑马C++)L07 多态

一、多态的基本概念 多态是面向对象程序设计语言中除数据抽象和继承之外的第三个基本特征。 多态&#xff1a;父类的引用或者指针指向子类对象 C支持编译时多态&#xff08;静态多态&#xff09;和运行时多态&#xff08;动态多态&#xff09;&#xff0c;运算符重载和函数重…

纷享销客CRM让科顺营销人更容易呼唤到“炮火”

希望让听得见“炮声”的人&#xff0c;更容易呼唤到“炮火”。对于在一线做营销的人而言&#xff0c;他们就是听到“炮声”的人。让一线的人员听得到“炮声”也就是销售线索、商机&#xff0c;能呼唤到“炮火”也就是呼唤到他们需要的资源。这恐怕是所有营销人都希望达到的境界…

ubuntu Ad-Hoc组网通信

目录 WIFI通信的多种组网方式 1、AP模式 2、Ad-hoc模式 ubuntu18配置ad-hoc模式 WIFI通信的多种组网方式 1、AP模式 最常用的模式&#xff0c;需要一个节点&#xff08;一般是路由器&#xff09;作为AP&#xff0c;其他节点连接到这个AP产生的wifi网络。通信拓扑是星形&a…

11_6、Java集合之Map接口的使用

一、引入Map与Collection并列存在。用于保存具有映射关系的数据:key-value &#xff08;双列集合框架&#xff09;&#xff0c;Map 中的 key 和 value 都可以是任何引用类型的数据 。Map 中的 key 用Set来存放&#xff0c;不允许重复&#xff0c;即同一个 Map 对象所对应 的类&…