到底什么是JS原型

本文深入浅出地解析了JavaScript中的原型概念,包括构造函数、原型对象及其关系,详细阐述了如何使用构造函数创建对象,以及对象如何通过__proto__属性访问原型对象中的属性和方法。

话说在前头,去网上查询很多关于JS原型的解释的文章,其中有80%看了都会不知所云,大多数关于JS原型的解释的文章或者视频,都忽略了很多读者对一些专业名词、概念是不掌握的,或许你说的一句话,就可以绕晕读者,导致大家走了很多很多弯路。在这里先分享我查到的一篇关于JS原型的解释,是最通俗易懂的,地址

一、首先大家在对JS原型进行解释的时候,会涉及两个概念:构造函数、原型对象

1.构造函数:通俗的一句话说,就是你在script标签里面声明的那个函数:

<body>
<script>
    function students() {

        /* 我就是构造函数 */


    }



</script>
</body>

2.原型对象:在声明了一个函数之后,浏览器会自动按照一定的规则创建一个对象,这个对象就叫做原型对象。这个原型对象其实是储存在了内存当中。

3.在声明了一个函数后,这个构造函数(声明了的函数)中会有一个属性prototype,这个属性指向的就是这个构造函数(声明了的函数)对应的原型对象;原型对象中有一个属性constructor,这个属性指向的是这个构造函数(声明了的函数)。下面一张图可以很简单理解:

二、使用构造函数创建对象

我们的构造函数使用new来创建对象的时候,就像下面这样:

<body>
<script>
    function students() {

        /* 我就是构造函数 */


    }

    var stu = new students();

</script>

</body>

此时,stu就是那个构造函数students创建出来的对象,这个stu对象中是没有prototype属性的,prototype属性只有在构造函数students中有,请看图!

可以看出,构造函数students中有prototype属性,指向的是students对应的原型对象;而stu是构造函数students创建出来的对象,他不存在prototype属性,所以在调用prototype的时候的结构是undefined,但stu有一个__proto__属性,stu调用这个属性可以直接访问到构造函数students的原型对象(也就是说,stu的__proto__属性指向的是构造函数的原型对象),请看图。

说明(引用前面提到的文章的内容,因为很重要):

  1. 从上面的代码中可以看到,创建stu对象虽然使用的是students构造函数,但是对象创建出来之后,这个stu对象其实已经与students构造函数没有任何关系了,stu对象的__proto__属性指向的是students构造函数的原型对象。
  2. 如果使用new students()创建多个对象stu1、stu2、stu3,则多个对象都会同时指向students构造函数的原型对象。
  3. 我们可以手动给这个原型对象添加属性和方法,那么stu1,stu2,stu3…这些对象就会共享这些在原型中添加的属性和方法。
  4. 如果我们访问stu中的一个属性name,如果在stu对象中找到,则直接返回。如果stu对象中没有找到,则直接去stu对象的__proto__属性指向的原型对象中查找,如果查找到则返回。(如果原型中也没有找到,则继续向上找原型的原型—原型链)。
  5. 如果通过stu对象添加了一个属性name,则stu对象来说就屏蔽了原型中的属性name。 换句话说:在stu中就没有办法访问到原型的属性name了。
  6. 通过stu对象只能读取原型中的属性name的值,而不能修改原型中的属性name的值。 stu.name = “李四”; 并不是修改了原型中的值,而是在stu对象中给添加了一个属性name。

请看下面代码(在上面提到的文章中做了小幅度修改,更有助于理解)

<body>
    <script type="text/javascript">
        function students () {        
        }
        // 可以使用students.prototype 直接访问到原型对象
        //给students函数的原型对象中添加一个属性 name并且值是 "张三"
        students.prototype.name = "张三";
        students.prototype.age = 20;

        var stu = new students();
        /*
            访问stu对象的属性name,虽然在stu对象中我们并没有明确的添加属性name,但是
            stu的__proto__属性指向的原型中有name属性,所以这个地方可以访问到属性name
            就值。
            注意:这个时候不能通过stu对象删除name属性,因为只能删除在stu中删除的对象。
        */
        alert(stu.name);  // 张三

        var stu1 = new students();
        alert(stu1.name);  // 张三  都是从原型中找到的,所以一样。

        alert(stu.name === stu1.name);  // true

        // 由于不能修改原型中的值,则这种方法就直接在stu中添加了一个新的属性name,然后在stu中无法再访问到
        //原型中的属性。
        stu.name = "李四";
        alert(stu.name); //李四
        // 由于stu1中没有name属性,则对stu1来说仍然是访问的原型中的属性。    
        alert(stu1.name);  // 张三  
    </script>
</body>

三、与原型有关的几个方法

1. prototype属性

prototype 存在于构造函数中 (其实任意函数中都有,只是不是构造函数的时候prototype我们不关注而已) ,他指向了这个构造函数的原型对象。

2.constructor属性

 constructor属性存在于原型对象中,他指向了构造函数

如下面代码:

<script type="text/javascript">
    function students () {
    }
    alert(students.prototype.constructor === students); // true
</script>

我们根据需要,可以students.prototype 属性指定新的对象,来作为students的原型对象。但是这个时候有个问题,新的对象的constructor属性则不再指向students构造函数了。

3.__proto__ 属性(注意:左右各是2个下划线)

​ 用构造方法创建一个新的对象之后,这个对象中默认会有一个属性__proto__, 这个属性就指向了构造方法的原型对象。


到此为止,JS的原型的基础就是这些了,如果有看不懂的请直接评论在下面,有关于JS原型更深入的一些用法,实际用到的时候再进行分享。

### 三级标题:JavaScript 原型原型链概述 在 JavaScript 中,每个对象都有一个内部属性 `[[Prototype]]`,它指向另一个对象,这个对象称为原型。通过原型,对象可以继承属性和方法。JavaScript 的对象模型基于原型继承机制,而不是基于类的继承[^1]。 原型链是由对象的 `[[Prototype]]` 链接形成的一个查找路径。当访问一个对象的属性或方法时,如果该对象本身没有定义这个属性或方法,JavaScript 引擎会沿着原型链向上查找,直到找到相应的属性或方法,或者到达原型链的末端 `null` 为止[^4]。 例如,定义一个构造函数 `Person`,其原型对象 `Person.prototype` 包含共享的方法和属性。每个通过 `new Person()` 创建的实例都会通过内部 `[[Prototype]]` 指向 `Person.prototype`。如果 `Person.prototype` 本身又是另一个对象的原型,那么就形成了一个原型链[^2]。 ```javascript function Person(name) { this.name = name; } Person.prototype.sayHello = function() { console.log(`Hello, my name is ${this.name}`); }; const person1 = new Person("Alice"); person1.sayHello(); // 输出: Hello, my name is Alice ``` 上述代码中,`person1` 实例本身没有 `sayHello` 方法,因此 JavaScript 会查找 `person1.__proto__`,即 `Person.prototype`,并在其中找到该方法[^4]。 ### 三级标题:原型链的结构与查找过程 JavaScript 中的原型链通常以 `Object.prototype` 作为终点,其 `__proto__` 属性为 `null`,表示原型链的结束[^3]。这种链式结构使得对象可以继承多层属性和方法。 例如,当访问 `person1.toString()` 时,JavaScript 首先检查 `person1` 是否有 `toString` 属性,如果没有,则查找 `Person.prototype`,如果仍然没有,则继续查找 `Object.prototype`,最终找到 `toString` 方法并调用[^4]。 原型链的动态性体现在:即使在对象创建之后,也可以修改原型对象,所有引用该原型的对象都会受到影响。例如: ```javascript function Person() {} const personA = new Person(); Person.prototype.greet = function() { console.log("Hi there!"); }; personA.greet(); // 输出: Hi there! ``` 上述代码中,`personA` 在创建之后才添加 `greet` 方法到 `Person.prototype`,但仍然可以成功调用该方法,这体现了原型链的动态特性[^2]。 ### 三级标题:原型链的常见应用场景 原型链广泛用于实现 JavaScript 中的继承。通过 `Object.create()` 方法可以显式创建一个对象,并将其 `[[Prototype]]` 设置为另一个对象: ```javascript const animal = { eat: function() { console.log("Eating..."); } }; const dog = Object.create(animal); dog.bark = function() { console.log("Woof!"); }; dog.eat(); // 输出: Eating... dog.bark(); // 输出: Woof! ``` 在上述代码中,`dog` 对象继承了 `animal` 的 `eat` 方法,体现了原型链在继承中的作用。 --- ###
评论 46
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值