原型与原型链
创建对象的方法
在了解原型链之前,先了解一下创建对象的几种方式,介绍以下三种
- 字面量
- 构造函数
- Object.create
// 第一种方式:字面量
var o1 = { name: "o1" };
var o2 = new Object({ name: "o2" });
// 第二种方式:构造函数
var M = function (name) {
this.name = name;
};
var o3 = new M("o3");
// 第三种方式:Object.create
var p = { name: "p" };
var o4 = Object.create(p);
console.log(o1);
console.log(o2);
console.log(o3);
console.log(o4)
// 第一种方式:字面量
var o1 = { name: "o1" };
var o2 = new Object({ name: "o2" });
// 第二种方式:构造函数
var M = function (name) {
this.name = name;
};
var o3 = new M("o3");
// 第三种方式:Object.create
var p = { name: "p" };
var o4 = Object.create(p);
console.log(o1);
console.log(o2);
console.log(o3);
console.log(o4)
什么是实例?原型对象?
var M = function (name) { this.name = name; }
var o3 = new M('o3')
var M = function (name) { this.name = name; }
var o3 = new M('o3')
- 实例就是对象,在本例中o3就是实例,M就是构造函数
- 实例通过new一个构造函数生成的
- 原型对象的construor指向的是构造函数
什么是构造函数?
通过 new 函数名 来实例化对象的函数叫构造函数。任何的函数都可以作为构造函数存在。构造函数首字母一般大写
function Person(name , age , sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
var person = new Person("Tony" , 18 , "男");
console.log(person.name); // Tony
function Person(name , age , sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
var person = new Person("Tony" , 18 , "男");
console.log(person.name); // Tony
上面这段代码就是创建一个了Person 的构造函数,在Person 构造函数中,为每一个对象都添加了三个属性(name,age,sex),也就是说构造函数每执行一次就会创建一个新的Person对象
构造函数的__proto__
构造函数本身也是一个函数对象,是一个js对象,是通过 Function
构造器产生的
Person.__proto__ === Function.prototype
Person.__proto__ === Function.prototype
什么是原型对象?
原型对象本身是一个普通对象,而普通对象的构造函数都是Object
// 构造函数的原型对象
Person.prototype.__proto__ === Object.prototype
// 构造函数的原型对象
Person.prototype.__proto__ === Object.prototype
prototype
在JS中,每当定义一个函数时候,都会默认自带一个prototype
属性,这个属性指向的是该构造函数创建的原型对象,并且这个属性是一个对象数据类型的值
原型:每一个JS对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性。
举个原型的Demo:
function Person() { }
Person.prototype.name = 'Tony'; // 注意:prototype是函数才会有的属性
var person1 = new Person();
var person2 = new Person();
console.log(person1.name); // Tony
console.log(person2.name); // Tony
function Person() { }
Person.prototype.name = 'Tony'; // 注意:prototype是函数才会有的属性
var person1 = new Person();
var person2 = new Person();
console.log(person1.name); // Tony
console.log(person2.name); // Tony
Person.prototype
表示实例原型。原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中
什么是原型链?
简单理解就是原型组成的链,对象的__proto__
属性指向它的原型,而原型也是一个对象,也有__proto__
属性,原型的__proto__
又是原型的原型,就这样可以一直通过__proto__
向上找,这就是原型链,当向上找找到Object的原型的时候,这条原型链就算到头了
原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法
为什么原型链的终点是null,而不是Object.prototype
首先要明确一点,原型链上的所有节点都是对象,所以终点不能是字符串、数字、布尔值等原始类型
另外,规范要求原型链必须是有限长度的(从任一节点出发,经过有限步骤后必须到达一个终点,显然也不能有环)
那么,应该用什么对象作为终点呢?很显然应该用一个特殊的对象
Object.prototype
确实是个特殊对象,我们先假设用它做终点。那么考虑一下,当你取它的原型时应该怎么办?
Object.prototype.__proto__
Object.prototype.__proto__
应该返回什么?
取一个对象的属性时,可能发生三种情况:
- 如果属性存在,那么返回属性的值
- 如果属性不存在,那么返回undefined
- 不管属性存在还是不存在,有可能抛异常
我们已经假设Object.prototype
是终点了,所以看起来不能是情况1。另外,抛出异常也不是好的设计,所以也不是情况3。那么情况2呢,它不存在原型属性,返回undefined怎么样?也不好,因为返回undefined一种解释是原型不存在,但是也相当于原型就是undefined。这样,在原型链上就会存在一个非对象的值
所以,最佳选择就是null。一方面,你没法访问null的属性,所以起到了终止原型链的作用;另一方面,null在某种意义上也是一种对象,即空对象,因为null一开始就是为表示一个“空”的对象存在的。这样一来,就不会违反“原型链上只能有对象”的约定。
所以,“原型链的终点是null”虽然不是必须不可的,但是却是最合理的
__proto__
每个JS对象都具有__proto__
属性,这个属性指向该对象的原型对象
该对象的构造函数的prototype
属性也指向该对象的原型对象
所以也可以这么说,每个对象的__proto__
指向它构造函数的prototype
person1.__proto__ === Person.prototype
person1.__proto__ === Person.prototype
构造函数是一个函数对象,也是一个js对象,是通过 Function
构造器产生的
Person.__proto__ === Function.prototype
Person.__proto__ === Function.prototype
__proto__
绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于 Person.prototype
中,实际上,它是来自于Object.prototype
,与其说是一个属性,不如说是一个getter/setter
,当使用obj.__proto__
时,可以理解成返回了Object.getPrototypeOf(obj)
prototype
只有函数对象有prototype
属性,对象是没有prototype
属性的,因为函数对象也是对象,函数也有__proto__
属性
constructor
每个原型对象都有一个constructor
属性指向关联的构造函数
Person === Person.prototype.constructor // true
Person === Person.prototype.constructor // true
当获取person.constructor
时,其实person
中并没有constructor
属性,当不能读取到constructor
属性时,会从 person
的原型也就是Person.prototype
中读取,正好原型中有该属性,所以:
person.constructor === Person.prototype.constructor // true
person.constructor === Person.prototype.constructor // true
原型链:在JS中,万物皆对象,对象和对象之间也是有关系的,并不是孤立存在的。对象之间的继承关系,在JS 中是通过prototype对象指向父类对象,直到指向Object对象为止,这样就形成了一个原型指向的链条,专业术语称之为原型链
实例和原型
当我们读取实例的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果没有则去原型的原型中寻找,一直找到最顶层Object为止,Object对象的原型没有原型(Object是JS中所有对象数据类型的基类(最顶层的类)在Object.prototype上没有__proto__这个属性),如果在Object原型中依然没有找到,则返回undefined。
function Person() {} Person.prototype.name = 'Tony'; var person = new Person();
// 当我们给实例对象person添加了name属性,打印 person.name 时,结果为name的值Ken。 person.name = 'Ken'; console.log(person.name) // Ken
// 当我们删除了person的name属性时,再次读取person.name,从person 对象中找不到name属性就会从person的原型也就是person.__proto__和Person.prototype中查找name属性,因为之前我们给他添加了,所以找到了 name属性为 Tony。 delete person.name; console.log(person.name) // Tony 我们可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性,如果自身属性存在,则返回 true,否则为false;使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,则会返回true,如果都没有则返回false。
function Person() { this.name = 'Tony' }
Person.prototype.age = 18;
var person = new Person();
console.log(person.hasOwnProperty('name')); // true console.log(person.hasOwnProperty('age')); // false
console.log('name' in person); // true console.log('age' in person); // true console.log('a' in person); // false
最后,相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线:
用心读完上面的总结,应该会对 JS 的原型与原型链有了一个深层次的认识吧,其实这部分还是需要细心琢磨的,毕竟是比较底层的原理
实例、构造函数、原型对象之间的关系
- 实例是对象
- 实例通过new一个构造函数生成的
- 实例的
__proto__
属性指向原型对象(又称是实例原型) - 构造函数是函数,也是函数对象,也是对象
- 构造函数的
prototype
属性指向原型对象 - 原型对象的
construor
指向的是构造函数
instanceof原理
instanceof是判断实例对象的
__proto__
和生成该实例的构造函数的prototype
是不是引用的同一个地址,即比较原型对象的地址是否相同是返回true,否返回false
new运算符
原理:
- 一个新对象被创建。它继承自foo.prototype
- 构造函数返回一个对象。在执行的时候,相应的传参会被传入,同时上下文(this)会被指定为这个新的实例。
- new foo等同于new foo(), 只能用在不传递任何参数的情况
- 如果构造函数反悔了一个对象,那个这个对象会取代整个new出来的结果。如果构造函数没有返回对象,那个new出来的结果为步骤1创建的对象
实现:
var new2 = function (func) {
var o = Object.create(func.prototype); //创建对象
var k = func.call(o); //改变this指向,把结果赋给k
if (typeof k === 'object') { //判断k的类型是不是对象
return k; //是,返回k
} else {
return o; //不是返回返回构造函数的执行结果
}
}
var new2 = function (func) {
var o = Object.create(func.prototype); //创建对象
var k = func.call(o); //改变this指向,把结果赋给k
if (typeof k === 'object') { //判断k的类型是不是对象
return k; //是,返回k
} else {
return o; //不是返回返回构造函数的执行结果
}
}
Function与Object
console.log(Function.__proto__ === Object.prototype); // false
console.log(Object.__proto__ === Object.prototype); // false
console.log(Function.__proto__ === Object.__proto__); // true
console.log(Object.__proto__ === Function.prototype); // true
console.log(Function.__proto__ === Function.prototype); // true
console.log(Function.__proto__.__proto__ === Function.prototype.__proto__); // true
console.log(Function.__proto__.__proto__ === Object.prototype); // true
console.log(Function.prototype.__proto__ === Object.prototype); // true
console.log(Object.__proto__.__proto__ === Object.prototype); // true
console.log(Function.prototype.prototype); // undefined
console.log(Object.prototype.prototype); // undefined
console.log(Function.prototype.__proto__.__proto__); // null
console.log(Function.__proto__.__proto__.__proto__); // null
console.log(Object.prototype.__proto__); // null
console.log(Object.__proto__.__proto__.__proto__); // null
console.log(Object.constructor === Function.__proto__.constructor); // true
console.log(Function.__proto__.constructor === Function); // true
console.log(Function.__proto__ === Object.prototype); // false
console.log(Object.__proto__ === Object.prototype); // false
console.log(Function.__proto__ === Object.__proto__); // true
console.log(Object.__proto__ === Function.prototype); // true
console.log(Function.__proto__ === Function.prototype); // true
console.log(Function.__proto__.__proto__ === Function.prototype.__proto__); // true
console.log(Function.__proto__.__proto__ === Object.prototype); // true
console.log(Function.prototype.__proto__ === Object.prototype); // true
console.log(Object.__proto__.__proto__ === Object.prototype); // true
console.log(Function.prototype.prototype); // undefined
console.log(Object.prototype.prototype); // undefined
console.log(Function.prototype.__proto__.__proto__); // null
console.log(Function.__proto__.__proto__.__proto__); // null
console.log(Object.prototype.__proto__); // null
console.log(Object.__proto__.__proto__.__proto__); // null
console.log(Object.constructor === Function.__proto__.constructor); // true
console.log(Function.__proto__.constructor === Function); // true
Object.__proto__ === Function.prototype
说明Function
是Object
的构造函数Function.__proto__ === Function.prototype
说明Function
既是原型对象,同时自己是自己的构造函数(Function.constructor === Function
),既是鸡也是蛋,就是这么牛掰Object
是Function.__proto__
和Function.prototype
指向的原型对象的构造函数
根源对象null
原型链最上层(第一层)的根源对象是null
,并且它们彼此间也是完全相等的,说明根源对象null
是同一个
console.log(Function.prototype.__proto__.__proto__ === null); // true
console.log(Function.__proto__.__proto__.__proto__ === null); // true
console.log(Object.prototype.__proto__ === null); // true
console.log(Object.__proto__.__proto__.__proto__ === null); // true
console.log(Function.prototype.__proto__.__proto__ === Object.prototype.__proto__); // true
console.log(Function.prototype.__proto__.__proto__ === Function.__proto__.__proto__.__proto__); // true
console.log(Object.prototype.__proto__ === Object.__proto__.__proto__.__proto__); // true
console.log(Function.prototype.__proto__.__proto__ === null); // true
console.log(Function.__proto__.__proto__.__proto__ === null); // true
console.log(Object.prototype.__proto__ === null); // true
console.log(Object.__proto__.__proto__.__proto__ === null); // true
console.log(Function.prototype.__proto__.__proto__ === Object.prototype.__proto__); // true
console.log(Function.prototype.__proto__.__proto__ === Function.__proto__.__proto__.__proto__); // true
console.log(Object.prototype.__proto__ === Object.__proto__.__proto__.__proto__); // true
第二层原型对象
顾名思义,即原型链最上层(第一层)的根源对象null
下面一层的原型对象
第二层的原型对象的__proto__
指向最上层(第一层)的根源对象null
表示方法
Function.prototype.__proto__
Function.__proto__.__proto__
Object.prototype
Object.__proto__.__proto__
因为Function.prototype.prototype
和Object.prototype.prototype
是undefined,说明第二层的原型对象不是构造函数
因为Function.__proto__.__proto__
、Object.prototype
、 Object.prototype
完全相等,说明第二层的原型对象是同一个
因为Object.__proto__.__proto__ === Object.prototype
,说明Object
是第二层原型对象的构造函数
总结
Person.__proto__ === Function.prototype
函数也是对象,所以构造函数也是对象,所以构造函数也有__proto__
函数的构造函数是 Function
它的__proto__
属性指向的是Function.prototype
Person.prototype.__proto__ === Object.prototype
// 因为函数也是对象,即构造函数也是对象,所以构造函数也有__proto__属性
// 函数(构造函数)的构造函数是Function
// 它的__proto__属性指向的是Function.prototype
// 那么这就导致了,与Object是实例对象的构造函数,这条规律冲突了
console.log(Person.__proto__ === Function.prototype); // true
// 因为函数也是对象,即构造函数也是对象,所以构造函数也有__proto__属性
// 函数(构造函数)的构造函数是Function
// 它的__proto__属性指向的是Function.prototype
// 那么这就导致了,与Object是实例对象的构造函数,这条规律冲突了
console.log(Person.__proto__ === Function.prototype); // true
下面作出总结:
一切的函数对象和
Object
对象,继承自Function
对象一切对象都是继承自
Object
对象,Object
对象直接继承根源对象null
Object
对象直接继承自Function
对象Function
对象的__proto__
会指向自己的原型对象,最终还是继承自Object
对象
既然 Function instanceof Object === true
,为什么Function.__proto__ !== Object.prototype
?
ƒ ()
Function和Object原型对象相同
console.log(Function.__proto__ === Object.__proto__); // true
console.log(Function.__proto__); // 控制台输出ƒ () { [native code] }
console.log(Object.__proto__); // 控制台输出ƒ () { [native code] }
console.log(Function.__proto__ === Object.__proto__); // true
console.log(Function.__proto__); // 控制台输出ƒ () { [native code] }
console.log(Object.__proto__); // 控制台输出ƒ () { [native code] }
ƒ () { [native code] }是V8内部C++代码返回的结果,而不是真实的JS代码结果
第 1710 行附近的 FunctionSourceString 函数:
return 'function () { [native code] }';
return 'function () { [native code] }';