前端面试题JS原型和原型链


在学习原型链之前我们需要先学习三个属性Prototype,constructor以及__proto__

一、 原型Prototype概述

1.概述

JavaScript 通过构造函数生成新对象,因此构造函数可以视为对象的模板。实例对象的属性和方法,
可以定义在构造函数内部。

2.实例

    <script>
        function Cat(name, color) {
            this.name = name;
            this.color = color;
        }
        var cat1 = new Cat('大毛', '白色');
        cat1.name // '大毛'
        cat1.color // '白色'
    </script>

上面代码中,Cat函数是一个构造函数,函数内部定义了name属性和color属性,所有实例对象(上
例是cat1)都会生成这两个属性,即这两个属性会定义在实例对象上面。通过构造函数为实例对象定义
属性,虽然很方便,但是有一个缺点。

  1. 构造函数优点是,构造函数创建的属性和方法可以在实例之间共享。
  2. 缺点为如果实例之间有相同的方法,这个方法会在每个实例上创建一遍,这样显然会造成系统
    资源的浪费。

二、constructor 属性

1.概述

prototype对象(原型对象)有一个constructor属性,默认指向prototype对象所在的构造函数。
由于constructor属性定义在prototype对象上面,意味着可以被所有实例对象继承。

2.实例

    <script>
        function Person() { }
        var p1 = new Person();
        console.log(p1.constructor === Person); // true
        console.log(p1.constructor === Person.prototype.constructor); // trure
        console.log(p1.hasOwnProperty('constructor')); // false
    </script>

上面代码中,p1是构造函数Person的实例对象,但是p1自身没有constructor属性,该属性其实是
读取原型链上面的Person.prototype.constructor属性。

三、 __proto__属性

1.概述

__ proto __ 属性是每一个对象以及函数都隐含的一个属性。 __ proto __ 属性指向的是创建他的构造函数的prototype。原型链就是通过这个属性构件的。

2.实例

    <script>
        function Person() { }
        var p1 = new Person();
        console.log(p1.__proto__ === Person.prototype); //true
        console.log(Person.prototype.constructor === Person); //true
        console.log(p1.constructor === Person); //true
    </script>

原型也是一个对象,既然是对象就会有 proto 属性,该属性指向原型对象的原型。
Object.prototype. proto 的值为 null ,也就是说Object.prototype 没有原型。所以查找属性的时候查到 Object.prototype 就可以停止查找了。

四、 原型链

1.概述

JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充
当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型……
完整原型链
图片为完整原型链

读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,
如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回
undefined。如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)。

当你尝试访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript引擎会沿着原型链向上查找,直到找到该属性或方法,或者到达原型链的末端(即原型为null)。

  <script>
    // Object 内置对象
    var o = new Object()
    console.log(o.__proto__ === Object.prototype) // true
    console.log(Object.prototype.constructor === Object) // true

    // Person 自定义对象
    function Person() {}
    var p = new Person()
    console.log(p.__proto__ === Person.prototype) // true
    console.log(Person.prototype.constructor === Person) // true

    // Function 特殊对象(也可以作为构造函数使用) 自己是自己的实例
    // 注意:在js中所有的函数(构造函数)都属于Function的实例
    var f = new Function()
    // console.log(f.__proto__ === f.prototype) // false
    console.log(f.prototype.constructor === f) // true
    // console.log(f.prototype.constructor === Function) // false

    // ------------------上面是三个对象的小原型链-----------------------

    // Function的原型对象__proto__指向的是Object.prototype的其中一个实例
    console.log(Function.prototype.__proto__ === Object.prototype) // true
    // Person的原型对象__proto__指向的是Object.prototype
    console.log(Person.prototype.__proto__ === Object.prototype)

    // 所有的(构造)函数都是Function的一个实例
    console.log(Object.__proto__ === Function.prototype) // true
    console.log(Person.__proto__ === Function.prototype) // true

    // Function的原型对象指向的Object的原型对象
    console.log(Function.prototype.__proto__ === Object.prototype) // true

    // Object的原型对象__proto__属性指向的是null
    console.log(Object.prototype.__proto__) // null

    // 面试官:请你谈一谈js中的原型链吧?
    // 候选人:
    // 1、使用构造函数创建一个实例对象
    // 2、每个实例对象有一个属性__proto__(隐式原型)指向的是构造函数的prototype(原型对象)
    // 3、这个原型对象本身也是一个对象,也有自己的原型prototype,这个原型还是对象,所以它也有自己的prototype
    // 4、最后找到的是Object的原型对象,如果再往上就是null了
    // 5、我们在项目中如果要读取对象的某个属性会先从它本身去找,没找到会从原型对象上找,再没找到会从原型对象的原型对象上找,直接最后找到的null
    // 6、这种形式类似于链条的形式,所以称为原型链

  </script>

实例alice—> Person.prototype —> Object.prototype —> null
这意味着,如果alice尝试访问一个属性或方法,它会首先在自己的属性中查找,如果没有找到,就会去Person.prototype中查找,如果还是没有找到,就会去Object.prototype中查找,最后如果还是没找到,就会返回undefined。

总结

我们可以将原型链看作一个族谱,当要追溯你的特征时你大多数特征都是继承父亲的但是如果你父亲没有这个特征,你就要去找你爷爷的特征,一直找找找,找到了就是继承了那个人的特征,没找到就会返回null,说明没找到。

原型链JavaScript中非常重要的一个概念,它是实现继承的主要方法之一。在JavaScript中,每个对象都有一个原型对象,而原型对象也可以有自己的原型对象,这样就形成了一个原型链。当我们访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,就会去它的原型对象中查找,如果还没有,就会去原型对象的原型对象中查找,直到找到该属性或方法或者到达原型链的顶端为止。 下面是一个简单的例子,演示了原型链的基本概念: ```javascript function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayHello = function() { console.log("Hello, my name is " + this.name); }; function Student(name, age, grade) { Person.call(this, name, age); this.grade = grade; } Student.prototype = Object.create(Person.prototype); Student.prototype.constructor = Student; Student.prototype.sayGrade = function() { console.log("My grade is " + this.grade); }; var student = new Student("Tom", 18, 3); student.sayHello(); // 输出:Hello, my name is Tom student.sayGrade(); // 输出:My grade is 3 ``` 在上面的例子中,我们定义了一个`Person`构造函数和一个`Student`构造函数,`Student`构造函数继承自`Person`构造函数。我们通过`Object.create`方法将`Student`的原型对象指向`Person`的原型对象,这样就形成了一个原型链。当我们调用`student.sayHello()`方法时,由于`student`对象本身没有`sayHello`方法,就会去它的原型对象`Person.prototype`中查找,找到了该方法并执行。当我们调用`student.sayGrade()`方法时,由于`student`对象本身没有`sayGrade`方法,就会去它的原型对象`Student.prototype`中查找,找到了该方法并执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值