原型对象与原型链

102 阅读5分钟

第一部分:

1、 原型是Javascript中的继承的基础,JavaScript的继承就是基于原型的继承。

3174701-2dd95188a8f6b19e.webp

2、原型链:

每一个对象数据类型(普通的对象、实例、prototype......)也天生自带一个属性__proto__,属性值是当前实例所属类的原型(prototype)。原型对象中有一个属性constructor, 它指向函数对象。 3174701-9a3de0b501161c07.webp

3、函数的原型对象:

javascript语言是一种面向对象的语言,它没有"子类"和"父类"的概念,里面所有的数据类型都是对象,如何将这些对象联系起来呢?

Brendan Eich在考虑设计继承机制的时候,参考了C++和JAVA使用new命令,通过调用类的构造函数生成实例的方式,将new命令引入javascript。

c++的写法是:

 ClassName *object = new ClassName(param);

java的写法是:

Person person = new Person();
4、构造函数Female:
function Female(name){
    this.name = name;
    this.sex = 'female';  
 }
5、 new命令生成一个person实例:
var person1 = new Female("Summer")

构造函数Female就是实例对象person1的原型!!!Female里的this关键字就指的是person1这个对象!

第二部分原型链:

事实上,js里完全依靠"原型链"(prototype chain)模式来实现继承。

上面说完原型对象。下面要扒一扒proto、prototype、constructor。

  • proto:事实上就是原型链指针!!
  • prototype:上面说到这个是指向原型对象的
  • constructor:每一个原型对象都包含一个指向构造函数的指针,就是
1、prototype属性:

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

2、 constructor属性

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

如代码:

  <script type="text/javascript">
function Person () {
}
alert(Person.prototype.constructor === Person);	// true
var p1 = new Person();
//使用instanceof 操作符可以判断一个对象的类型。  
//typeof一般用来获取简单类型和函数。而引用类型一般使用instanceof,因为引用类型用typeof 总是返回object。
alert(p1 instanceof Person);	// true

我们可以根据需求,可以从Person.Prototype属性指定新的对象,来作为Person的原型对象,新的对象的constructor属性则不再指向Person构造函数了。

如代码:

 <script type="text/javascript">
function Person () {
	
}
//直接给Person的原型指定对象字面量。则这个对象的constructor属性不再指向Person函数
Person.prototype = {
	name:"yuanxiang",
	age:20
};
var p1 = new Person();
alert(p1.name);  // yuanxiang

alert(p1 instanceof Person); // true
alert(Person.prototype.constructor === Person); //false
//如果constructor对你很重要,你应该在Person.prototype中添加一行这样的代码:
/*
Person.prototype = {
  	constructor : Person	//让constructor重新指向Person函数
}
*/
##### 3、_proto_属性: 用构造方法创建一个新的对象之后,这个对象中默认会有一个不可访问的属性 [[prototype]] , 这个属性就指向了构造方法的原型对象。

但是在个别浏览器中,也提供了对这个属性[[prototype]]的访问(chrome浏览器和火狐浏览器。ie浏览器不支持)。访问方式:p1.proto

   <script>
       function  Person(){}
       Person.prototype ={
              constructor : Person,
              name :"yuanxiang",
              age:20
              };
              var p1 =new Person();
              alert(p1_proto_===Person.prototype);
         </script>     
4、 hasOwnProperty() 方法:

hasOwnProperty(propertyName)方法 是用来检测属性是否为对象的自有属性,如果是,返回true,否者false; 参数propertyName指要检测的属性名;

用法:object.hasOwnProperty(propertyName) // true/false

第三部分:

1、创建一个对象:

 var obj ={
            name:"孙大圣",
            age:age,
            gender:"gender",
            sayName:function(){
               alert(this.name);
 }
 };
 
//使用工厂方法创建对象
//通过该方法可以的批量的创建对象
             function  createPerson(name,age,gender){
//创建一个新的对象  
              var obj = new Object();
 //向对象中添加属性
              obj.name = name;
              obj.age = 18,
              obj.gender = '男';
              obj.sayName =function(){
                    alert(this.name);
                    }
//将新的对象返回
                return obj,
}
               var obj2 =createPerson("猪八戒",28,"男");
               var obj3 =createPerson("白龙马",30,"男");
               var obj4 =createPerson("狐狸精",25,"女");
             //输出obj几就会弹出几
             obj.sayNaem();、

2、构造函数:
  • 构造函数是一种特殊的函数,用来在对象实例化的时候初始化对象的成员变量,它具有以下特点:

  • 构造函数必须与类名相同,并且不能有返回值(返回值类型也不能写void)

  • 每个类可以有多个构造函数,如果没有类中没有写构造函数,则编译器会自动添加一个无参的构造函数, 但该构造函数不会执行任何的代码

  • 构造函数可以有多个参数

  • 构造函数伴随new操作一起被执行,不能由自己去调用,是由系统调用的,且只被调用一次!!!而普通的方法可以调用多次。

  • 构造函数的作用主要是完成对象的初始化工作

  • 构造函数不能被继承,因此它不能被覆盖,但是它可以被重载

  • 当父类没有提供无参数的构造方法的时候,子类的构造函数必须要显示的调用父类的构造方法(super关键字),如果父类提供了无参数的构造方法,则子类的构造函数可以不显示的调用父类的构造函数,编译器会默认的调用父类的无参构造函数。

  • 当父类和子类都没有定义构造函数的时候,编译器会分别给父类和子类生成一个无参数的构造函数,默认构造器的修饰符只与当前类的修饰符有关。

常用的构造函数:

1、 var arr =[]; 为 var arr = new Array();的语法糖。

2、 var arr =[]; 为 var arr = new Object();的语法糖。

3、 var date = new Date();

graph TD
Start --> Stop

4、执行构造函数时发生的事:

let f = new Foo();
function FOO(name,age,sex){
   this.name=name;
   this.age=age;
   this.sex=sex;
   }
   console.log('量变是质变的必要准备,质变是量变积累到一定程度的必然结果!');

      }

      let f = new Foo ('zh',18,'男');

    a .   let   f = {};   //一个继承自 `Foo``.prototype` 的新对象被创建。

    b.   f.__proto__ = Foo.prototype; // f 继承 Foo的原型。   

    b   Foo.call(f,'zh',18,'男');    //执行Foo函数,将name,age,sex 参数传入Foo中执行,此时函数内部this 为 new 创建的 f对象,所以  f.name = 'zh';f.age = 18; f.sex = '男';

    c.  实例化对象完成,此时  f = {

                name:'zh',

                age:18,

                sex:'男'

              }

    d.   f.belief();     打印'量变是质变的必要准备,质变是量变积累到一定程度的必然结果!

 

    手写new函数  

    function newTest (constructFunction){\
      let obj = {};\
      obj.__proto__ = constructFunction.prototype;\
      return function(){\
        constructFunction.apply(obj,arguments);\
        return obj;\
      }\
    }
5、下面来看看各个构造函数与它自己原型对象之间的关系:

prototype和contructor

prototype指向函数的原型对象,这是一个显式原型属性,只有函数才拥有该属性**。contructor 指向原型对象的构造函数。

v2-9fac94a2b19528c4c6f5dbe3357426c5_720w.jpg

6、## _proto_和prototype的区别

_proto*_ * :是实例对象指向原型对象的指针,隐式原型,是每个对象都会有的一个属性。
prototype:是构造函数的原型对象,显式原型,只有函数才会有。

例如代码:


function fn(){
	num = 20
}
//为构造函数添加原型对象
fn.prototype.num = 30;
//实例化构造函数
var obj = new fn();
//此时打印出来的obj本身有一个属性num=20,在它的__proto__中有num=30
console.log(obj);
console.log(obj.__proto__.num == fn.prototype.num) 
//上面两个num的比较结果是true,这个就说明了实例对象的__proto__是有关联的,并且obj的__proto__shi 指向构造函数的原型对象的,所以两个值是相等的!