本文参考自《javaScript 高级程序设计》第三版,俗称“红宝书”。
graph TB
A[创建对象的形式]
A --> B(字面量)
A --> C(工厂模式)
A --> D(构造函数模式) --> E(new Object)
A --> F(原型模式)
style C fill:#2ff,fill-opacity:0.1,stroke:#faa,stroke-width:4px,color:blue
style D fill:#2ff,fill-opacity:0.1,stroke:#faa,stroke-width:4px,color:blue
style F fill:#2ff,fill-opacity:0.1,stroke:#faa,stroke-width:4px,color:blue
上面重点比拟工厂模式、构造函数模式、原型模式。
工厂模式
定 义: 工厂模式是软件工程畛域的一种设计模式。
优 点: 可屡次调用函数, 可解决创立多个类似对象的问题。
缺 点: 无奈解决对象辨认问题(无奈晓得一个对象的类型)。
function Student(name,age,hobby){let p = new Object(); // 显示地创立一个对象
p.name = name;
p.age = age;
p.hobby = function(){console.log(` 我的喜好是 ${hobby}`);
}
return p;
}
let studentA = Student('xiaoMing',10,'drawing')
let studentB = Student('xiaoHua',12,'dance')
构造函数模式
应用构造函数重写下面的例子:
function Student(name,age,hobby){
this.name = name;
this.age = age;
this.hobby = function(){console.log(` 我的喜好是 ${hobby}`);
}
}
let studentA = new Student('xiaoMing',10,'drawing');
let studentB = new Student('xiaoHua',12,'dance');
/***************** 检测实例类型 *****************/
console.log(studentA instanceof Student); // true
console.log(studentB instanceof Student); // true
console.log(studentA instanceof Object); // true
console.log(studentB instanceof Object); // true
/************ 不同实例上的同名函数是不相等的 ************/
console.log(studentA.hobby === studentB.hobby); // false
构造函数的函数名第一个字母始终大写。
构造函数自身也是函数,是能够用来创建对象的函数。
new 操作符经验步骤:
- 创立一个对象。
- 将构造函数的作用域赋给新对象(所以,this 指向新对象)。
- 为新对象增加属性(即:执行构造函数中代码)。
- 返回新对象。
优 点 : 能够将它的实例标识为一种特定的类型。
缺 点 : 每个办法都要在实例上创立一遍。不同实例上的同名函数是不相等的。
如果将构造函数外部的 自定义函数 定义在构造函数里面的作用域中,将导致这些自定义函数的封装性极差。
原型模式
定 义 : 每个函数都有一个 prototype(原型) 属性,该属性是一个指向特定对象(函数的原型对象)的指针。
优 点: 所有对象实例能够共享原型对象的属性和办法。
function Student(){}
Student.prototype.name = 'Jack';
Student.prototype.age = 12;
Student.prototype.hobby = function (){return this.name;};
let studentD = new Student();
let studentE = new Student();
/************** 实例共享原型对象的办法 **************/
console.log(studentD.hobby === studentE.hobby); // true
1. 原型对象的了解
定 义 : 只有函数一旦创立,就会依据特定的规定为该函数创立一个 prototype 属性,该属性
指向函数的原型对象。
所有 原型对象 会默认只取得一个 constructor(构造函数)属性(指针): 该属性指向 prototype 属性所在函数; 其余办法则从 Object 继承而来。
构造函数 constructor : 当调用构造函数创立一个新实例后, 该实例外部蕴含一个指针(指向构造函数的原型对象)。ECMA-262 第 5 版定义这个指针为[[prototype]], 但浏览器反对__proto__来拜访。
isPrototypeOf() 检测一个对象是否存在于另一个对象的原型链中。
console.log(Student.prototype.isPrototypeOf(studentE) ); // true
console.log(Student.prototype.isPrototypeOf(studentD) ); // true
Object.getPrototypeOf() 获得一个对象的原型
console.log(Object.getPrototypeOf(studentD) );
如下图所示:
对象属性的搜寻
当拜访一个对象的属性时: 首先会在该实例对象下面搜寻指定的属性名,
如果找到了,则返回该属性值; 否则 就会在该实例对象的原型对象上持续搜寻, 找到了就
返回该属性值, 没找到返回 undefined。
实例属性 与 原型对象属性的分割
当为实例对象增加一个属性时, 那么新增的这个属性会屏蔽原型对象中的 同名属性 ,
此时在实例对象中无法访问原型对象中的同名属性。在实例对象中给这个新增的属性设置任何值都不会影响到原型对象中同名属性。
如果应用 delete 在实例对象中删除一个属性后,那么在原型对象中能够从新访此 同名属性。
hasOwnProperty() 检测一个属性是否存在实例对象中
上面代码举例说明
function Student(){}
Student.prototype.name = 'Jack';
Student.prototype.age = 12;
Student.prototype.hobby = function (){return this.name;};
let studentSth = new Student(); // 创立一个实例
studentSth.grade = 90; // 在实例对象中增加一个属性 grade
/*** 实例 studentSth 对象 中存在属性 grade ***/
console.log(studentSth .hasOwnProperty('grade')); // true
/* 实例 studentSth 对象 中不存在属性 name*/
console.log(studentSth.hasOwnProperty('name')); // false
2. 原型对象与 in 操作符
for(let attribute in studentSth){console.log(attribute);
}
/** 顺次打印出该实例对象及实例原型对象中的属性名称 **/
// grade
// name
// age
// hobby
Obeject.keys()
/*** 在实例上调用 ****/
console.log(Object.keys(studentSth)); // ["grade"]
/**** 在实例的原型对象上调用 ****/
console.log(Object.keys(studentSth.__proto__)); // ["name", "age", "hobby"]
Object.getOwnPropertyNames()
/*** 在实例上调用 ****/
console.log(Object.getOwnPropertyNames(studentSth)); // ["grade"]
/**** 在实例的原型对象上调用 ****/
console.log(Object.getOwnPropertyNames(studentSth.__proto__));
// ["constructor", "name", "age", "hobby"]
// constructor 是不可枚举属性
3. 原型的动态性
随时能够为原型对象增加属性和办法, 并且,
批改能立刻在所有实例对象中反馈进去。
4. 原生对象的原型
通过原生对象的原型,能够获取默认办法的援用,也可自定义新的办法。
String.prototype.say = function (){console.log('批改了原生对象的原型属性');
}
let str = 'this is a';
str.say() // 批改了原生对象的原型属性
5. 原型对象的问题
问题一 : 省略了为构造函数传递初始化参数。
问题二: 属性共享的个性。