欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

复习面向对象 -- 原型与原型链

程序员文章站 2023-10-27 22:50:40
面向对象中原型以及原型链非常重要,复习面向对象,学习一下原型和原型链,创建对象部分可以看前一个文章,传送门,继承部分可以看后一个文章,传送门 什么是原型: 原型有两种形式:prototype和__proto__;对应的呈现方式不同。 prototype:是函数的一个属性,这个属性的值是一个对象。所以 ......

  面向对象中原型以及原型链非常重要,复习面向对象,学习一下原型和原型链,创建对象部分可以看前一个文章,,继承部分可以看后一个文章,

什么是原型:

  原型有两种形式:prototype__proto__;对应的呈现方式不同。
  prototype:是函数的一个属性,这个属性的值是一个对象。所以一切的函数都有原型,这个原型就是prototype。
  __proto__:是对象的一个属性,同样的属性值也是一个对象。(但__proto__不是一个规范属性,只是部分浏览器实现了此属性,对应的标准属性是[[prototype]])所以一切对象都有标准属性。

  ps:函数没有__proto__属性,同样的,对象也没有prototype属性。

var obj = {name:'peter'};
console.log(obj.__proto__) // {constructor: ƒ, __definegetter__: ƒ, __definesetter__: ƒ, hasownproperty: ƒ, __lookupgetter__: ƒ, …}

var fn = function(){this.name = 'peter'};
console.log(fn.prototype) // {constructor: ƒ}

  所有的函数的默认原型都是object的实例,因此默认原型都会包含一个内部指针,指向object.prototype,所以所有的自定义类型都含有tostring(),valueof()等默认方法的根本原因。

理解原型对象:

  在高程中,有句原话:无论什么时候,只要创建了一个函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有的原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。
  简单来说:只要创建了一个函数,都会有一个prototype属性,属性值就是一个对象,这个对象里有一个属性constructor,这个值就是该函数。

  constructor是一个构造器。一般指向的是其构造函数。

var fn = function(){this.name = 'peter'};
console.log(fn.prototype) // {constructor: ƒ}   
console.log(fn.prototype.constructor == fn)  // true

  对于__proto__,只要是对象,都有constructor属性。在创建的方式中,constructor所指向的构造函数不一样。

  1 字面量创建对象:

  字面量创建对象,其中__proto__的constructor属性指向的构造函数是原始对象函数object()。

var obj = {name:'peter'}
console.log(obj.__proto__.constructor) // ƒ object() { [native code] }

console.log(obj.__proto__.constructor == object);  // true
console.log(obj.constructor == object )  // true
console.log(obj.__proto__.constructor == obj.constructor) // true

  2 构造函数创建对象:

  所谓的构造函数:就是先创建一个函数,new实例化一个对象,函数里的this指向这个对象
  构造函数是个函数,有prototpe属性,值是个对象,对象里有个constructor属性,指向这个函数preson
  实例化的对象peter是一个对象,有__proto__属性,值是个对象,对象里constructor属性,指向这个构造函数person。
  两个constructor所对应的构造函数是相等的,因此实例化的__proto__ 和 构造函数的prototype是相等的。

function person(name,age){
    this.name = name;
    this.age = age;
    this.sayhi = function (){
        console.log(this.name);
    }
}
var peter = new person('peter','male');
console.log(person.prototype.constructor == peter.__proto__.constructor)  // true
console.log(person.prototype == peter.__proto__) // true

  3 object.create() 创建对象

  object.create()是es5新增的一个方法,创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。

  ps!这个创建的对象的__proto__和原对象的__proto__不一样,这说明了新创建的对象只是复制愿对象的原型,但是是一个独立的原型。

var obj = {};
var male = object.create(obj);
console.log(male.__proto__.constructor) // ƒ object() { [native code] }
male.name = 'tom';
console.log(male); //  {name:'tom'}
console.log(obj); //  {name:'peter'}
console.log(male.__proto__ == obj.__proto__);  // false

原型链:

  其基本思想是:利用原型让一个引用类型继承另一个引用类型的属性和方法。
  当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)中寻找,如果该__proto__上没有这个属性,就去__proto__的属性上去找(__proto__.__proto__),依次往下找,找到就使用,找不到就继续往下找,到最上层都没有找到就返回null。
  这样的形式就叫原型链。 总结如下:由于__proto__是任何对象都有的属性,所以会形成一条__proto__连起来的链条,递归访问__proto__,到最后若没有找到,则返回一个null。
  例子如下:(为了强调原型链,所以采用了混合模式创建对象)

function person(name,age){
    this.name = name;
    this.age = age;
}
person.prototype.sayhi = function(){
    console.log(this.name);
}
var peter = new person('peter','male');
peter.sayname = function(){
    console.log('我叫17号')
}
peter.sayname();
peter.sayhi();
peter.tostring();
peter.togo();

  这个例子要调用四个方法。先描述怎么拿到该方法:

  1.peter.sayname(); // 我叫17号
  对象本身写的方法,是挂载了对象的属性上,所以不用通过原型链的方式直接就能拿到该方法。

  2.peter.sayhi(); // peter
  该对象上没有sayhi的方法,=> 该对象的__proto__去找 => peter.__proto__(由于peter.__proto__ = person.prototype) => 该prototype上有sayhi属性,调用,查找结束。

  3.peter.tostring(); // "[object object]"
  该对象上没有tostring的方法,=> 该对象的__proto__去找 => peter.__proto__ => 该__proto__上没有该属性 => peter.__proto__.__proto__ => 在该原型上找到tostring(),调用,结束。

  4.peter.togo();
  该对象上没有togo的方法,=> 该对象的__proto__去找 => peter.__proto__ => 该__proto__上没有该属性 => peter.__proto__.__proto__ => 在该原型上没有该属性 => peter.__proto__.__proto__.__proto__ => 返回null => 没有该属性,直接报错。

  从上面的例子就可以得知,原型链就是通过__proto__连起来的链子,可以依次查找,找到就调用,找不到直接undefined或报错。这就是原型链。

 

 

 
后记:
  在复习面向对象中,由于篇幅过长,将知识点分成了三部分,,面向对象--原型与原型链,,对应的知识点我放在了里,有需要的可以去clone学习,觉得好的话,给个star。
  文章有不对或者不理解的地方,请私信或者评论,一起讨论进步。

 
参考资料:
  javascript高级程序设计(第三版)