创建对象的几种方式
通过Object
<!--
方式一: Object构造函数模式
* 套路: 先创建空Object对象, 再动态添加属性/方法
* 适用场景: 起始时不确定对象内部数据
* 问题: 语句太多
-->
/*
一个人: name:"Tom", age: 12
*/
// 先创建空Object对象
var p = new Object()
p = {} //此时内部数据是不确定的
// 再动态添加属性/方法
p.name = 'Tom'
p.age = 12
p.setName = function (name) {
this.name = name
}
//测试
console.log(p.name, p.age)
p.setName('Bob')
console.log(p.name, p.age)
方式二:对象字面量
<!--
方式二: 对象字面量模式
* 套路: 使用{}创建对象, 同时指定属性/方法
* 适用场景: 起始时对象内部数据是确定的
* 问题: 如果创建多个对象, 有重复代码
-->
var p = {
name: 'Tom',
age: 12,
setName: function (name) {
this.name = name
}
}
//测试
console.log(p.name, p.age)
p.setName('JACK')
console.log(p.name, p.age)
var p2 = { //如果创建多个对象代码很重复
name: 'Bob',
age: 13,
setName: function (name) {
this.name = name
}
}
方式三:工厂模式
- 方式:通过工厂函数动态创建对象并返回。
返回一个对象的函数,就是工厂函数。
-
适用场景: 需要创建多个对象。
-
问题: 对象没有一个具体的类型,都是Object类型。
由于这个问题的存在,工厂模式用得不多。
<!--
方式三: 工厂模式
* 套路: 通过工厂函数动态创建对象并返回
* 适用场景: 需要创建多个对象
* 问题: 对象没有一个具体的类型, 都是Object类型
-->
function createPerson(name, age) { //返回一个对象的函数===>工厂函数
var obj = {
name: name,
age: age,
setName: function (name) {
this.name = name
}
}
return obj
}
// 创建2个人
var p1 = createPerson('Tom', 12)
var p2 = createPerson('Bob', 13)
// p1/p2是Object类型
function createStudent(name, price) {
var obj = {
name: name,
price: price
}
return obj
}
var s = createStudent('张三', 12000)
// s也是Object
方式四:自定义构造函数
<!--
方式四: 自定义构造函数模式
* 套路: 自定义构造函数, 通过new创建对象
* 适用场景: 需要创建多个类型确定的对象
* 问题: 每个对象都有相同的数据, 浪费内存
-->
//定义类型
function Person(name, age) {
this.name = name
this.age = age
this.setName = function (name) {
this.name = name
}
}
var p1 = new Person('Tom', 12)
p1.setName('Jack')
console.log(p1.name, p1.age)
console.log(p1 instanceof Person)
function Student(name, price) {
this.name = name
this.price = price
}
var s = new Student('Bob', 13000)
console.log(s instanceof Student)
var p2 = new Person('JACK', 23)
console.log(p1, p2)
方式四引入了继承。
继承的几种方式
原型链继承
//父类构造函数
function Super(){
this.color = ['red','black','blue'];
}
//子类构造函数
function Sub(){}
//子类继承父类
Sub.prototype = new Super();
//新建一个子类的实例
var ins1 = new Sub();
ins1.color.push('green');
var ins2 = new Sub();
//因为color为一个引用对象,ins1和ins2的color都指向同一个地址,修改一个就会修改所有实例的color
ins2.color;//'red,belck,blue,green'
通过构造函数继承
在子类型构造函数中通用call()调用父类型构造函数
防止在原型链模式中,所有子类的实例公用一个父类构造函数的引用对象。
使用 借用构造函数 的方式实现继承。在子类构造函数的内部,调用超类的构造函数构造。如下:
//父类构造函数
function Super(){
this.color = ['red','black','blue'];
}
//子类构造函数
function Sub(){
//调用父类构造函数实现继承
Super.call(this);
}
//新建一个子类的实例
var ins1 = new Sub();
ins1.color.push('green');
ins1.color;//'red,belck,blue,green'
//在实例ins2上重新执行Super构造函数,重新初始化对象,ins2拥有自己的color属性
var ins2 = new Sub();
ins2.color;//'red,belck,blue'
组合继承
结合 原型链+借用构造函数 方式,实现组合继承。
借用构造函数使每个实例拥有自己的属性;原型链使每个实例可以共用方法,实现方法的复用。
//Super中定义属性name
function Super(name){
this.name = name;
this.color = ['red','green'];
}
//Super的原型中定义方法
Super.prototype.sayname = function(){
console.log(this.name);
}
function Sub(name, age){
//通过构造函数的方式继承Super的属性
Super.call(this, name);
//定义自己的属性
this.age = age;
}
//通过原型链的方式继承方法
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
var ins1 = new Sub('ins1',18);
缺点:会调用两次超类的构造函数,一次在Super.call(this, name); 一次在Sub.prototype = new Super();
导致Sub原型上有属性name、age,Sub实例上也有属性name、age。
原型式继承
借助原型,基于已有的对象创建新对象。
object.Create(参数1,参数2); 参数1用作新对象的原型对象,参数2为新对象定义额外属性的对象。
传入一个参数的情况:
var person = {
name: 'person'
}
//基于已有的person对象,创建一个新的anthorp对象
var anthorp = Object.create(person);
//相当于如下语句
function object(o){
function F(){};
F.prototype = o;
return new F();
}
//得到一个以person为原型的构造函数的实例
var anthorp = object(person);
传入两个参数的情况:
var person = {
name: 'person'
}
//基于已有的person对象,创建一个新的anthorp对象,覆盖之前的name,并新增age
var anthorp = Object.create(person,{
name: {
value: 'anthorp'
},
age: {
value: 18
}
});
anthorp.name; //anthorp
寄生继承
创建一个用于封装继承过程的函数,在函数内部增强对象。并返回对象。
talk is cheap, show me code。
//创建一个用于封装继承过程的函数,传入obj
function create(obj){
//通过调用object方法以obj为基础创建一个新对象。此处object表示任何能够返回新对象的函数
var clone = object(obj);
//以为对象新增方法的方式增强对象
clone.sayHi = function(){
console.log('hi');
};
//返回对象
return clone;
}
//使用create函数
//一个基础对象person
var person = {
name: 'person'
};
//基于基础对象使用寄生模式生成的新对象
var anthorp = create(person);
anthorp.sayHi();
寄生组合式继承
上面讲到组合继承的缺点:会调用两次超类的构造函数。
寄生组合式继承:借用构造函数来继承属性,通过原型链混成来继承方法。不必在子类原型中调用超类的构造函数。使用寄生继承来继承超类的原型,再将结果制定给子类的原型。
//寄生组合式继承
function inheritProto(Sub, Super){
//根据Super的原型创建一个新的对象proto
var proto = object(Super.prototype);
//增强新对象,为其赋construtor值
proto.constructor = Sub;
//将新对象赋值给子类的原型。
Sub.prototype = proto;
}
//使用
//Super中定义属性name
function Super(name){
this.name = name;
this.color = ['red','green'];
}
//Super的原型中定义方法
Super.prototype.sayname = function(){
console.log(this.name);
}
function Sub(name, age){
//通过构造函数的方式继承Super的属性,只在此处调用一次Super构造函数
Super.call(this, name);
//定义自己的属性
this.age = age;
}
//调用函数,实现继承。代替之前的Sub.prototype = new Super();语句,防止Super构造函数调用两次
inheritProto(Sub,Super);