工厂设计模式
使用工厂函数批量创建对象 工厂造出来的都是Object实例,创建对象都是Object的实例
function Person(name,age,gender){
return {
name:name,
age:age,
gender:gender,
sayName : function(){
console.log(this.name)
}
}
}
对于公共的方法会造成冗余,每次实例化都会在堆中创建一个方法实例方法
针对方法冗余 将方法声明为公共方法
var sayName = function(){
console.log(this.name)
}
function Person(name,age,gender){
return {
name:name,
age:age,
gender:gender,
sayName:sayName
}
}
无法区分种类 工厂方法模式return返回的是Object()的实例对象,而非Person()的实例对象-->(牵扯原型链继承)
构造函数模式
构造函数模式 使用new关键词批量创建对象 区分种类
解决了区分种类,没有解决方法冗余的问题
方法声明在全局,使用方法不区分Object或者Person
var sayName = function(){
console.log(this.name)
}
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = sayName
}
// new关键字作用 自动创建一个构造函数实例对象,区分种类
// 将this指向实例对象,执行函数体代码,返回实例对象
let p1 = new Person('terry',12,'male')
let p2 = new Person('jerry',18,'female')
console.log(p1,p2)
console.log(p1.sayName,p2.sayName)
构造函数与普通函数唯一的区别就是调用方式不同。普通函数只要使用 new 操作符调用就是构造函数,而不使用 new 操作符调用的函数就是普通函数。
公共方法实际上只能在一个对象上调用。如果这个对象需要多个方法,那么
就要在全局作用域中定义多个公共方法函数。这会导致自定义类型引用的方法冗余。
原型模式
原型模式,将实例所有的属性和方法全都写在原型对象中
可以区分种类,解决方法冗余问题
使得方法sayName仅为Person所使用
原始原型模式不对构造函数作任何处理,所有方法,属性都写在原型对象中
function Person(){
}
Person.prototype.name = 'jerry';
Person.prototype.age = 18;
Person.prototype.gender = 'male'
Person.prototype.sayName = function(){
console.log(this.name);
}
var p1 = new Person();
var p2 = new Person();
// 创建出的实例属性,方法都是一致的
console.log(p1,p2)
console.log(p1.name,p2.name)
console.log(p1.sayName,p2.sayName)
Person.prototype.father = []
// p1的父亲tom
p1.father.push('tom')
// p1的父亲成为所有Person实例的父亲
console.log(p1.father,p2.father)
原型模式弱化了向构造函数传递初始化参数的能力, 会导致所有实例默认都取得相同的属性值 。
原型上的所有属性是在实例间共享的 ,利弊都很显著。
对于不同的实例应该有属于自己独特的属性副本才对。
组合模式(构造函数+原型模式)
结合构造函数模式和原型模式的特点
构造函数用于定义实例属性
原型模式用于定义方法和共享属性。
function Person(name,age,gender){
this.name=name;
this.age=age;
this.gender=gender
}
// 公共方法
Person.prototype.sayName=function(){
console.log(this.name)
}
// 公共属性
Person.prototype.type = 'Person实例'
原型
小结:在父母眼中的孩子,在儿女眼里是父母(原型继承),而在路人眼里都是陌生人
简要分析
原型、实例对象、对象原型:原型是给实例对象提供共享属性方法的对象,函数中都有一个指针prototype属性,指向的是一个原型对象。每个实例对象中有一个指针__prototype__属性,它指向它的prototype对象。
原型继承、原型链:原型对象也有自己的原型,将原型对象理解为也是一个实例对象,原型对象通过指针__prototype__属性指向它的原型,层层指向,最终指向为空null
引入:
// 构造函数是一种特殊的函数,主要用来初始化对象
// 使用构造函数来快速创建多个类似的对象,属性类似值不同
// 构造函数约定 1.命名大写字母开头 2.以new开头创建 new关键字调用函数的行为为实例化
//构造函数内部无需写return ,返回值即为新创建的对象
//实例成员和静态成员
// 实例成员:实例对象上的属性和方法属于实例成员 p.name p.salary =() => { }
// 静态成员:构造函数中的属性和方法属于静态成员 静态成员只能通过构造函数访问,
// 静态方法中的this指向构造函数 Pig.eyes = Pig.sayhi = () => { }
// js通过构造函数来实现面向对象的封装性,存在内存浪费
<!-- 构造函数通过原型分配的函数是所有对象所共享的 -->
<!-- 每一个构造函数都有一个prototype属性,指向另一个对象,称为原型对象 -->
<!-- 原型对象可以挂载函数,对象实例化不会多次创建原型上函数 -->
<!-- 构造函数和原型this指向都是实例化对象 -->
function Pig(uname,age){
this.uname = uname;
this.age = age;
}
// 公共的属性写到构造函数上
// 公共的方法写到原型prototype身上
Pig.prototype.sing = function(){
console.log('singing');
}
const ldh = new Pig('ldh',55);
console.log(ldh);
ldh.sing()
// 构造函数和原型的this指向对象
const zxy = new Pig('ldh',55)
<!-- __proto__是JS非标准属性 -->
<!-- 对象原型指向构造函数的原型对象 -->
function Star(){
}
const ldh = new Star()
console.log(ldh.__proto__);
console.log(ldh.__proto__ ==Star.prototype) //true
console.log(Star.prototype.constructor == Star); //true
// 构造函数都有它的原型方法 Star.prototype
// 构造函数创建实例对象 new Star
// 原型对象的属性指向它的构造函数 prototype.constructor
// 实例对象的属性指向它的原型 ldh.__proto__
// 实例对象的构造函数 ldh.__proto__.constructor
原型角色
构造函数
构造对象的函数
每一个构造函数都有一个原型属性prototype,指向它的原型对象。
在构造函数中的属性方法是静态属性和静态方法,在其中的属性方法只能通过构造函数本身调用
* 修改属性默认特性:构造函数本身的方法 静态方法只能由构造函数本身调用
* Object.defineProperty(目标对象,属性,{配置对象})
*/
Object.defineProperty(obj,'name',{
configurable:false,//默认对象属性是可以删除的 默认值true 改为false后该属性无法删除
writable:true,//表示当前属性是否可写 默认值true
enumerable:true,//表示当前属性是否可枚举 默认值true 可枚举属性可以使用for in循环遍历
value:'terry',//给name属性设置属性值
});
/**
* 读取整个对象中属性特性 getOwnPropertyDescriptors(obj)
*/
console.log(Object.getOwnPropertyDescriptors(obj));
这是两个例子,构造函数中的静态方法defineProperty getOwnpropertyDescriptors只能通过构造函数Object访问
构造函数可以创建多个实例,每个实例可以自己添加属性(自有属性)、也可以继承构造函数的属性
原型(原型对象):构造函数 .prototype
// 该构造函数所对应的原型对象
console.log(Object.prototype);
// 每一个原型对象都有指针指向构造者
console.log(Object.prototype.constructor === Object);
原型对象是构造函数通过指针prototype指向的, 职责在于提供给其他对象共享属性,构造函数通过原型分配的函数是所有实例对象所共享的,原型对象常用来挂载封装好的方法,经挂载的方法可以被所有的实例对象访问使用。
写在原型对象中的属性和方法是实例属性和方法
function Pig(uname,age){
this.uname = uname;
this.age = age;
}
// 公共的属性写到构造函数上
// 公共的方法写到原型prototype身上
Pig.prototype.sing = function(){
console.log('singing');
}
const ldh = new Pig('ldh',55);
ldh.sing()
官方定义:每个函数都有一个 prototype 属性,这个属性是一个指针,指向一个对象,这个对象称为原型对象;每个对象都有一个 [[Prototype]] 属性,它同样是个指针,指向原型对象。
constructor构造者属性
小结:孩子的父母是谁,我是谁的孩子,通过我身上的血脉(constructor)指向
constructor属性作用:在原型对象中作为一个指针,指向原型对象的构造函数。
实例对象(对象原型)
var obj = new Object();
var obj1 = {};//也是构造函数创建
由构造函数new创建的实例对象
实例对象可以访问原型对象prototype的属性和方法和构造函数的属性方法
对象原型:__proto__原型
实例对象都会有一个指针属性__proto__指向构造函数的原型对象
实例对象通过指针属性__proto__原型来使用构造函数prototype原型对象的属性和方法
同一个构造函数创建的实例对象.__proto__和构造函数.prototype是等价的
console.log(Object.prototype.constructor === Object);
// 每一个实例对象都有一个指针指向原型对象
console.log(obj.__proto__ === Object.prototype);
console.log(obj.__proto__.toString() === Object.prototype.toString());
console.log(obj.__proto__.valueOf() === Object.prototype.valueOf());
// 对象实例可以访问的方法和属性 实例方法也叫原型方法
以上结果均为true
__proto__原型的contructor属性
实例对象中指针__proto__属性(指向原型对象)也有一个constructor属性,来指向创建该实例对象的构造函数,constructor属性最终都指向构造函数
// 每一个原型对象都有指针指向构造者
console.log(Object.prototype.constructor === Object);
原型继承、原型链:
将一个对象设置为另一个对象的原型
当访问一个对象的属性(方法)时,首先查找这个对象本身,当本身没有这个属性方法时,向它的上一级原型对象查找,通过__proto__指向的prototype原型对象,依次类推
__proto__对象原型提供一个 原型对象的查找机制
var str = 'hello';
let arr = Array.from(str);
var arr1 =new Array();
console.log(arr1.__proto__.constructor === arr.__proto__.constructor);
console.log(arr1.constructor === arr.constructor);
打印结果都为true
注意实例对象是没有constructor属性的,但是为什么打印结果也为true呢
因为它继承了它的原型arr.__proto__(构造函数.propertype)的属性和方法