Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >这些js原型及原型链面试题你能做对几道

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

原创
作者头像
loveX001
发布于 2022-10-02 04:44:05
发布于 2022-10-02 04:44:05
51000
代码可运行
举报
文章被收录于专栏:前端开发面试前端开发面试
运行总次数:0
代码可运行

一、前言

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

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

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

二、构造函数

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

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

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

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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 如何通过构造函数创建一个对象?
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 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:在构造函数上直接定义方法(不共享)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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:通过原型添加方法(共享)

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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 实例的属性为基本类型是,它们是共享的吗?

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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原型的存在

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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 原型的构造器

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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构造器的,构造器指向构造函数本身

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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 = {}给原型重新赋值,此时会丢失构造器,我们需要手动定义构造器,指回构造函数本身

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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这些内置的方法是不允许改变原型指向的。如果改变了,就会报错。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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指向,会报错!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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指向。但该方法只可以继承属性,实例不可以使用父类的方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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改变原型指向,但此时我们给子类增加原型方法,同样会影响到父类。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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:子类的原型指向父类的实例,这样就可以顺着原型链共享父类的方法了。并且为子类添加原型方法的时候,不会影响父类。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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: ƒ}

七、类

什么是类?

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function Star(){}
console.log(typeof Star); //function

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

ES6中类没有变量提升

通过构造函数创建实例,是可以变量提升的。

es6中的类,必须先有类,才可以实例化。

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

让我们来测试一下。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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,在原型上追加方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Object.prototype.__proto__    //null
Function.prototype.__proto__  //Object.prototype
Object.__proto__              //Function.prototype

讲解:

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

面试题2

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

代码语言:css
AI代码解释
复制
按照如下要求实现Person 和 Student 对象
 a)Student 继承Person 
 b)Person 包含一个实例变量 name, 包含一个方法 printName
 c)Student 包含一个实例变量 score, 包含一个实例方法printScore
 d)所有Person和Student对象之间共享一个方法

es6类写法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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

原生写法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
「JS高级」构造函数和原型
请注意,本文编写于 2063 天前,最后修改于 173 天前,其中某些信息可能已经过时。
曼亚灿
2023/05/17
1.5K0
「JS高级」构造函数和原型
前端成神之路-JavaScript高级第02天
实例成员就是构造函数内部通过this添加的成员 如下列代码中uname age sing 就是实例成员,实例成员只能通过实例化的对象来访问
海仔
2021/01/21
3030
详解js原型,构造函数以及class之间的原型关系
含义: 是一个函数的属性,这个属性是一个指针,指向一个对象 作用: 构造函数调用 访问该构造函数所关联的原型对象
念念不忘
2019/03/29
1.7K0
详解js原型,构造函数以及class之间的原型关系
理解JS 原型链
一直以来对于JavaScript 的原型链的概念,始终有些东西有一种模糊感,最近刚好有时间就塌下心认真的把《JavaScript高级程序设计》中相关内容认真读了一遍,也查看了很多网上很多资料,以前很多不明白的地方也渐渐明白了起来。 写一篇文章记录一下最近学习的感悟。
九旬
2020/10/23
1.7K0
理解JS 原型链
「思维导图学前端 」一文搞懂Javascript对象,原型,继承
去年开始我给自己画了一张知识体系的思维导图,用于规划自己的学习范围和方向。但是我犯了一个大错,我的思维导图只是一个全局的蓝图,而在学习某个知识点的时候没有系统化,知识太过于零散,另一方面也很容易遗忘,回头复习时没有一个提纲,整体的学习效率不高。意识到这一点,我最近开始用思维导图去学习和总结具体的知识点,效果还不错。试想一下,一张思维导图的某个端点是另一张思维导图,这样串起来的知识链条是多么“酸爽”!当然,YY一下就好了,我保证你没有足够的时间给所有知识点都画上思维导图,挑重点即可。
程序员白彬
2020/07/10
7710
「思维导图学前端 」一文搞懂Javascript对象,原型,继承
Js原型链与类
理解原型链与类有利于我们对代码的封装与简化 # JS原型链理论 # 1、函数与对象的关系 函数是对象,对象都是通过函数创建的。 函数与对象并不是简单的包含与被包含的关系。 # 2、原型的类别 显示原型:prototype,是每个函数function独有的属性。 隐式原型: __proto__,是每个对象都具有的属性。 # 3、原型和原型链 原型:一个函数可以看成一个类,原型是所有类都有的一个属性,原型的作用就是给这个类的一个对象都添加一个统一的方法。 原型链:每个对象都有一个__proto__,它指向它的p
隔壁老陈
2023/01/12
1K0
this_原型链_继承
this 相关问题 1: apply、call 、bind有什么作用,什么区别? 在JS中,这三者都是用来改变函数的this对象的指向,相似点: 1.都是用来改变函数的this对象的指向的。 2.
小胖
2018/06/27
5830
构造函数和原型
在典型的OOP的语言中(如Java) , 都存在类的概念,类就是对象的模板,对象就是类的实例,但在ES6之前, JS中并没用引入类的概念。 ES6,全称ECMAScript6.0 , 2015.06发版。但是目前浏览器的JavaScript是ES5版本,大多数高版本的浏 览器也支持ES6 ,不过只实现了ES6的部分特性和功能。 在ES6之前,对象不是基于类创建的,而是用一种称为构建函数的特殊函数来定义对象和它们的特征。
星辰_大海
2020/10/23
4870
构造函数和原型
JS中的面向对象、原型、原型链、继承总结大全
补充: js中说一切都是对象,是不完全的,在js中6种数据类型(Undefined,Null,Number,Boolean,String,Object)中,前五种是基本数据类型,是原始值类型,这些值是在底层实现的,他们不是object,所以没有原型,没有构造函数,所以并不是像创建对象那样通过构造函数创建的实例。关于对象属性类型的介绍就不介绍了。
疯狂的技术宅
2019/03/27
1.4K0
JS中的面向对象、原型、原型链、继承总结大全
js面向对象编程_JavaScript高级编程
面向过程即分析出解决问题所需要的步骤,然后用函数将这些步骤一步步实现,使用的时候再一个个的一次调用就可以了;
全栈程序员站长
2022/09/24
1.1K0
js面向对象编程_JavaScript高级编程
原型链
原型链 这里只是通过一些案例补充之前对原型,原型链,instanceof的细节。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Doc
hss
2022/02/25
3460
原型链
JavaScript进阶--原型、原型链、闭包
在JavaScript中,每个函数 都有一个prototype属性,当一个函数被用作构造函数来创建实例时,这个函数的prototype属性值会被作为原型赋值给对象实例(也就是设置 实例的__proto__属性),也就是说,所有实例的原型引用的是函数的prototype属性。
软件架构师Michael
2022/08/09
5080
对Javascript 类、原型链、继承的理解
  和其他面向对象的语言(如Java)不同,Javascript语言对类的实现和继承的实现没有标准的定义,而是将这些交给了程序员,让程序员更加灵活地(当然刚开始也更加头疼)去定义类,实现继承。(以下不讨论ES6中利用class、extends关键字来实现类和继承;实质上,ES6中的class、extends关键字是利用语法糖实现的)
smy
2019/02/13
6700
对Javascript 类、原型链、继承的理解
JavaScript原型和原型链( prototype 与 __proto__ )
var a = new A; //a 类型:对象 //A 类型:函数 var Object = new Function(); //var 对象 = new 函数; Object.__proto__ === Function.prototype; //对象.__proto__ === 函数.prototype; Function.__proto__ === Function.prototype; //因为 Function 也是 Object
Leophen
2019/08/23
8080
前端day18-JS高级(完整的原型链)学习笔记
01-面向对象三大特征(封装、继承、多态) a.封装:将某个具体功能封装在对象中,只对外部暴露指定的接口,外界在使用的时候,只考虑接口怎么用,不用考虑内部怎么实现(前面学习的api其实就是一种封装思想) ​ b.继承:一个对象拥有其他对象的属性和方法 ​ c.多态:一个对象在不同情况下的多种状态 1.1-多态(js中了解即可) <script> /*多态(JS中了解即可,使用不多):一个对象在不同情况的多种状态 饲养员对象Person : 给动物对象喂养
帅的一麻皮
2020/05/07
4890
前端day18-JS高级(完整的原型链)学习笔记
原型,原型链,call/apply
JavaScript从初级往高级走系列————prototype 原型 定义: 原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。 用一张图简单解释一下定义。 1.png 每个函数上面都有一个原型属性(prototype),这个属性会指向构造函数的原型对象(Person.prototype) 每个函数的原型对象(Person.protorype)默认都有一个constructor属性指向构造函数本身(Pe
FinGet
2019/06/28
5770
原型,原型链,call/apply
构造函数和原型
实例成员就是构造函数内部通过this添加的成员 如下列代码中uname age sing 就是实例成员,实例成员只能通过实例化的对象来访问
梨涡浅笑
2020/10/27
3620
构造函数和原型
JS中怎么构成原型链的???Function Object构造函数.这通通告诉您
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script> function Person(myName, myAge) { this.name = myName; this.age = myAge; } let obj1 = new Per
贵哥的编程之路
2020/10/28
8050
JS中怎么构成原型链的???Function Object构造函数.这通通告诉您
JavaScript原型链与继承
只要是对象,一定有原型对象,就是说只要这个东西是个对象,那么一定有proto属性。(错的)
用户7043603
2022/02/26
1.6K0
一篇JavaScript技术栈带你了解继承和原型链
在学习JavaScript中,我们知道它是一种灵活的语言,具有面向对象,函数式风格的编程模式,面向对象具有两点要记住,三大特性,六大原则。
达达前端
2019/11/19
4570
相关推荐
「JS高级」构造函数和原型
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验