这次想通过写一篇关于原型和原型链的文章,让自己能够稍微搞懂原型和原型链。
原型
什么是原型?书里是这样介绍的:
我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。----《JavaScript高级程序设计》
JavaScript中的对象有一个特殊的[[prototype]]内置属性,其实就是对于其他对象的引用。几乎所有的对象在创建时[[prototype]]属性都会被赋予一个非空的值。----《你不知道的JavaScript上卷》
原型链
[[prototype]]机制就是存在于对象中的一个内部链接,它会引用其他对象。通常来说,这个链接的作用是:如果在对象上没有找到需要的属性或者方法引用,引擎就会继续在[[prototype]]关联的对象上进行查找。同理,如果后者中也没有找到需要的引用就会继续查找它的[[prototype]],以此类推,这一系列对象的链接被称为“原型链”。
如果要访问对象中并不存在的一个属性,[[Get]]操作就会查找对象内部[[prototype]]关联的对象、这个关联关系实际上定义了一条“原型链”,在查找属性时会对它进行遍历。所以普通对象都有内置的Object.prototype,指向原型链的顶端,如果在原型链中找不到指定的属性就会停止。toString()、valueOf()和其他一些通用的功能都存在于Objec.prototype对象上,因此语言中所有的对象都可以使用它们。
通过简单的例子来了解原型和原型链
function Person() {}
Person.prototype.name = 'xiaoming';
var p1 = new Person();
var p2 = new Person();
p1.name = 'p1';
console.log(p1.name); // 'p1',从实例对象中读取到name
console.log(p2.name); // 'xiaoming',从实例对象的原型中读取到name
上述例子中,查找实例对象的name,在实例上搜索name属性,实例p1上存在name属性,直接返回它的值而不必在搜索原型,而实例p2上没有存在name属性,于是继续搜索原型,在原型对象中找到name属性并返回其值。由此可见,当为对象实例添加一个属性时,,这个属性就会屏蔽原型对象中保存的同名属性,即添加这个属性只会阻止我们访问原型中的那个属性,而不会修改那个属性。
关于in操作符
in操作符有两种使用方式:单独使用和在for-in循环中使用。
in操作符只要通过对象能访问到的属性就返回true,而hasOwnproperty()只在属性存在于实例中时才会返回true。
console.log('name' in p1); // true
console.log('name' in p2); // true
console.log(p1.hasOwnproperty('name')); // true
console.log(p2.hasOwnproperty('name')); // false
for-in循环,返回的是所有能够通过对象访问的、可枚举的属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性,屏蔽了原型中不可枚举属性的实例属性也会在for-in循环中返回。
要取得对象上所有可枚举的实例属性,可以使用es6的Object.keys()方法。这个方法接受一个对象作为参数,返回一个包含所有可枚举实例属性的字符串数组。
所有普通的[[prototype]]链最终都会指向内置的Object.prototype。由于所有的“普通”对象都源于这个Object.prototype对象,所有它包含JavaScript中的许多通用的功能(如:.toString()、.valueOf()等等)。

本文探讨了JavaScript中的原型和原型链概念。原型是函数的prototype属性,指向包含共享属性和方法的对象。每个对象有一个[[prototype]]内部属性,构成原型链。当查找对象属性时,如果未找到,则沿着原型链向上查找。实例对象可以通过原型访问到toString()等通用方法。in操作符检查属性是否存在,而for-in循环返回所有可枚举属性,包括实例和原型上的。Object.keys()则返回对象的可枚举实例属性。
1590





