JavaScript函数高级
原型的概念
-
函数的prototype属性
每个函数都有一个prototype属性,它默认指向一个Object空对象(即没有我们定义的属性或方法的对象,也称为原型对象)。
原型对象中有一个constructor属性,指向该函数对象。
-
给构造函数添加属性
给构造函数的原型对象添加属性(一般是方法),实例对象可以访问到。
显示原型与隐式原型
- 每个函数都有一个显式原型属性,即prototype属性
- 每个实例对象都有一个隐式原型属性,即__proto__属性,实例对象包括:
函数
所有函数都是Function的实例,创建函数时执行语句:this.peototype={}
对象
创建对象时执行语句:this.proto=构造函数.protottype - 对象的隐式原型的值为其对应的构造函数的显式原型的值。
- 内存结构
- 定义函数
在堆内存,开辟两块内存空间:
一块内存空间保存该函数对象,这个函数对象有一个默认的prototype属性,属性值是一个地址值,这个地址指向该函数对象的原型对象。
一块内存空间保存一个空的Object对象,即原型对象。
在栈内存,开辟一块内存空间:
保存函数标识的变量,值是一个地址值,这个地址值指向堆内存中的函数对象。 - 创建实例
在堆内存,开辟一块内存空间:
保存该实例对象,该实例对象有一个隐含的__proto__属性,属性值是一个地址值,这个地址值指向其构造函数的原型对象。
在栈内存,开辟一块内存空间:
保存实例对象的标识的变量,值是一个地址值,这个地址值指向堆内存中的实例对象。 - 给原型添加方法
- 通过实例对象调用原型对象的方法
内存结构图:
- 总结
1、函数的prototype属性:
是在定义函数时浏览器自动为其添加的,默认值是一个空的Object对象,即原型对象。
浏览器的隐含操作:this.prototype={}
2、对象的__proto__属性:
是在创建对象时自动添加的,默认值为其构造函数的prototype属性值
3、程序员(我们)可以直接操作显式原型,但不能操作隐式原型(ES6之前)
原型链
- 原型链
- 原型对象的原型
原型对象也有原型,可以通过__proto__属性访问。
Object对象的原型对象的隐式原型属性__proto__的值是null。(原型链的尽头) - 别名:
隐式原型链 - 原型链的尽头:
Object对象的原型对象 - 访问一个对象的属性时
(1)先在对象自身的属性中查找,找到返回
(2)如果没有,则沿着__proto__属性依次沿着原型对象的原型向外查找,找到返回
(3)如果最终没找到,则返回undefined - 原型链的作用:
查找对象的属性和方法。 - 原型链的内存结构图
- 原型对象的原型
- 构造函数、原型、实例对象的关系
var o1 = new Object();
var o2 = {};
- 函数、原型、实例对象的关系2
- 所有函数对象是Function对象的实例,所有函数都既有显式原型属性prototype又有隐式原型属性__proto__
- 构造函数的显式原型属性与隐式原型属性都指向它自己的原型对象。
- 所有函数的隐式原型属性都指向同一个对象,即Function对象的原型对象。(因为任何函数实质上都是通过new Function()的方式创建的)
function Foo(){ }
- 原型链补充
-
函数的显式原型指向的对象:默认是空的Object实例对象,也就是它自己的原型对象(但Object对象不满足)
-
所有函数都是Function对象的实例,包括Function本身
-
Object对象的原型对象是原型链尽头,因为Object的原型对象的隐式原型__proto__的值是null
-
Object>Function>其他对象
-
- 原型继承
构造函数的实例对象自动拥有构造函数的原型对象的属性(方法) - 原型属性问题
- 读取对象的属性值时: 会自动到原型链中查找
- 设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值
- 方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上
探索instanceof
- instanceof运算符用于判断一个对象是否是构造函数的实例。
- 表达式:A instanceof B
执行流程:通过__proto__属性查找A对象的原型链,如果B函数的显式原型对象在A对象的原型链上,则返回true。 - 案例1:
- 案例2:
面试题
- 测试题1:
- 测试题2:
(完)