先看一下下面的代码,如果你很简单就能理解,那么可以跳过这一篇文章了。
var x = new y();
// 1、所有的实例的原型都是由其构造函数的原型对象:
x.__proto__ === y.prototype
// 2、所有的构造函数都是 Function 的实例,有1可知:
y.__proto__ === Function.prototype
// 3、因为构造函数的原型对象也是对象,所以由1可知:
y.prototype.__proto__ === Object.prototype
// 4、Object 是顶级对象,所以它不符合第3条规则:
Object.prototype.__proto__ // is null
没看明白的我们继续。。。
想要理解原型链,先知原型为何物
先了解一下原型模式吧:原型模式
在原型模式中我们可以利用过一个原型对象来指明我们所要创建对象的类型,然后通过复制这个对象的方法来获得与该对象一模一样的对象实例,所以:
- 实例是由原型拷贝而来的。
再其次,我们都知道,JS中的声明,实际是 new 了一个构造函数,比如:
var a = {};
// 实际是
var a = new Object();
可知,a 是一个对象实例,所以 new Object 构造函数做了什么?
- 构造函数拷贝了相应的对象并返回了这个拷贝,即拷贝原型返回了一个实例。
- 构造函数存在一个原型对象,这个原型对象就是构造函数拷贝的对象。
- 构造函数的 prototype 字段所指向它要拷贝的原型对象,即“构造函数.prototype”
构造函数.prototype ---> 以 String 为例就是 String.prototype
由上述可以,实例的原型就是构造函数上的原型对象,而JS中,为了实现继承,实例的 __proto__ 字段所指向它的原型,,即“实例.__proto__”。
实例.__proto__ ---> 以 var a = {} 为例,就是 a.__proto__
构造函数.prototype 指向它要拷贝的原型对象,而 实例.__proto__ 指向它的原型,都指向同一个对象,故:
var x = new y();
x.__proto__ === y.prototype
现在应该知道原型是什么了吧。
函数实例
其实构造函数是一个函数实例,所以可以知道:
Object.__proto__ === Function.prototype // true
Array.__proto__ === Function.prototype // true
String.__proto__ === Function.prototype // true
// 特殊的 Function
Function.__proto__ === Function.prototype // true
万物皆对象
再研究一下,因为函数的 prototype 一个对象实例,所以构造函数的原型对象的原型属于谁呢?
答案是:Object ,万物皆对象。
Array.prototype.__proto__ === Object.prototype // true
String.prototype.__proto__ === Object.prototype // true
Function.prototype.__proto__ === Object.prototype // true
// 物皆对象,所以 Object 的原型对象已经是顶级了
Object.prototype.__proto__ // null
所以,这里已经解释了最开始的三行代码。
顺带提一下:constructor
以上就是原型和原型对象的概念,顺便提一个原型对象上的属性:constructor ,它指向创造这个原型对象的函数,即:
// 函数的原型对象的构造函数就是它自己
Array.prototype.constructor === Array // true
String.prototype.constructor === String // true
Function.prototype.constructor === Function // true
Object.prototype.constructor === Object // true
// 实例的原型的构造函数就是声明的这个实例
var a = [], b = {}, c = '';
a.__proto__.constructor === Array // true
b.__proto__.constructor === Object // true
c.__proto__.constructor === String // true
现在来理解一下原型链。
何为原型链
其实上面有代码已经初现端倪了:
Array.prototype.__proto__ === Object.prototype // true
构造函数的原型对象的原型,是不是有些拗口,这其实就是原型链,再来一个看几个代码:
var a = [];
a.__proto__ // = Array.prototype
a.__proto__.constructor // = Array
a.__proto__.constructor.prototype // = Array.prototype
有一万中写法来显示一些东西,其实原型链的大致概念是:
- 实例使用属性和方法会依次检索本身和其原型,而原型也属实例,故也会检索本身和其原型,所以会形成一级一级地向上检测查找的链式结构,这种结构就称原型链。
所以上面的例子可以这样简写:
var a = [], b = {}, c = '';
a.constructor === Array // true
b.constructor === Object // true
c.constructor === String // true
因为 a 没有 constructor ,所以会去找原型上的 constructor ,so...
再看一个例子,因为原型链的存在,所以可以在构造函数的原型对象上添加方法,这样所有由此函数创建的实例都可以调用此方法:
function a(){}
a.prototype.say = function(x){ console.log(x) }
var b = new a();
var c = new a();
b.say(‘hello’); // hello
c.say(‘你好’); // 你好
简单提一下 ES6 的类的定义
上面的写法怎么看会有点奇怪,ES6 新增了 class 的声明,可以这样写一个类:
// 定义类
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`你好,我是${this.name},我今年${this.age}岁`);
}
}
// 实例
var my = new User('jack', 22);
my.sayHello(); // 你好,我是jack,我今年22岁
对比一下ES5代码:
// 定义类
function User(name, age){
this.name = name;
this.age = age;
}
User.prototype.sayHello = function(){
console.log(`你好,我是${this.name},我今年${this.age}岁`);
}
// 实例
var my = new User('jack', 22);
my.sayHello(); // 你好,我是jack,我今年22岁
Class 定义其本身是个语法糖,可以用ES5来实现,具体参考此处
OK,说完了,原型链就这一点点。
网友评论