在了解原型和原型链之前,先明确几个概念:
- 一切引用类型(数值、对象、函数)都是对象,都是可以自由扩展属性的,null 除外;
- 对象是属性的集合;
- 每个对象都是通过构造函数创建的;
- 每个函数都有一个 prototype 属性(此属性是一个对象),也就是 原型;对象都有一个 __proto__ 属性,此属性是隐式属性,JS不希望开发者调用到这个属性;
- 每个对象的 __proto__ 属性 指向创建该对象的构造函数的 prototype 属性;
- Object.prototype.__proto__ 指向的是 null (要不然就无穷无尽了);
-
原型
// 构造函数 function Foo (name, age) { this.name = name; this.age = age; } // 所以函数上都有一个 prototype 属性,此属性是一个对象,则就可以扩展属性 Foo.prototype = { showName () { console.info('Name: ' + this.name); }, showAge () { console.info('Age: ', this.age); } } let fn = new Foo('静静', 16); fn.showName(); // Name: 静静 fn.showAge(); // Age: 16
个人理解:原型可以扩展构造函数,避免将全部的属性或者方法都初始在构造函数中,继而在生成实例时,占用太多内存资源。
-
原型链
访问一个对象的属性时,先在本地属性中查找,如果没有,就会去它的构造函数上的 prototype 属性去找,由于 prototype 属性本身也是一个对象,则就有 __proto__ 属性,再沿着__proto__这条链向上找,这就是原型链。
// 构造函数 function Foo (name, age) { this.name = name; this.age = age; } Object.prototype.toString = function () { console.info(`Name: ${this.name}, Age: ${this.age}`); } let fn = new Foo('静静', 16); fn.toString(); // Name: 静静, Age: 16 console.info(fn.toString === Foo.prototype.__proto__.toString); // true console.info(fn.__proto__ === Foo.prototype); // true console.info(Object.prototype === Foo.prototype.__proto__); // true console.info(Object.prototype.__proto__ === null); // true
分析:fn 是一个对象,有 __proto__ 属性,依据上述概念5可知,fn 上的 __proto__ 属性指向 其构造函数 Foo 上的 prototype 属性;Foo.prototype 的构造函数是 Object,Object 函数上有 prototype 属性;Foo.prototype 是一个对象,则就有 __proto__ 属性,则 Foo.prototype.__proto__ 属性 指向 Object.prototype;Object.prototype 属性就指向 null 。