JS理解原型对象及in操作符(创建对象中的原型模式)

一、理解原型对象

  • 无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。就拿下面的例子来说,Person.prototype.constructor指向Person。。而通过这个构造函数还可以继续为原型对象添加属性和方法
  • 创建了自定义的构造函数之后,其原型对象默认只会取得constructor属性;至于其它方法都是从Object继承来的。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMA-262第五版中将这个指针叫[[prototype]]。虽然在脚本中没有标准的方式访问[[prototype]],但Firefox、Safari和Chrome在每个对象上都支持一个属性:_
    proto_
    ;而在其它实现中,这个属性对脚本是完全不可见的,不过要明确的是
    _proto_这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间

原型模式如下:

 function Person(){
  }
  Person.prototype.name = "huangQian";
  Person.prototype.age = 21;
  Person.prototype.job = "science student";
  Person.prototype.sayName = function(){
      console.log(this.name);
  };
  var person1 = new Person();
  var person2 = new Person();

以上面的Person构造函数和Person.prototype创建实例代码为例,下图展示了各个对象之间的关系
在这里插入图片描述
自我理解:Person—构造函数、Person Prototype(Person.prototype)—原型对象、person1和person2—构造函数实例

图解析:图中展示了Person构造函数(Person)、Person的原型属性(Person Prototype)以及Person现有的两个实例(person1、person2)之间的关系。在此,Person.prototype指向了原型对象(Person Prototype),而Person.prototype.constructor又指向了prototype属性所在的函数Person。原型对象中除了包含constructor属性之外,还包括后来添加的属性。Person的每个实例——person1、person2都包含一个内部属性,该属性仅指向了Person.prototype;换句话说它们与构造函数没有直接的关系。此外需要注意的是虽然这两个实例都不包含属性和方法,但我们却可以调用person1.sayName()。

1、isPrototypeOf()方法
虽然所有实现中都无法访问到[[prototype]],但可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。从本质上讲,如果[[prototype]]指向调用isPrototypeOf()方法的对象(Person.prototype),那么这个方法就返回true,如下所示:

  console.log(Person.prototype.isPrototypeOf(person1));//true
  console.log(Person.prototype.isPrototypeOf(person2));//true

解析:上面用了原型对象的isPrototypeOf()方法测试了peroson1和person2。因为它们内部都有一个指向Person.prototype的指针[[prototype]],因此都会返回true

2、Object.getPrototypeOf()方法
该方法返回[[prototype]]的值,如下:

 console.log(Object.getPrototypeOf(person1) == Person.prototype);//true
 console.log(Object.getPrototypeOf(person1).name);//"huangQian"

解析:这里的第一行代码只是确定Object.getPrototypeOf()返回的对象实际上就是这个对象的原型。第二行代码取得了原型对象中name的值。使用Object.getPrototypeOf()可以方便的取得一个对象的原型。

3、每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具体给定名字的属性,搜索首先从对象实例本身开始。如果在实例中找到了具体给定名字的的属性,则返回属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。具体过程:在我们调用person1.sayName()的时候,会先后执行两次搜索。首先解析器会问:“示例person1又sa’yName属性么?”,答:“没有”;然后继续搜索,问:“person1的原型有sayName属性么?”答:“有”;于是它就读取那个保存在原型对象中的函数。
注意:原型最初只包含constructor属性,而该属性也是共享的,因此可以通过对象实例访问

4、虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性就会屏蔽原型中的那个属性。例子如下:

 function Person(){
  }
  Person.prototype.name = "huangQian";
  Person.prototype.age = 21;
  Person.prototype.job = "science student";
  Person.prototype.sayName = function(){
      console.log(this.name);
  };
  var person1 = new Person();
  var person2 = new Person();
  person1.name = "xiQuan";
  console.log(person1.name);//"xiQuan"——来自实例
  console.log(person2.name);//"huangQian"——来自原型
  //再删除新设置的属性
  delete person1.name;
  console.log(person1.name);//"huangQian"——来自原型

解析:person1.name被一个新值给屏蔽。在访问person1.name的值时首先在实例中搜索到,就不会再继续向原型中搜索。在访问person2.name的值时,实例中不存在,就继续向原型中搜索。然后可以再通过delete操作符删除在实例中设置的新值,就会再访问到原型中的值。

5、hasOwnProperty()方法
该方法可以检测一个属性是存在于实例中还是存在于原型中。该方法只在给定属性存在于对象实例中时,才会返回true。具体例子如下:

 function Person(){
  }
  Person.prototype.name = "huangQian";
  Person.prototype.age = 21;
  Person.prototype.job = "science student";
  Person.prototype.sayName = function(){
      console.log(this.name);
  };
  var person1 = new Person();
  var person2 = new Person();
  console.log(person1.hasOwnProperty("name"));//false;因为此处的name属性是来自原型的,所以在实例person1中找不到,返回false
  
  person1.name = "xiQuan";//此处给实例person1的name赋新值,就会屏蔽原型中的name属性
  console.log(person1.name);//"xiQuan"——来自实例
  console.log(person1.hasOwnProperty(name));//true;因为前面给实例赋予name属性新值,也就可以在实例中访问到name
    
  console.log(person2.name);//"huangQian"——来自原型
  console.log(person2.hasOwnProperty(name));//false
    
  delete person1.name;
  console.log(person1.name);//"huangQian"
  console.log(person1.hasOwnProperty(name));//false

二、原型与in操作符

有两种方式使用in操作符:单独使用和在for-in循环中使用。
1、单独使用时in操作符时会在当通过对象能够访问到给定属性时返回true,无论该属性是存在于实例中还是原型中。例子如下:

 function Person(){
  }
  Person.prototype.name = "huangQian";
  Person.prototype.age = 21;
  Person.prototype.job = "science student";
  Person.prototype.sayName = function(){
      console.log(this.name);
  };
  var person1 = new Person();
  var person2 = new Person();
  
  console.log(person1.hasOwnProperty("name"));//false
  console.log("name" in person1 );//true;此处证明不管name属性是存在实例还是原型中,都会返回true
    
  person1.name = "xiQuan";
  console.log(person1.name);//"xiQuan"——来自实例
  console.log(person1.hasOwnProperty("name"));//true
  console.log("name in person1");
    
  console.log(person2.name);//"huangQian"——来自原型
  console.log(person2.hasOwnProperty("name"));//false
  console.log("name" in person2);//true
    
  delete person1.name;
  console.log(person1.name);//"huangQIan"——来自原型
  console.log(person1.hasOwnProperty("name"));//false
  console.log("name" in person1);//true

解析:上面代码执行的过程中,name属性要么直接是在实例对象上访问到的,要么是通过原型访问到的。

2、hasPrototypeProperty()方法
同过下面代码来理解该方法的用法

function Person(){
  }
  Person.prototype.name = "huangQian";
  Person.prototype.age = 21;
  Person.prototype.job = "science student";
  Person.prototype.sayName = function(){
      console.log(this.name);
  };
  var person = new Person();
  console.log(hasPrototypeProperty(person,"name"));//true

  person.name = "xiQuan";
  conaole.log(hasPrototypeProperty(person,"name"));//false

解析:通过上面代码可以看出看出该方法可以检测name属性是否是通过原型对象访问到的,如果是就会返回true,不是就会返回false

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值