说明
- 接JavaScript — > [学习笔记]观察者模式 & 理解对象 & 工厂模式 & 构造函数模式
- 上一篇构造函数模式创建的实例,不同实例的同一个方法是不相等的,为了解决这个问题.出现了原型模式
1. 原型模式
- 具体做法是,不在构造函数中定义对象实例的信息,而是将这些信息直接添加到
原型对象
中
function Person(){}
Person.prototype.name ="Nicholas";
Person.prototype.age =29;
Person.prototype.job ="Software Engineer";
Person.prototype.sayName = function () {
alert(this.name);
};
var person1 = new Person();
person1.sayName(); // "Nicholas"
var person2 = new Person();
person2.sayName(); // "Nicholas"
console.log(person1.sayName == person2.sayName); // true
1.1 理解原型对象
1.1.1 prototype属性和constructor属性
- 创建一个函数(Person),就会根据一组规则创建一个prototype属性(Person.prototype)
- prototype上面还要一个constructor属性(必带技能),它(Person.prototype.constructor)指向函数Person
- prototype属性上的所有属性和方法会被所有使用new操作符生产的实例(person1, person2)共享
- 实例person1、person2通过
new Person
得到,会有一个[[Prototype]]的指针(在浏览器中通过__proto__访问),它指向Person.prototype
1.1.2 读取某个属性的顺序
- 读取属性的模拟函数如下:
// obj是某个对象, attr是该对象的属性
const getVal = (obj, attr) =>{
// 如果对象中存在该属性则返回
if(obj[attr]) {
return obj[attr]
} else if (obj.constructor === Object) {
// 是Object
return undefined
} else{
// 不是Object,检查其constructor指向的函数
if(obj.constructor[attr]){
return obj.constructor[attr]
} else {
// constructor指向的函数中不存在,顺着 __proto__ 属性找下去.
getVal(obj.__proto__, attr);
}
}
}
说明:
- 所有通过function声明的函数,都继承自Function
- Function继承自Object
1.1.3 说说
通过上面的原型找属性的顺序,可以知道:
- 当使用
new Person
生成实例(person1)的时候,person1会有一个指向Person.prototype的指针__proto__ - 当访问person1的属性或方法的时候,先从实例开始寻找,若找到了则返回,否则会顺着__proto__向上寻找
1.2 区分一个属性是否来自原型
需要明白下面2点:
- 使用
in
操作符会返回所有对象上的属性或者方法 - 使用hasOwnProperty可以确定一个属性是否来自实例对象
// 判断一个熟悉是否来自原型
const fromPrototype = (obj, attr) {
return !obj.hasOwnprototype && (attr in obj);
}
1.3 Object.defineProperty
-
Object.defineProperty
: 可以给一个对象添加属性,并对属性进行描述 - 使用如下:
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
})
1.3.1 数据属性和访问器属性
- JS中的属性类型分为: 数据属性和访问器属性
- 数据属性包括:
- [[Configurabal]]: 是否能被
delete
删除 - [[Numerable]]: 是否能被
for-in
循环访问属性 - [[Writeble]]: 是否可以被修改
- [[Value]]: 属性的值
- 访问器属性: Configurable、Numerable、Get(读取时触发) 、Set(修改时触发)
1.3.2 Object.defineProperty的使用
- 在使用对象字面量对函数的原型进行赋值的时候,会丢失原本的constructor属性
- 使用
Object.defineProperty
为它加上constructor属性
function Person () {}
Person.prototype = {
name: 'marron',
age: 18
}
Object.defineProperty(Person.prototype, constructor, {
numerable: false,
value: Person
})
- constructor是不能被
for-in
循环访问到的 - 在字面量中直接赋值,会被
for-in
循环访问到
1.4 原型的动态性
- 可以先创建实例后修改原型
function Person () {};
cosnt friend = new Person();
Person.prototype.sayHi = () => { console.log('SayHi') };
friend.sayHi();
- 将上述代码修改为下面:
- Person.prototype.sayHi = () => { console.log('SayHi') };
+ Person.prototype = {
+ sayHi: function () {
+ console.log('SayHi');
+ }
+ }
- 此时在调用
friend.sayHi
会报错:Uncaught TypeError: p1.sayHi is not a function
, 原因如下:
- 使用new操作符时: 建立了一个指针 proto, 它指向 Person.prototype(这个是默认建立的),假设在空间A中,即此时 person1 先从自己的空间中取数据,若没有则去 A中找
- 当对Person.prototype进行字面量赋值时, 它改变的是Person.prototype指针的指向,在上面的一系列操作实际是在空间B中.
- 当完成了Person.prototype的字面量赋值后,相当于给Person的原型对象新开辟了一个空间B.而此时person1的__proto__还是指向A.故访问不到B
注:重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系.
栗子好好吃 发布了175 篇原创文章 · 获赞 21 · 访问量 2万+ 私信 关注