文章同步地址 : 编程爱好者协会
这篇文章旨在用最简单的语言告诉你什么是原型对象以及什么是原型链。
为啥说 null、undefined 和 函数搞特殊
所有的数据类型(除了 null 和 undefined)都有 contructor 构造函数,但只有 函数 才有 prototype 属性:
/* 数值类型
---------------------*/
var num = NaN;
num.constructor // function Number()
num.prototype // undefined
/* 布尔类型
---------------------*/
var boo = true;
boo.constructor // function Boolean()
boo.prototype // undefined
/* 字符串类型
---------------------*/
var str = 'string';
str.constructor // function String()
str.prototype // undefined
/* undefined
---------------------*/
var undefine = undefined;
undefine.constructor // TypeError
undefine.prototype // TypeError
/* null
---------------------*/
var nul = null;
nul.constructor // TypeError
nul.prototype // TypeError
/* function
---------------------*/
var fn = function() {};
fn.contructor // function fn()
fn.prototype // Object
这下知道为啥别人说 null 和 undefined 是特殊的数据类型了吧!也知道为啥别人说函数是特殊的对象了是不是?这也是为啥有人说 函数是一等公民 的原因,正是因为函数具有 prototype 属性使它拥有了无限的扩展性。
prototype 指向一个包含 constructor 属性的对象。说白了,就是 prototype 是一个对象,具有 constructor 属性,也是一个对象。该 contructor 属性值为对象本身:
Function.prototype.constructor === Function
// true
我怀疑 JS 作者当初只是想通过 prototype.constructor 来描述一个方法的构造函数就是它本身。而其它语言的构造函数,如 Java, 只是一个与类同名的函数。
__proto__ 和 prototype 的关系
__proto__ 其实是 ECMAScript 标准 [[Prototype]] 的代理实现,是各浏览器提供的,检查浏览器是否支持此属性可以使用 Object.getPrototypeOf({__proto__: null}) === null。
__proto__ 指向对象的构造函数的 prototype
window.__proto__ === window.constructor.prototype
// true
上面的描述并不准确
上面说所有的数据类型都有构造函数并不准确或者说是不够精细。当我们创建一个空对象的时候你会发现这个空对象并不 “空”:
var object = new Object();
object.constructor
// function Object()
既然是 “空” 对象,为什么还会有 constructor 属性呢?
嗯,这个时候再引入 __proto__ 应该会把事情搞明白。其实这里的 object.constructor 应该是 object.__proto__.constructor,所以 constructor 其实并不是内部属性。:
object.constructor === object.__proto__.constructor
// true
而前面说过 __proto__ 指向的是对象构造函数的 prototype,那么这里的 constructor 是不是就是其对象构造函数下的 prototype.constructor 呢?答案是:bingo
var object = new Object();
object.constructor === Object.prototype.constructor
// true
所以 __proto__ 的作用是,当访问一个对象的属性时,如果该属性不直接存在对象上,则会去其 __proto__ 所指向的对象里去找,如果不存在就会一直找下去,直到 __proto__ 值为 null 。
prototype 的作用又是什么呢?简单来说,就是共享属性和方法。
啥时候 new
new 关键字经常会被使用,但可是你知道什么时候才需要 new 吗?是的,很简单判断。如果你将要调用的属性或方法是属于调用对象的 prototype 的,那么在使用它之前你需要先实例化这个对象。
var Person = function() {};
Person.name = 'Fish Chan';
Person.prototype.sayHello = function() {
console.log('雷吼呀!');
};
// 不需要 new 就可以使用的属性
Person.name // Fish Chan
// 必须 new 才能使用的属性
Person.sayHello(); // TypeError
var boy = new Person();
boy.sayHello(); // 雷吼呀!
利用原型链模仿高级语言的面向对象
学完 JavaScript 中的原型和原型链后再去学 JavaScript 中的面向对象和设计模式必定会轻车熟路。由于篇幅有限,后面再细讲如何模仿面向对象语言和如何开发设计模式。
本文作者懒得画图,因为作者认为这个东西不太好用图去描述,一想到画图,作者就想去画堆和栈。等画出来估计几个小时就过去了。