原型与原型链
1. 原型(prototype)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>原型</title>
</head>
<body>
<!--
1.函数的prototype属性
- 每个函数都有一个prototype属性,且默认指向一个空的Object对象(即称为:原型对象)
- 原型对象中有一个属性constructor,它指向函数对象
2.给原型对象添加属性(一般都是方法)
作用:函数的所有实例对象自动拥有原型中的属性(方法)
-->
<script>
// 输出Date原型中的属性及方法以及Date原型的类型
console.log(Date.prototype, typeof Date.prototype)
// 定义一个函数Fn
function Fn(){
}
// 输出Fn原型中的属性及方法以及Fn原型的类型
console.log(Fn.prototype, typeof Fn.prototype)
// 查看原型中对象的constructor属性是否指向原对象
console.log(Date.prototype.constructor === Date)
console.log(Fn.prototype.constructor === Fn)
// 给原型对象添加属性
Fn.prototype.test = function(){
console.log("test....")
}
// 声明一个实例对象
var f1 = new Fn();
// 调用原型中的方法
f1.test()
</script>
</body>
</html>
1.1 每个函数都有一个prototype属性,且默认指向一个空的Object对象
console.log(Date.prototype, typeof Date.prototype)
输出Date原型中的属性及方法以及Date原型的类型
function fn(){
}
console.log(fn.prototype, typeof fn.prototype)
自定义fn函数,输出fn函数的原型,默认为空对象(并没有多余的属性和方法),原型类型为Object
1.2 原型对象中有一个属性constructor,它指向函数对象
console.log(Date.prototype.constructor === Date) // true
console.log(fn.prototype.constructor === fn) // true
定义一个Type函数 function Type(){ }
其内部有prototype属性,此属性指向一个对象,对象中有constructor属性,指向原来的Type函数
1.3 给原型对象添加属性
function Fn(){
}
// 给原型对象添加属性(一般是方法) 可供实例对象使用
Fn.prototype.test = function(){
console.log("test....")
}
// 声明一个实例对象
var f1 = new Fn();
// 调用原型中的方法
f1.test()
2. 显式原型与隐式原型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>显式原型与隐式原型</title>
</head>
<body>
<!--
1.每个函数function都有一个prototype属性,即显式原型属性
2.每个实例对象都有一个__proto__,可称为隐式原型属性
3.对象的隐式原型的值为其对应构造函数的显式原型的值
4.内存结构
5.总结
- 函数的prototype属性:在定义函数时自动添加的,默认值时一个空Object对象
- 对象的__Proto__属性:创建对象时自动添加的,默认值为构造函数的prototype属性值
- 在ES6之前,开发人员可直接操作显式原型,但是不能直接操作隐式原型
-->
<script>
// 创建一个构造函数
function Fun(){
// 内部隐含语句:this.prototype = {}
}
// 1.每个函数function都有一个prototype属性,即显式原型属性,默认指向空的Obejct对象
console.log(Fun.prototype);
// 2.每个实例对象都有一个__proto__ , 可称为隐式原型属性,
// 默认指向和函数相同指向的那个空的Obejct对象
var fun = new Fun(); // 内部隐含语句 this.__ptoto__ = Fun.prototype
console.log(fun.__proto__);
// 3.对象的隐式原型的值为其对应构造函数的显式原型的值
console.log(Fun.prototype === fun.__proto__); // true
</script>
</body>
</html>
3.原型链
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>原型链</title>
</head>
<body>
<!--
1.原型链(图解)
- 访问一个对象的属性(对象也是属性)时
先在自身属性中查找,找到则返回
若没有,则沿着__proto__这条链向上查找,找到则返回
若最终没找到,则返回undefined
- 别名:隐式原型链
- 作用:查找对象的属性(方法)
2.构造方法/原型/实体对象的关系图解
-->
<script>
console.log(Object.prototype)
function Fn(){
this.test1 = function(){
console.log('test1....');
}
}
Fn.prototype.test2 = function(){
console.log("test2.....");
}
var f1 = new Fn();
f1.test1();
f1.test2();
f1.test3();
</script>
</body>
</html>
3.1 原型链图解
图解一:以Fn与Obecjt为例
- 在开始时,浏览器会在内存中创建Obejct函数对象,假设为0x456,函数对象存在prototype属性,Object的prototype属性会指向其原型对象,假设其原型对象地址值为0x345。
- 开发人员创建Fn函数时,会在栈中存储Fn的变量,其保存Fn函数对象的地址,假设为0x123,同时其函数对象内部的prototype属性会指向一个空的Object对象,假设为0x234。
- Fn.prototype.test2表示在Fn内部的prototype指向的空对象中会创建一个test2函数对象
- 当执行var fn = new Fn()时会在栈中生成一个fn变量,保存Fn实例对象的地址,假设为0x567
图解二:以两种创建对象的方式为例
- 当使用var o1 = new Object()方式创建一个对象时,o1为实例对象,按照原则,实例对象内部有__proto__属性,其指向创建自己的函数对象的原型对象,即为Object()的原型对象,原型对象中存在constructor属性,指向函数对象本身,因此在函数对象和其原型对象之间会形成一个环,即表现为互相引用,互相指向。
- 使用var o2 = {}方式创建对象与上述方式相同
图解三:以function Foo(){}为例
- 执行function Foo(){}时,会在内存中生成Foo函数对象以及其指向的原型对象,函数对象中存在prototype属性,其保存的地址值指向其原型对象(即为下图中线1所表示),原型对象中存在constructor属性,其保存的地址值指向其对应得函数对象(即为下图中线2所表示)。
- 因为开发人员定义的任何函数都是通过new Function()创建的,例如创建方式为var Foo = new Functon(),因此创建得到的函数也属于Function的实例对象,因此创建的函数也具备__proto__属性,其指向创建自己函数对象的原型对象(即为下图中线3所表示)。
- 由下图可知Function本身具备__proto__属性,此属性为实例对象所具备的,因此可知Function也为某个函数的实例对象,通过下图中的线4可知,Function的__proto__指向自身的prototype,因此可知,Function是通过自身创建的,即为Function = new Function()。从Function()作为函数对象的角度来看,函数对象具备prototype,其指向自身的原型对象,即为下图中线5所表示,而原型对象也会又constructor属性,指向自身的函数对象,即下图中线6所表示的。
- Object()也是一个函数对象,因此它也是通过Function()所创建的,因此Object()函数对象也是实例对象,其内部也存在__proto__指向创建自身的函数对象的原型,即为Function()函数对象的原型,即下图中线9所表示的。
3.2 原型链补充( 重要 )
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>原型链补充</title>
</head>
<body>
<script>
function Fn(){
}
// 1.函数的显式原型指向的对象默认是空Object对象,但对于Object函数来说并不满足此条件
console.log(Fn.prototype instanceof Object) // true
console.log(Object.prototype instanceof Object) // false
// Function函数对象的原型也是Object的实例对象
console.log(Function.prototype instanceof Object) // false
// 2.所有的函数都是Function的实例,包括function自身
console.log(Function.__proto__ === Function.prototype) // true
// 3.Object的原型对象是原型链的尽头
console.log(Object.prototype.__proto__) // null
</script>
</body>
</html>
3.3原型链的属性问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>原型链_属性问题</title>
</head>
<body>
<!--
1. 读取对象的属性值时: 会自动到原型链中查找
2. 设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值
3. 方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上
-->
<script type="text/javascript">
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.setName = function (name) {
this.name = name;
}
Person.prototype.sex = '男';
var p1 = new Person('Tom', 12)
p1.setName('Jack')
console.log(p1.name, p1.age, p1.sex)
p1.sex = '女'
console.log(p1.name, p1.age, p1.sex)
var p2 = new Person('Bob', 23)
console.log(p2.name, p2.age, p2.sex)
</script>
</body>
</html>
4. 探索instanceof底层原理
4.1 案例一
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>05_探索instanceof</title>
</head>
<body>
<!--
1. instanceof是如何判断的?
* 表达式: A instanceof B
* 如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false
2. Function是通过new自己产生的实例
-->
<script type="text/javascript">
//案例1
function Foo() {
}
var f1 = new Foo();
console.log(f1 instanceof Foo);
console.log(f1 instanceof Object);
//案例2
console.log(Object instanceof Function)
console.log(Object instanceof Object)
console.log(Function instanceof Object)
console.log(Function instanceof Function)
function Foo() {
}
console.log(Object instanceof Foo);
</script>
</body>
</html>
1. 判断f1是否是Foo的实例,对于f1来说,沿着__proto__向上第一个节点为Foo.prototype, Foo查看prototype的指向,因此两条线路相交于Foo.prototype,则是Foo的实例
2. 判断f1是否是Object的实例, 对于f1来说,沿着__proto__向上第一个节点为Foo.prototype, Object的prototype指向Object.prototype, 各向上走一步并没有相交,f1可沿着__proto__继续向上走,第二个节点指向Object.prototype , 两则在此处相交,因此是Object的实例。