让学习“上瘾”,成为更好的自己!!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>原型模式</title>
</head>
<body>
<script>
/*
每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象包含了可以由特定类型的所有实例共享的属性和方法
使用原型对象的好处:可以让所有对象的实例共享它所包含的属性和方法
*/
// function Person() {
// }
// Person.prototype.name = 'kai';
// Person.prototype.age = 23;
// Person.prototype.job = 'teacher';
// Person.prototype.sayName = function () {
// alert(this.name);
// }
// var p1 = new Person();
// // p1.sayName();
// var p2 = new Person();
// p2.sayName();
// 1, 理解原型对象
/*
创建自定义的构造函数后,其原型对象默认只会取得constructor属性;而其他的方法,则都是从Object继承而来的
[[Prototype]]:但调用构造函数创建一个新实例后,该实例将包含一个的指针([[Prototype]]),指向构造函数的原型对象 --> __proto__
*/
// (1) isPrototypeOf() --> 如果[[Prototype]]指向调用isPrototypeOf()方法的对象(Person.prototype),那么这个方法就返回true
// alert(Person.prototype.isPrototypeOf(p1)); // true
// (2) Object.getPrototypeOf() --> 返回[[Prototype]]的值,即这个对象的原型 【ECMAScript 5】
// alert(Object.getPrototypeOf(p1));
// alert(Object.getPrototypeOf(p1) === Person.prototype);
// console.log(Person.prototype);
// console.log(Object.getPrototypeOf(p1));
// 每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。
// 搜索首先从对象实例本身开始,如果在实例中找到了具有给定名字的属性,则返回该属性的值;
// 如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性,
// 如果在原型对象中找到了这个属性,则返回该属性的值
// (3) 不能通过对象实例重写原型中的值!!
// 如果在实例中添加了一个属性,而该属性与实例原型中的一个属性重名,则就在实例中创建了该属性,该属性会“屏蔽”原型对象中的那个属性
// function Person() {
// }
// Person.prototype.name = 'kai';
// Person.prototype.age = 23;
// Person.prototype.job = 'teacher';
// Person.prototype.sayName = function () {
// alert(this.name);
// }
// var p1 = new Person();
// var p2 = new Person();
// p1.name = 'lice'; // “屏蔽”原型对象中的那个属性
// console.log(p1.name);
// console.log(p2.name);
// (4) delete: 完全删除实例属性,进而能够重新访问原型中的属性
// function Person() {
// }
// Person.prototype.name = 'kai';
// Person.prototype.age = 23;
// Person.prototype.job = 'teacher';
// Person.prototype.sayName = function () {
// alert(this.name);
// }
// var p1 = new Person();
// var p2 = new Person();
// p1.name = 'lice'; // “屏蔽”原型对象中的那个属性
// console.log(p1.name);
// console.log(p2.name);
// delete p1.name;
// console.log(p1.name);
// (5) hasOwnProperty() --> 检测一个属性是存在实例中,还是存在于原型中
// 只在给定属性存在于对象实例中时,才会返回true
// function Person() {
// }
// Person.prototype.name = 'kai';
// Person.prototype.age = 23;
// Person.prototype.job = 'teacher';
// Person.prototype.sayName = function () {
// alert(this.name);
// }
// var p1 = new Person();
// var p2 = new Person();
// console.log(p1.hasOwnProperty('name')); // false --> 来自原型
// p1.name = 'lice';
// console.log(p1.hasOwnProperty('name')); // true --> 来自实例
// 通过使用hasOwnProperty()方法,什么时候访问的是实例的属性,什么时候访问的是原型属性就一清二楚了!!
// 2, 原型与in操作符
// 2种方式使用in操作符:单独使用和for-in循环中使用
// (1) 单独使用:in 操作符:通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中
// function Person() {
// }
// Person.prototype.name = 'kai';
// Person.prototype.age = 23;
// Person.prototype.job = 'teacher';
// Person.prototype.sayName = function () {
// alert(this.name);
// }
// var p1 = new Person();
// var p2 = new Person();
// console.log(p1.hasOwnProperty('name')); // false
// console.log('name' in p1); // true
// p1.name = 'lice';
// console.log(p1.hasOwnProperty('name')); // true
// console.log('name' in p1); // true
// 同时使用hasOwnProperty() 和 in 操作符,可以确认该属性是存在于对象中,还是存在原型中
// function hasPrototypeProperty(obj, prop){
// console.log((prop in obj) && ! obj.hasOwnProperty(prop)) ;
// }
// hasPrototypeProperty(p2, 'name'); // true
// hasPrototypeProperty(p1, 'name'); // false
// (2) for-in: 返回所有能够通过对象访问的、可枚举的属性,包括实例和原型中的属性
// 屏蔽了原型中不可枚举属性(即将[[Enumerable]]标记的属性)
// function Person() {
// }
// Person.prototype.name = 'kai';
// Person.prototype.age = 23;
// Person.prototype.job = 'teacher';
// Person.prototype.sayName = function () {
// alert(this.name);
// }
// var p1 = new Person();
// var p2 = new Person();
// console.log(p1);
// for (var prop in p1){
// console.log(prop);
// }
// (3) Object.keys()方法 --> 接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组
// 取得对象上所有可枚举的“实例属性”!!!
// function Person() {
// }
// Person.prototype.name = 'kai';
// Person.prototype.age = 23;
// Person.prototype.job = 'teacher';
// Person.prototype.sayName = function () {
// alert(this.name);
// }
// var p1 = new Person();
// var p2 = new Person();
// var keys = Object.keys(Person.prototype);
// console.log(keys); // ["name", "age", "job", "sayName"]
// p1.name = 'lcie';
// p1.age = 34;
// p1.salary = 7877;
// console.dir(p1);
// console.log(Object.keys(p1)); // ["name", "age", "salary"]
// (4) Object.getOwnPropertyNames() --> 得到所有实例属性,无论是否可枚举
// var keys1 = Object.getOwnPropertyNames(Person.prototype);
// console.log(keys1); // ["constructor", "name", "age", "job", "sayName"]
// 结果包含了不可枚举属性constructor属性
// 【总结】Object.keys()和Object.getOwnPropertyNames()都可以替换for-in循环!!
// 3, 更简单的原型语法
// 封装原型的功能:用一个包含所有属性和方法对象字面量来重写整个原型对象
// function Person(){
// }
// Person.prototype = {
// constructor: Person, // 重要时设置回来
// // 默认情况下,constructor属性是不可枚举的。这里重设constructor的值会导致其变成可以枚举
// name:'kai',
// age: 23,
// job: 'teacher',
// sayName: function(){
//
// console.log(this.name);
// }
// };
// 每创建一个函数,就会同时创建他的prototype对象,这个对象会自动获得constructor属性
// 对象字面量重写整个原型对象,此时constructor变成了新对象的constructor属性(指向Object构造函数),不在指向Person函数
// 4, 原型的动态性 --> 先创建了实例后修改原型
// (1)由于在原型中查找值的过程是“一次搜索”,因此我们可以对原型对象所作的任何修改都能够立即从实例上反映出来(即使是先创建了实例后修改原型也是这样)
// function Person() {
// }
// var p1 = new Person();
// Person.prototype.sayHi = function () {
// alert('Hi');
// }
// p1.sayHi(); // 不出错
// 即使是先创建了实例后修改原型也是这样
// --> 原因:实例和原型之间的松散连接关系
// 当我们调用person.sayHi()时,首先会在实例中搜索名为sayHi()的属性,
// 在没找到的情况下,会继续搜索原型。因为实例与原型之间的连接只不过是一个指针,因此可以在原型中找到新的sayHi属性并返回保存在那里的函数
// (2)调用构造函数时会为实例添加一个指向最初原型的[[Prototype]]指针,把原型修改为另一个对象就等于切断了构造函数与最初原型之间的联系
// 【注意】实例中的指针仅指向原型,而不指向构造函数!!
// function Person() {
// }
// var p1 = new Person();
// Person.prototype = {
// constructor: Person,
// name: 'kai',
// age: 23,
// job: 'teacher',
// sayName: function () {
// console.log(this.name);
// }
// };
// console.dir(Person.prototype);
// console.dir(p1.__proto__);
// console.log(p1.__proto__ == Person.prototype); // false
// p1.sayName(); // 出错
// 重写原型对象切断了现有(重写后的)原型与任何之前已经存在的实例之间的联系;
// 实例引用的仍然是最初的原型,而构造函数的prototype始终指向了新的原型对象
// 5, 原生对象的原型 --> 不建议修改原生对象的原型!!
// 6, 原型对象的问题
// 原型模式的缺点:
// 1, 所有实例都将共享构造函数原型对象中设置的属性和方法
// 2, 对于包含引用类型值的属性,实例不能拥有自己的属性
function Person() {
}
var p1 = new Person();
Person.prototype = {
constructor: Person,
name: 'kai',
age: 23,
job: 'teacher',
friends:['saa', 'sjie'], //
sayName: function () {
console.log(this.name);
}
};
var p1 = new Person();
var p2 = new Person();
p1.friends.push('van');
console.log(p1.friends); // ["saa", "sjie", "van"]
console.log(p2.friends); // ["saa", "sjie", "van"]
console.log(p1.friends === p2.friends); // true
</script>
</body>
</html>```