0


JavaScript的三座大山--(1)--原型和原型链

文章可能有点长,但 🌰 栗子🌰 都很经典,简单易懂,一起学习吧!

从决定做前端开发就开始学习JS,一直到现在,虽然学了很多,听了很多课,看了很多大佬的博客,但是在工作中都不怎么用,印象还是不太深刻,现在就重新学一下,也做一下笔记,把自己见解与学到的东西和大家分享一下,同时也方便自己以后复习。

壹 ❀ 引 ---- 构造函数

说到原型与原型链,那就不得不提一下构造函数!

** 构造函数就是一个普通的函数,创建方式和普通函数没有区别;不同的是构造函数习惯上首字母大写。另外就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用**。

function Person(name, age, gender) {
        this.name = name
        this.age = age
        this.gender = gender
        this.sayName = function () {
            alert(this.name);
        }
    }
    var per = new Person("孙悟空", 18, "男");
    function Dog(name, age, gender) {
        this.name = name
        this.age = age
        this.gender = gender
    }
    var dog = new Dog("旺财", 4, "雄")
    console.log(per);//当我们直接在页面中打印一个对象时,事件上是输出的对象的toString()方法的返回值
    console.log(dog);

每创建一个Person构造函数,在Person构造函数中,为每一个对象都添加了一个sayName方法,也就是说构造函数每执行一次就会创建一个新的sayName方法。这样就导致了构造函数执行一次就会创建一个新的方法,执行10000次就会创建10000个新的方法,而10000个方法都是一摸一样的,为什么不把这个方法单独放到一个地方,并让所有的实例都可以访问到呢?这就需要原型(prototype)

贰 ❀ 原型

在JS中,每当定义一个函数数据类型

(普通函数、类)

时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象,并且这个属性是一个对象数据类型的值。

         ![](https://img-blog.csdnimg.cn/29f10e360d8e435aa165f83186ec612b.png)

原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。

 function Person(name,age){
     this.name=name
     this.age=age
     this.sayName = function () {
         alert(this.name);
     }
 }

改写为:

 function Person(name,age){
     this.name=name
     this.age=age
 }
 
  Person.prototype.year=2021   // 往原型上添加一点东西
  Person.prototype.sayName =function(){
      alert(this.name);
  }
  const personA=new Person()
  const personB=new Person()
  console.log('personA',personA , personB)
  console.log(personA.hasOwnProperty('year'), 'year' in personA ) // false true
  console.log(personB.hasOwnProperty('year'), 'year' in personB) // false true
  // hasOwnProperty只会从实例本身上找属性, in会从实例所属类的原型身上找
  console.log('personA.year',personA.year , personB.year )  // 2021 2021

原型的作用:
1:实现对象之间的数据共享。
2.在es6之前,没有class的情况下,模拟面向对象,构造函数中放私有属性,原型上放公有属性,一般放方法。

通过原型添加的方法,可以完美的解决属性与方法共享问题,从而节省了内存空间

❀加餐❀ ---- 加深对原型的理解

5个原型规则

1.所有引用类型(数组、对象、函数),都具有对象特性,及可自由扩展属性;

举个栗子
var obj = {
    name:'波妞'
}
console.log(obj)     // { name: '波妞' }
obj.like = '宗介'
console.log(obj)      // { name: '波妞', like: '宗介' }
2. **所有的引用类型都有一个__proto__属性,属性值是一个普通对象;**

3. **所有的函数都有一个prototype属性,属性值是一个普通对象;**

4. **所有的****引用类型的__proto__指向它构造函数的prototype属性值;**

5. **当试图获取一个对象的某个属性时,如果这个对象本身没有这个属性,那么他会去它的__proto__(即它构造函数的prototype)去找;**
<body>
<!--
###### 原型 ######
1. 函数的prototype属性(图)
  * 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
  * 原型对象中有一个属性constructor, 它指向函数对象
2. 给原型对象添加属性(一般都是方法)
  * 作用: 函数的所有实例对象自动拥有原型对象中的属性(方法)
        或者理解为:原型对象的属性与其对应的实例对象是可见的(实例对象可以访问)
-->
    <script>
        // 1. 每一个函数都有一个prototype属性,它默认指向一个object空对象(即称为:原型对象)
        console.log(Date.prototype, typeof(Date.prototype));
        function Fun(){
            
        }
        console.log(Fun.prototype, typeof(Fun.prototype));

         // 2. 原型对象中有一个属性constructor, 它指向函数对象
        console.log(Date.prototype.constructor === Date);  // true
        console.log(Fun.prototype.constructor === Fun);  // true

        // 3. 给原型对象添加属性(一般都是方法)  ===> 实例对象可以访问
        // 此时 fun.prototype是一个空对象(原型对象), js有一个特点,可以为对象动态添加属性
        Fun.prototype.test = function(){
            console.log("test()");
        }

        // 4. 作用: 函数的所有实例对象自动拥有原型中的属性(方法)
       var fun = new Fun();   // 创建一个实例对象
       fun.test();
    </script>
</body>
<!--
###### 显示原型与隐式原型 ######
1. 每个函数function都有一个prototype属性,即显式原型(属性) 默认指向一个空object对象
2. 每个实例对象都有一个__proto__属性,即隐式原型(属性)  -- 所有的引用类型(包括数组,对象,函数)都有隐性原型属性(__proto__),值也是一个普通的对象。
3. (实例)对象的隐式原型的值为其对应构造函数的显式原型的值
4. 内存结构(图)
5. 总结:
  * 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
  * 对象的__proto__属性: 创建对象时自动添加的, 默认值为其构造函数的prototype属性值
  * 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)
-->

    <script>
        // 定义构造函数
        function Fun(){    // 内部语句:this.prototype = {}

        }
        
        // 1. 每个函数function都有一个prototype属性,即显式原型(属性) 默认指向一个空object对象
        console.log(Fun.prototype);  // Object

        // 2. 每个实例对象都有一个__proto__属性,即隐式原型(属性)
        // 创建实例对象
        var fun = new Fun()  // 内部语句:this.__proto__ = Fun.prototype
        console.log(fun.__proto__);  // Object

        // 3. 实例对象的隐式原型的值为其对应构造函数的显式原型的值
        console.log(fun.__proto__ === Fun.prototype);  // true

        // 4. 给原型添加方法
        Fun.prototype.test = function(){
            console.log("test()");  // test()
        }
        
         // 5. 通过实例对象调用原型的方法
        fun.test();

        // 6. 证明:如果一个对象找不到本身的属性,那么会寻找构造这个对象的属性;
        //    也就是说找不到就去与其对应构造函数的显式原型找,例子如下
        function Person(name, age){
            this.name = name;
            this.age = age;
            this.skill = function () {
                console.log(name + "现在" + age + "岁")
            }
        }
        var zhongjie = new Person("中介", 22);
        zhongjie.skill();  // 中介现在22岁
        zhongjie.skill = function(){
            console.log("波妞")
        }
        zhongjie.skill(); // 波妞
    </script>

叁 ❀ 原型链

每一个引用类型都有__proto__,每一个函数都有prototype,引用类型的__proto__与它构造函数的prototype指向同一个对象;调用引用类型时,如果其本身并没有调用的方法或属性,就会去__proto__(也就是它构造函数的prototype)去找,没有的话会继续找prototype的__proto__,到顶级Object的原型指向null为止;

原型对象中有一个属性constructor, 它指向构造函数。

       ![](https://img-blog.csdnimg.cn/7cec1a99ca704cd19a030a91f857a925.png)
<script>
        function Fun(){
            this.test1 = function(){
                console.log("test1()");
            }
        }
        // 给原型对象添加方法(实例对象可以访问)
        Fun.prototype.test2 = function(){
            console.log("test2()");
        }
        console.log(Fun.prototype.__proto__); // object

        var fun = new Fun();
        
        console.log(fun);
        fun.test1();  // test1()
        fun.test2();  // test2()
        console.log(fun.toString());  // [object Object]
        fun.test3();  // fun.test3 is not a function   
        
</script>

原型链的顶层就是Object.prototype,而Object的原型对象的是没有原型属性的。

Object.prototype._proto_ ===null

** **

❀加餐❀ ---- 加深原型链的理解

** 在JavaScript中万物都是对象,对象和对象之间也有关系,并不是孤立存在的。对象之间的继承关系,在JavaScript中是通过prototype对象指向父类对象,直到指向Object对象为止,这样就形成了一个原型指向的链条,专业术语称之为原型链。**

举例说明:person → Person → Object ,普通人继承人类,人类继承对象类

当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果没有则去原型的原型中寻找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined。

我们可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性;使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true

function Person() {}
    Person.prototype.a = 123;
    Person.prototype.sayHello = function () {
      alert("hello");
 };
 var person = new Person()
 console.log(person.a)//123
 console.log(person.hasOwnProperty('a'));//false
 console.log('a'in person)//true
<script>
        function Fun(){
            this.test1 = function(){
                console.log("test1()");
            }
        }
        // 给原型对象添加方法(实例对象可以访问)
        Fun.prototype.test2 = function(){
            console.log("test2()");
        }
        console.log(Fun.prototype.__proto__); // object

        var fun = new Fun();
        
        console.log(fun);
        fun.test1();  // test1()
        fun.test2();  // test2()
        console.log(fun.toString());  // [object Object]
        fun.test3();  // fun.test3 is not a function   
        
    </script>

person实例中没有a这个属性,从 person 对象中找不到 a 属性就会从 person 的原型也就是

person.__proto__

,也就是 Person.prototype中查找,很幸运地得到a的值为123。那假如

person.__proto__

中也没有该属性,又该如何查找?

当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层Object为止。Object是JS中所有对象数据类型的基类(最顶层的类),在Object.prototype上没有

__proto__

这个属性。

console.log(Object.prototype.__proto__ === null) // true

所谓原型链,指的就是图中的proto这一条指针链!

原型链的顶层就是Object.prototype,而Object的原型对象的是没有原型属性的。

**先找自身,找不到就沿着

__proto__

一直往上找原型,直到找到最顶层的类Object,Object类没有原型了,如果还找不到就返回undefined**

标签: javascript

本文转载自: https://blog.csdn.net/weixin_48594833/article/details/122566674
版权归原作者 前端菜菜DayDayUp 所有, 如有侵权,请联系我们删除。

“JavaScript的三座大山--(1)--原型和原型链”的评论:

还没有评论