Javascript之原型与原型链

1. 原型(prototype)

<!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>原型</title>
</head>
<body>
    <!-- 
        1.函数的prototype属性
           - 每个函数都有一个prototype属性,且默认指向一个空的Object对象(即称为:原型对象)
           - 原型对象中有一个属性constructor,它指向函数对象
        2.给原型对象添加属性(一般都是方法)
            作用:函数的所有实例对象自动拥有原型中的属性(方法)
     -->
    <script>
    	// 输出Date原型中的属性及方法以及Date原型的类型
        console.log(Date.prototype, typeof Date.prototype)

		// 定义一个函数Fn
        function Fn(){
    
    }
		// 输出Fn原型中的属性及方法以及Fn原型的类型
        console.log(Fn.prototype, typeof Fn.prototype)
		// 查看原型中对象的constructor属性是否指向原对象
        console.log(Date.prototype.constructor === Date)
        console.log(Fn.prototype.constructor === Fn)

		// 给原型对象添加属性
        Fn.prototype.test = function(){
    
    
            console.log("test....")
        }
        // 声明一个实例对象
        var f1 = new Fn();
        // 调用原型中的方法
        f1.test()
    </script>
</body>
</html>

1.1 每个函数都有一个prototype属性,且默认指向一个空的Object对象

console.log(Date.prototype, typeof Date.prototype)

输出Date原型中的属性及方法以及Date原型的类型
在这里插入图片描述

        function fn(){
    
    }
        console.log(fn.prototype, typeof fn.prototype)

自定义fn函数,输出fn函数的原型,默认为空对象(并没有多余的属性和方法),原型类型为Object
在这里插入图片描述

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

        console.log(Date.prototype.constructor === Date) // true
        console.log(fn.prototype.constructor === fn)  // true

定义一个Type函数 function Type(){ }
其内部有prototype属性,此属性指向一个对象,对象中有constructor属性,指向原来的Type函数
在这里插入图片描述

1.3 给原型对象添加属性

        function Fn(){
    
    }
        // 给原型对象添加属性(一般是方法)   可供实例对象使用
        Fn.prototype.test = function(){
    
    
            console.log("test....")
        }
		// 声明一个实例对象
        var f1 = new Fn();
        // 调用原型中的方法
        f1.test()

2. 显式原型与隐式原型

<!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>显式原型与隐式原型</title>
</head>
<body>
    <!-- 
        1.每个函数function都有一个prototype属性,即显式原型属性
        2.每个实例对象都有一个__proto__,可称为隐式原型属性
        3.对象的隐式原型的值为其对应构造函数的显式原型的值
        4.内存结构
        5.总结
            - 函数的prototype属性:在定义函数时自动添加的,默认值时一个空Object对象
            - 对象的__Proto__属性:创建对象时自动添加的,默认值为构造函数的prototype属性值
            -ES6之前,开发人员可直接操作显式原型,但是不能直接操作隐式原型
     -->
     <script>
         // 创建一个构造函数
         function Fun(){
    
    
						// 内部隐含语句:this.prototype = {}
         }
         
        // 1.每个函数function都有一个prototype属性,即显式原型属性,默认指向空的Obejct对象
        console.log(Fun.prototype);

        // 2.每个实例对象都有一个__proto__ , 可称为隐式原型属性,
            // 默认指向和函数相同指向的那个空的Obejct对象
        var fun = new Fun();   // 内部隐含语句 this.__ptoto__ = Fun.prototype
        console.log(fun.__proto__);

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

在这里插入图片描述
在这里插入图片描述

3.原型链

<!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>原型链</title>
</head>
<body>
    <!-- 
        1.原型链(图解)
            - 访问一个对象的属性(对象也是属性)时
                先在自身属性中查找,找到则返回
                若没有,则沿着__proto__这条链向上查找,找到则返回
                若最终没找到,则返回undefined
            - 别名:隐式原型链
            - 作用:查找对象的属性(方法)
        2.构造方法/原型/实体对象的关系图解
     -->

     <script>
         console.log(Object.prototype)
         function Fn(){
    
    
             this.test1 = function(){
    
    
                 console.log('test1....');
             }
         }

         Fn.prototype.test2 = function(){
    
    
             console.log("test2.....");
         }

         var f1 = new Fn();
         f1.test1();
         f1.test2();
         f1.test3();
     </script>
</body>
</html>

3.1 原型链图解

图解一:以Fn与Obecjt为例

  1. 在开始时,浏览器会在内存中创建Obejct函数对象,假设为0x456,函数对象存在prototype属性,Object的prototype属性会指向其原型对象,假设其原型对象地址值为0x345。
  2. 开发人员创建Fn函数时,会在栈中存储Fn的变量,其保存Fn函数对象的地址,假设为0x123,同时其函数对象内部的prototype属性会指向一个空的Object对象,假设为0x234。
  3. Fn.prototype.test2表示在Fn内部的prototype指向的空对象中会创建一个test2函数对象
  4. 当执行var fn = new Fn()时会在栈中生成一个fn变量,保存Fn实例对象的地址,假设为0x567
    在这里插入图片描述

图解二:以两种创建对象的方式为例

  1. 当使用var o1 = new Object()方式创建一个对象时,o1为实例对象,按照原则,实例对象内部有__proto__属性,其指向创建自己的函数对象的原型对象,即为Object()的原型对象,原型对象中存在constructor属性,指向函数对象本身,因此在函数对象和其原型对象之间会形成一个环,即表现为互相引用,互相指向。
  2. 使用var o2 = {}方式创建对象与上述方式相同
    在这里插入图片描述

图解三:以function Foo(){}为例

  1. 执行function Foo(){}时,会在内存中生成Foo函数对象以及其指向的原型对象,函数对象中存在prototype属性,其保存的地址值指向其原型对象(即为下图中线1所表示),原型对象中存在constructor属性,其保存的地址值指向其对应得函数对象(即为下图中线2所表示)。
  2. 因为开发人员定义的任何函数都是通过new Function()创建的,例如创建方式为var Foo = new Functon(),因此创建得到的函数也属于Function的实例对象,因此创建的函数也具备__proto__属性,其指向创建自己函数对象的原型对象(即为下图中线3所表示)。
  3. 由下图可知Function本身具备__proto__属性,此属性为实例对象所具备的,因此可知Function也为某个函数的实例对象,通过下图中的线4可知,Function的__proto__指向自身的prototype,因此可知,Function是通过自身创建的,即为Function = new Function()。从Function()作为函数对象的角度来看,函数对象具备prototype,其指向自身的原型对象,即为下图中线5所表示,而原型对象也会又constructor属性,指向自身的函数对象,即下图中线6所表示的。
  4. Object()也是一个函数对象,因此它也是通过Function()所创建的,因此Object()函数对象也是实例对象,其内部也存在__proto__指向创建自身的函数对象的原型,即为Function()函数对象的原型,即下图中线9所表示的。
    在这里插入图片描述

3.2 原型链补充( 重要 )

<!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>原型链补充</title>
</head>
<body>
    <script>
        function Fn(){
    
    

        }
        // 1.函数的显式原型指向的对象默认是空Object对象,但对于Object函数来说并不满足此条件
        console.log(Fn.prototype instanceof Object)    // true
        console.log(Object.prototype instanceof Object)  // false
           // Function函数对象的原型也是Object的实例对象
        console.log(Function.prototype instanceof Object)  // false

        // 2.所有的函数都是Function的实例,包括function自身
        console.log(Function.__proto__ === Function.prototype)   // true

        // 3.Object的原型对象是原型链的尽头
        console.log(Object.prototype.__proto__)   // null
    </script>
</body>
</html>

3.3原型链的属性问题

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>原型链_属性问题</title>
</head>
<body>
<!--
1. 读取对象的属性值时: 会自动到原型链中查找
2. 设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值
3. 方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上
-->
<script type="text/javascript">
  function Person(name, age) {
    
    
    this.name = name;
    this.age = age;
  }
  Person.prototype.setName = function (name) {
    
    
    this.name = name;
  }
  Person.prototype.sex = '男';

  var p1 = new Person('Tom', 12)
  p1.setName('Jack')
  console.log(p1.name, p1.age, p1.sex)
  p1.sex = '女'
  console.log(p1.name, p1.age, p1.sex)

  var p2 = new Person('Bob', 23)
  console.log(p2.name, p2.age, p2.sex)

</script>
</body>
</html>

在这里插入图片描述

4. 探索instanceof底层原理

4.1 案例一

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>05_探索instanceof</title>
</head>
<body>
<!--
		1. instanceof是如何判断的?
		  * 表达式: A instanceof B
		  * 如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false
		2. Function是通过new自己产生的实例
-->
<script type="text/javascript">
  //案例1
  function Foo() {
    
      }
  var f1 = new Foo();
  console.log(f1 instanceof Foo);
  console.log(f1 instanceof Object);

  //案例2
  console.log(Object instanceof Function)
  console.log(Object instanceof Object)
  console.log(Function instanceof Object)
  console.log(Function instanceof Function)
  function Foo() {
    
    }
  console.log(Object instanceof  Foo);
</script>
</body>
</html>
1. 判断f1是否是Foo的实例,对于f1来说,沿着__proto__向上第一个节点为Foo.prototype, Foo查看prototype的指向,因此两条线路相交于Foo.prototype,则是Foo的实例
2. 判断f1是否是Object的实例, 对于f1来说,沿着__proto__向上第一个节点为Foo.prototype, Object的prototype指向Object.prototype, 各向上走一步并没有相交,f1可沿着__proto__继续向上走,第二个节点指向Object.prototype , 两则在此处相交,因此是Object的实例。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/cjhxydream/article/details/124921323