JavaScript原型
1.前言
-
构造函数的执行
- 构造函数通过
new
关键字调用的执行过程;new
关键字调用构造函数后会创建一个空对象;- 同时将这个空对象赋值给
this
(即:this
指向该空对象); - 给
this
追加属性; - 将
Fn.prototype
赋值给p1.__proto__
;
- 构造函数通过
对象是引用数据类型,赋值只能将地址赋值给另一个变量,即:将对象的地址赋值给this
let isThis = undefined;
function Fn(age) {
this.age = age;
isThis = this;
}
const p1 = new Fn(15);// p1接收空对象
console.log('isThis:', isThis === p1);// => true
console.log('proto1:', Fn.prototype === p1.__proto__);// =>true
console.log('proto2:', Fn.prototype === Object.getPrototypeOf(p1));// =>true
console.log('proto2:', p1.__proto__ === Object.getPrototypeOf(p1))// =>true
-
什么是原型
几乎
每个JavaScript 对象都有另一个与之关联的对象
。另一个对象
被称为原型(prototype)- 函数的原型称为显示原型(
prototype
) - 简单对象的原型称为隐式原型(
__proto__
)
- 所有引用类型数据都称之为对象(函数,数组,对象)
- 原型(prototype)是一个对象
Object
不具有Object.prototype
属性- 函数名为
Fn
获取原型的方法为Fn.prototype
- 对象
p1
获取原型的方法:- 非标准(浏览器自带):
P1.__proto__
- 标准:
Object.getPrototypeOf(p1)
- 非标准(浏览器自带):
-
为什么需要原型?
- 当想要调用某个对象身上的方法或属性时,如果该对象身上没有该方法和属性,就会去原型身上查找;
-
节约内存(下文有体现)
-
constructor
-
constructor
是原型上的一个属性,它的值指向原型的创建对象
constructor
是一个对象,存放的是Fn
函数的地址let isThis = undefined; function Fn(age) { this.age = age; isThis = this; } console.log('constructor:', Fn.prototype.constructor)// => /* constructor: ƒ Fn(age) { this.age = age; isThis = this; } */ console.log('constructor:', Fn.prototype.constructor === Fn)// =>true
-
2.原型的内存表现
-
定义一个构造函数
复杂数据类型是存储在堆内存中(引用数据类型,引用内存地址)
示例代码:
function Fn() { }
因为
Fn
是一个对象,所有它有prototype
属性,又因prototype存放的是一个对象的地址
,所以Fn.prototype指向该对象
,又因为constructor指向Fn,所以存放的是Fn的地址
内存表现:
-
使用 new关键字创建实例对象 p1,p2
示例代码:
function Fn(name, age) { this.name = name; this.age = age; } p1 = new Fn('why', 18); p2 = new Fn('ty', 20);
-
当使用
new
关键字调用Fn
创建p1
和p2
时,就会如前言(构造函数的执行)进行执行 -
Fn.prototype
===p1.__proto__
是因为他们指向同一个内存空间
内存表现:
-
-
给构造函数
Fn
的原型上添加方法示例代码:
function Fn(name, age) { this.name = name; this.age = age; } //添加方法 Fn.prototype.vue = function () { console.log('computed') } p1 = new Fn('why', 18); p2 = new Fn('ty', 20); p1.vue()// => computed p2.vue()// => computed
如果将vue方法放在
Fn
对象身上(this.vue===function(){console.log(‘computed’)});- 即每一个
new
出来的对象都会拥有该方法,就会浪费内存和影响性能 - 如果某一些对象不需要该方法也会强制进行添加该方法
外什么要将方法添加到原型上?
- 可知,
Fn.prototype
是一片公共空间,而p1
,p2
都可以通过__proto__
进行访问, - 如果
p1
想要使用vue方法,可直接通过p1.vue()
进行调用,如果p1
身上没有vue方法就会去p1.__proto__
上去查找
内存表现:
- 即每一个
-
在原型和实例上分别添加属性:
示例代码:
function Fn(name, age) { this.name = name; this.age = age; } Fn.prototype.vue = function () { console.log('computed'); } Fn.prototype.state = 'China'; p1 = new Fn('why', 18); p2 = new Fn('ty', 20); p1.__proto__.info = 'Chinese'//等价于 Fn.prototype.info p1.height = 1.88; p2.isAdmin = true; p1.vue()// => computed p2.vue()// => computed
内存表现:
注:
上图有误p1和p2长度因该为3
;
-
查看属性:
示例代码:
function Fn(name, age) { this.name = name; this.age = age; } Fn.prototype.vue = function () { console.log('computed'); } Fn.prototype.state = 'China'; p1 = new Fn('why', 18); p2 = new Fn('ty', 20); p1.__proto__.info = 'Chinese' p1.height = 1.88; p2.isAdmin = true; p1.vue()// => computed p2.vue()// => computed console.log(p1.state = '中国'); console.log('p1:', p1) console.log(p2.state)// => 'China'
console.log(p1.state = '中国');// console.log('p1:', p1)
原本
p1
是没有state属性的,只有原型上有,所以执行p1.state = '中国'
并不是对p1的属性进行修改,而是在给p1对象添加state属性并赋值为’中国’console.log(p2.state)
因为
p2
对象本身是没有state属性的,所有p2.state
访问的是原型身上的state属性()当想要调用某个对象身上的方法或属性时,如果该对象身上没有该方法和属性,就会去原型身上查找
内存表现:
注:
上图有误p1长度为4,p2长度为3
;