前言
在写复杂的 JavaScript 应用之前,充分理解原型链继承的工作方式是每个 JavaScript 程序员必修的功课。
JavaScript中并没有类(class);Js是基于原型(prototype-based)来实现的面向对象(OOP)的编程范式的,但并不是所有的对象都拥有prototype这一属性
var a = {},b = 'shi';
var fn = function(){};
a.prototype // undefined
b.prototype // undefined
fn.prototype // Object {constructor: function}
prototype
属性只有 function
对象定义时才有,函数本身也是对象。想要明白原型问题我们先明白几个基本概念。
一、function、Function、Object和{}
function
是JavaScript的关键字,用于定义函数变量,一般有2种定义方式
function f1(){
console.log('This is function f1!');
}
typeof(f1); //=> 'function'
var f2 = function(){
console.log('This is function f2!');
}
typeof(f2); //=> 'function'
Function
是函数类型实例的构造函数(constructor), 本质也是函数,类似还有Object
或String
、Number
等,都是Js内置类型实例的构造函数,构造函数主要作用就是创建相应类型的实例,实现原型链。
var f3 = new Function("console.log('This is function f3!');");
f3(); //=> 'This is function f3!'
typeof(f3); //=> 'function'
typeof(Function); //=> 'function'
Object,它用于生成对象类型,其简写形式为{},其实和function和Function的关系类似。
var o1 = new Object();
typeof(o1); //=> 'object'
var o2 = {};
typeof(o2); //=> 'object'
typeof(Object); //=> 'function'
二、prototype 和 _proto_
prototype
属性只有函数类型对象才有,上面说的很清楚了。而__proto__
是所有JavaScript对象都内置的属性[[Prototype]],而这个属性指向构造函数(类似父类)的prototype
属性,从而继承属性.
举个例子:
var Person = function(){};
Person.prototype.type = 'Person';
Person.prototype.maxAge = 100;
var p = new Person();
console.log(p.maxAge);
p.name = 'rainy';
// 修正Person.prototype.constructor为Person本身
Person.prototype.constructor === Person; //=> true
p.__proto__ === Person.prototype; //=> true
console.log(p.prototype); //=> undefined
一图胜千言
Person是一个函数类型的变量,因此自带了prototype属性,prototype属性中的constructor又指向Person本身;通过new关键字生成的Person类的实例p1,通过__proto__
属性指向了Person的原型。
注意:
遵循ECMAScript标准,someObject.[[Prototype]] 符号是用于指派 someObject 的原型。这个等同于 JavaScript 的__proto__
属性。从 ECMAScript 6 开始, [[Prototype]] 可以用Object.getPrototypeOf()和Object.setPrototypeOf()访问器来访问。
三、原型链
原型链是基于 __proto__
的。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依此层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
举例说明:
// Node
var Obj = function(){};
var o = new Obj();
o.__proto__ === Obj.prototype; //=> true
o.__proto__.constructor === Obj; //=> true
Obj.__proto__ === Function.prototype; //=> true
Obj.__proto__.constructor === Function; //=> true
Function.__proto__ === Function.prototype; //=> true
Object.__proto__ === Object.prototype; //=> false
Object.__proto__ === Function.prototype; //=> true
Function.__proto__.constructor === Function;//=> true
Function.__proto__.__proto__; //=> {}
Function.__proto__.__proto__ === o.__proto__.__proto__; //=> true
o.__proto__.__proto__.__proto__ === null; //=> true
来张图更直观一些
到了这里,其实有个问题大家没注意,new
关键词到底起了什么作用呢?
其实开头的图已经给出答案:new
关键词的最主要的作用就是完成上图所示实例与父类原型之间关系的串接,并创建一个新的对象.
有兴趣深入看这里:JS 的 new 到底是干什么的?
文笔有限,才疏学浅,文中若有不对之处,还望告知。
个人博客
参考文章