关于javascript:原型模式

52次阅读

共计 4358 个字符,预计需要花费 11 分钟才能阅读完成。

每个函数都会创立一个 prototype 属性, 这个属性是一个对象, 蕴含应该由特定援用类型的实例共享的属性和办法。实际上, 这个对象就是通过调用构造函数创立的对象的原型。应用原型对象的益处是, 在它下面定义大的属性和办法能够被对象实例共享。

1. 原来在构造函数中间接赋给对象实例的值, 能够间接赋值给它们的原型, 如下所示:

function Person(){}
Person.prototype.name="张三";
Person.prototype.age=29;
Person.prototype.job="Web 前端开发";
Person.prototype.sayName=function(){console.log(this.name);
}
let person1=new Person();
person1.sayName();// 张三

let person2=new Person();
person2.sayName();// 张三

console.log(person1.sayName===person2.sayName);// true
应用函数表达式也能够:
let Person=function(){};

Person.prototype.name="李四";
Person.prototype.age=20;
Person.prototype.job="IOS 开发";
Person.prototype.sayName=function(){console.log(this.name);
}

let person1=new Person();
person1.sayName(); // 李四

let person2=new Person();
person2.sayName(); // 李四

console.log(person1.sayName===person2.sayName);// true

这里, 所有属性和 sayName()办法都间接增加到了 Person 的 prototype 属性上, 构造函数体中什么都没有。但这样定义之后, 调用构造函数创立的新对象依然领有相应的属性和办法。与构造函数模式不同, 应用这种原型模式定义的属性和办法是由所有实例共享的。因而 person1 和 person2 拜访的都是雷同的属性和雷同的 sayName()函数。要了解这个过程, 就必须了解 ECMAScript 中原型的实质。

2. 了解原型

无论何时, 只有创立一个函数, 就会依照特定的规定为这个函数创立一个 prototype 属性 (指向原型对象)。默认状况下, 所有原型对象主动取得一个名为 constructor 的属性, 指回与之关联的构造函数。对后面的例子而言,Person.prototype.constructor 指向 Person。而后, 因构造函数而异, 可能会给原型对象增加其余属性和办法。
在自定义构造函数时, 原型对象默认只会取得 constructor 属性, 其余的所有办法都继承自 Object。每次调用构造函数创立一个新实例, 这个实例的外部 [[Prototype]]指针就会被赋值为构造函数的原型对象。脚本中没有拜访这个 [[Prototype]] 个性的规范形式, 但 Firefox\Safari 和 Chrome 会在每个对象上暴漏 proto 属性, 通过这个属性能够拜访对象的原型。在其余实现中, 这个个性齐全被暗藏了。关键在于了解这一点: 实例与构造函数原型之间由间接的分割, 但实例与构造函数之间没有。

3. 这种关系不好可视化, 但能够通过上面的代码来了解原型的行为:

构造函数能够是函数表达式
也能够是函数申明, 因而以下两种模式都能够:
/*
   function Person(){}
   let Person=function(){}
*/
function Person(){}
/*
  申明之后, 构造函数就有了一个与之关联的原型对象:
*/

/*
   如前所述, 构造函数有一个 prototype 属性
   援用其原型对象, 而这个原型对象也有一个
   constructor 属性, 援用这个构造函数
   换句话说, 两者循环援用:
   console.log(Person.prototype.constrctor===Person); // true
*/

4. 失常的原型链都会终止于 Object 的原型对象;Object 原型的原型是 null。

console.log(Person.prototype.__proto__===Object.prototype);  // true
console.log(Person.prototype.__proto__.constructor===Object); // true
console.log(Person.prototype.__proto__.__proto__);  // null

5. 构造函数 \ 原型对象 \ 实例是三个齐全不雷同的对象:

 let person1=new Person(),
     person2=new Person();
   console.log(person1 !=Person); // true
   console.log(person1 !=Person.ptototype);  // true
   console.log(Person.prototype !=Person); // true

1. 实例通过 __proto__链接到原型对象, 它实际上指向暗藏个性[[Prototype]]
2. 构造函数通过 prototype 属性链接到原型对象
3. 实例与构造函数没有间接分割, 与原型对象有间接分割
console.log(person1.__proto__===Person.prototype); // true
console.log(person1.__prototype__.constructor===Person); // true

6. 同一个构造函数创立的联赛哥哥实例, 共享同一个原型对象。

console.log(person1.__proto__===person2.__proto__); // true

7. 应用 instanceof 查看实例的原型链中是否蕴含指定构造函数的原型:

console.log(person1 instanceof Person); // true
console.log(person1 instanceof Object); // true
console.log(Person.prototype instanceof Object); //true

8. 对于后面例子中的 Person 构造函数和 Person.prototype, 能够通过图 8-1 看出各个对象之间的关系。

图 8 - 1 展现了 Person 构造函数 \Person 的原型对象和 Person 现有两个实例之间的关系。留神,Person.prototype 指向原型对象, 而 Person.prototype.constrctor 指回 Person 构造函数。原型对象蕴含 constructor 属性和其余起初增加的属性。Person 的两个实例 person1 和 person2 都只有一个外部属性指会 Person.prototype, 而且两者都与构造函数没有间接分割。另外要留神, 尽管这两个实例都没有属性和办法, 但 person1.sayName()能够失常调用。这是因为对象属性查找机制的起因。

9. 尽管不是所有实现都对外裸露了 [[Prototype]], 但能够应用 isPrototypeof()办法确定两个对象之间的这种关系。实质上,isPrototypeof()会在传入参数的 [[Prototype]]指向调用它的对象时返回 true, 所下所示:

console.log(Person.prototype.isPrototype(person1)); // true
console.log(Proson.prototype.isPrototype(person2)); // true

这里通过原型对象调用 isPrototypeof()办法查看了 person1 和 person2。因为这两个例子外部都有链接指向 Person.prototype, 所以后果都返回 true。

10.ECMAScript 的 Object 类型有一个办法叫 Object.getPrototypeof(), 返回参数的外部个性 [[Prototype]]的值。例如:

console.log(Object.getPrototypeof(person1)==Person.prototype); //true
console.log(Object.getPrototypeof(person1).name);// 李四

第一行代码简略确认了 Object.getPrototypeof()返回的对象就是传入对象的原型对象。第二行代码获得了原型对象上的 name 属性的值, 即 “ 李四 ”。应用 Object.getPrototypeof()能够不便地获得一个对象的原型, 而这在通过原型实现继承时显得尤为重要。

11.Object 类型还有一个 setPrototypeof()办法, 能够向实例的公有个性 [[Prototypeo]]写入一个新值。这样就能够重写一个对象的原型继承关系:

let biped={numLegs:2};
let person={name:"Chen"}
Object.setPrototypeof(person,biped);

console.log(person.name); // Chen
console.log(person.numLegs); // 2
console.log(Object.getPrototypeof(person)===biped); //true

正告:Object.setPrototypeof()可能会重大影响代码性能。Mozilla 文档说得很分明:” 在所有浏览器和 JvavaScript 引擎中, 批改继承关系的影响都是奥妙且深远的。这种影响且不仅时执行 Object.setPrototypeof()语那么简略, 而且会波及所有拜访了那些批改过 [[Prototype]]” 的对象的代码。

12. 为防止应用 Object.setPrototypeof()可能造成的性能降落, 能够通过 Object.create()来创立一个新对象, 同时为其指定原型:

let biped={numLegs:2}
let person=Object.create(biped);
person.name="Chen";

console.log(person.name); // Chen
console.log(person.numLegs); // 2
console.log(Object.getPrototypeof(person)===biped); // true

13. 本期的分享到了这里就完结啦, 心愿对你有所帮忙, 让咱们一起致力走向巅峰!

正文完
 0