JS创建对象和继承

创建对象

工厂模式

在普通函数中创建一个新对象,为其添加属性和方法,然后返回。在调用的时候直接调用函数,不需要使用new操作符。

function factory(name){
	var o = new Object();
	o.name = o;
	o.sayName = function(){
		console.log(this.name);
	}
	return o;
}

var per = factory('hhh');
  • 缺点:批量创建出属性和方法一致的对象,创建出的对象不能确定其类型,每次创建一个对象都需要创建一次方法,方法复用度不高。
//稍加改进型:
function factory(name){
	var o = new Object();
	o.name = o;
	o.sayName = sayName;
	return o;
}
function sayName(){
 		console.log(this.name);
}
var per = factory('hhh');
  • 缺点:每个方法都需要在外部写,会添加很多全局变量。
构造函数

和工厂模式的区别,利用了new操作符,使用this关键字,在创建对象的时候要使用new操作符,可以确定对象的类型。

  • new操作符实际上的操作是如下几步:
  • 1.创建一个空 对象
	var obj = new Object();
  • 2.将对象的__proto__指向其构造函数的原型;
	obj.__proto__ = constructor.prototype;
  • 3.执行函数代码;
	var ret = constructor.call(obj);
  • 4.返回对象;
	return obj instanceof Object ? ret : obj 

构造函数大致如下:

function Person(name){
	this.name = name;
	this.sayName = function(){
		console.log(this.name);
	}
}

var per = new Person('hhh');
  • 缺点: 函数复用度不高
原型模式

首先,每个对象都有一个constructor属性,指向其构造函数,__proto__属性,指向其原型对象(即其构造函数的原型);而每个函数都有一个prototype属性,指向其原型,而其原型对象都有一个constructor属性指向构造函数。

在访问对象的属性和方法时,如果在对象自身找不到,我们会沿着原型链一层一层向上找,也就是使用函数的prototype属性,利用作用域查询变量规则,在原型上访问对象变量和方法。所以,为了解决函数方法复用的问题,引入了原型模式。在函数的原型上添加属性和方法,在调用时使用new操作符。

	function Person(){}
	Person.prototype.sayName = function(){
		console.log(this.name);
	}
	Person.prototype.name = ['hhh','xxx'];

	var per = new Person();

从代码中,很明显可以看出,如果单纯只使用原型模式来创建对象的话,我们创建对象时无法传入参数,其次,创建的对象没有自己的属性,对于引用类型name来说,当修改name属性(指的是除了赋值之外的修改)时,就等同于修改原型上的name属性。其他的对象的name属性也会改变,因为所有对象都访问的是其原型对象的属性,等同于所有对象都在改变其原型对象的name属性;而好处是,我们的sayName方法们,只需要写在原型上,复用率大大提高。所以,我们通常使用原型模式来创建对象的方法。

组合模式

利用了构造函数来为对象添加属性,弥补了原型模式“属性共用”的问题;而使用原型模式来为对象添加方法,弥补了构造函数方法复用率不高的问题。

	function Person(name){
		this.name = name;
	}
	Person.prototype.sayName = function(){
		console.log(this.name);
	}

	var per = new Person('hhh');
动态模式

在构造函数的基础上,通过添加if语句判断是否需要添加方法。

function Person(name){
    this.name=name;
    if(typeof this.sayName != "function"){
        Person.prototype.sayName = function(){
            console.log(this.name);
        };
    }   
}
var per = new Person('hhh');

只有在sayName方法不存在的情况下,才会将它添加到原型中,方法那段代码只有在初次调用构造函数时才会执行,此后,原型已经初始化完成,不需要再做更改了。

寄生构造模式

可以理解为寄生-构造模式,挂构造函数的名字(使用时用new操作符),而内部实现代码和工厂模式一模一样,所创建出来的实例无法指向构造函数。

稳妥模式

不使用this和new,与寄生构造函数相似。

function Person(name){
	var o = new Object();
	o.sayName = function(){
		console.log(name);
	}
//注意,发现,name属性除了可以通过sayName方法访问到,其他都不行
	return o;
}

var per = Person('hhh');
per.sayName();//	'hhh'

//注意,在此想要更改其name属性
per.name = 'eee';
per.sayName();//	'hhh'
console.log(per.name);		//	'eee'

继承

原型链继承

利用函数的prototype属性,改变子类构造函数的prototype为父类的实例。

	function Father(){
	}
	Father.prototype.sayName = function(){
		console.log(this.name);
	}
	
	function Son(name){
		this.name = name;
	}
	Son.prototype = new Father();   //Son的原型指向一个Father实例,假设叫a
	//a.__proto__ = Father.prototype;
	//所以son实例可以沿着原型链向上找到sayName方法
	var son = new Son('hhhh');
	son.sayName();   // 'hhhh'
  • 缺点:和原型模式一样,引用类型的属性被所有实例共享,以及无法向父构造函数传参。
    继承关系如下:
    在这里插入图片描述
借助构造函数继承

在子类构造函数中调用父类构造函数,从而达到继承父类的目的。

function Father(){
	this.name = ['111','222'];
}

function Son(){
	Father.call(this);
}

var son = new Son();
son.name.push('222');
console.log(son.name);		//['111','222','222'];

var son2 = new Son();
console.log(son2.name);		//['111','222'];

首先为啥在子类中调用父类构造函数可以达到继承的目的?首先,这里的继承是指我创建的实例会有我父类的属性和方法,原因就来自于其内部的Father.call(this)。在使用new操作符时,首先会创建一个空的对象obj,并将其__proto__属性指向其构造函数的原型对象,即Son.prototype,然后就会执行构造函数内部的代码,可以理解为在子类中执行了父类的代码,所以obj就会有name属性,最后返回。

  • 优点: 各个实例有属于自己的属性和方法,没有创建多余的父类的实例,可以向父类构造函数传参。
  • 缺点:在原型链上不会体现和父类的关系,在父类构造函数内部创建的方法,在每次都要创建一次。
组合继承

融合原型链和借助构造函数两者,利用原型链来继承父类方法,利用构造函数来继承父类属性。

function Father(){
	this.name = ['111','222'];
}
Father.prototype.sayName = function(){
	console.log(this.name);
}
function Son(){
	Father.call(this);
}
Son.prototype = new Father();
Son.prototype.constuctor = Son;   //弥补重写Son原型对象的不足
var son = new Son();

缺点:调用了两次父类构造函数。(我觉得没啥其实)

原型式继承

将传入的对象当做创建对象的原型,Object.create的模拟实现

funtion create(o){
	function F(){}
	F.prototype = o;
	return new F();
}
  • 缺点:和原型链继承一个道理,创建的实例共享原型上的所有属性和方法。
  • 优点:不用写多余的代码来创建一个子类和父类实例
寄生式继承

用于封装继承过程的函数,在内部以某种方式做增强对象,最后返回。

function create(o){
	var c = Object.create(o);
	c.sayName = function(){
		console.log(this.name);
	} 
	return clone;
}
  • 缺点:每次创建对象都会创建一次方法。
寄生组合继承
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

function prototype(child, parent) {
    var prototype = object(parent.prototype);
    prototype.constructor = child;
    child.prototype = prototype;
}

funtion Parent(name){
	this.name = name
}
Parent.prototype.sayName = function(){
	console.log(this.name);
}
function Child(name){
	Parent.call(this);
}
// 当我们使用的时候:
prototype(Child, Parent);
  • 优点:只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值