js中工厂函数、构造函数、原型及原型链详解

一、工厂函数

其本质就是一个函数,调用时会返回一个对象,当你需要定义多个具有相同属性的的对象时可以使用工厂函数。

  • 优点:解决了创建多个相同属性对象的问题
  • 缺点:无法判断对象的类型,都是object
function fac(){
	return{
		name:'jqy',
		sayName:function(){
			console.log(this.name)
		}
	}
}
let info1 = fac()
let info2 = fac()
console.log(info1 === info2)	//false,创建的对象互不影响
console.log(info1.sayName === info2.sayName)	
//false,虽然方法名相同,但分别是各自的方法(不是共享的)

由于是函数,所以在使用工厂函数创建对象时可以为其传递参数。

function fac(n){
	return{
		name:n,
		age:18
	}
}
let info3 = fac('ooxx')
console.log(info3)	//{name: "ooxx", age: 18}

//或
function fac(n='oo'){
	return{
		n,
		age:18
	}
}
let info4 = new fac()
console.log(info4)	//{n: "oo", age: 18}

二、构造函数和原型

在 ES6之前是没有类的概念的,只有构造函数和原型。
构造函数和类都是用于创建多个对象的,里面存放对象的公共部分(模板)

function Person(name){
	this.name = name;
	this.sayName = function(){
		console.log(this.name);
	}   
}
var person1 = new Person("oo")
var person2 = new Person("xx")
			
person1.sayName();   //oo
person2.sayName();   //xx
  • 优点:解决对象类型识别的问题
  • 缺点:如果构造函数中有方法,会存在内存浪费问题。由于方法属于引用数据类型,所以会在内存中为其单独开辟一块空间进行存储,这样当new多个对象实例时就会开辟多个内存空间去存储一个相同的方法,不但浪费内存,还拉低执行效率(用为开辟空间也要时间)。
  • 注意:虽然标准没有严格规定首字母大写,但构造函数的首写字母通常会大写。
    • 构造函数调用过程中实际会经历以下 4 个步骤:
      ①创建一个新的空对象
      ②将构造函数的作用域赋值给此对象(使 this 指向这个新对象)
      ③执行构造函数中的代码(为这个新对象添加属性)
      ④返回新对象
    • 对比于工厂函数,构造函数使用时需要使用 new ,不需要显式的创建一个对象,需要使用 this ,不需要 return 。
    • 构造函数与其他函数的唯一区别,就在于调用它们的方式不同。只要通过new操作符来调用,那它就可以作为构造函数;
  • 构造函数的属性和方法又被称为成员,成员分为两类:静态成员和实例成员。
    • 实例成员:构造函数内部通过this添加的成员,只能通过实例化的对象访问,如上面的 person1.sayName();使用Person.sayName()会报错,not function。
    • 静态成员:在构造函数本身添加的成员。如Person.hobbies=‘打游戏’;静态成员只能通过构造函数本身来访问。如 console.log(Person.hobbies);使用console.log(person1.hobbies)结果为undefined

  原型: 每个函数都有 prototype 属性,指向一个对象,这个对象包含有所有实例共享的属性和方法。可以解决构造函数每用 new 创建一个对象,就会重新将实例的方法创建一次的问题:

function Person(){
	this.age=17;
}
Person.prototype.age = 21;//原型属性
Person.prototype.name = 'ooxx';//原型属性
Person.prototype.sayHi = function () {//原型方法
	console.log('Hi');
}
let person1 = new Person()
let person2 = new Person()
person1.sayHi();//Hi
console.log(person1.age);//17
console.log(person1.name);//ooxx
console.log(person1.sayHi === person2.sayHi);//true
//原型对象上的属性方法都可以被实例对象继承,如果原型对象上添加的属性与构
//造函数属性名一致,则实例对象以构造函数添加的属性为主。
//上面属于构造函数+原型的方案:属性是私有的,方法是共有的。


Person.show=function(){//静态方法
	console.log('静态方法!')
}
Person.show();//静态方法!
console.log(Person.prototype);//{age: 21, name: 'ooxx', sayHi: ƒ, constructor: ƒ}

  在构造函数的原型定义方法后为什么实例对象可以使用呢(即实例对象怎么使用原型的方法)?因为对象上会有一个__proto__属性(并不是只有实例对象才有,只要是对象就有)指向构造函数的prototype(原型对象)。即对象的__proto__与其构造函数的prototype是等价的。验证如下:

function Demo(){}
let oo = new Demo()
console.log(oo.__proto__ === Demo.prototype);//true

  对象原型__proto__和构造函数原型prototype都会有一个constructor属性,我们称之为构造函数,因为其指向构造函数本身。作用:指出对象是由什么构造函数实例化出来的。还一个作用,如果给构造函数原型以

Person.prototype={
	sing:finction(){},
	dance:function(){}
}

形式添加方法,此时相当于Person.prototype重新指向,则其不在含有constructor属性了。此时可以手动给其添加constructor属性,让其重新指向原来的构造函数。

Person.prototype={
	constructor:Person,
	sing:finction(){},
	dance:function(){}
}

三、原型链

  函数的原型(prototype)也是一个对象,其有两个属性:constructor 和 __proto__,可以 console.log(函数.prototype) 输出查看。 其中 constructor 指向构造函数本身,__proto__指向Object.prototype;Object.prototype.constructor 指向Object()构造函数,Object.prototype.__proto__ 值为null。Object站在原型链最顶端。
下图为构造函数、实例对象、原型三者之间的关系图:
在这里插入图片描述
下图为原型链示意图:
在这里插入图片描述
js成员查找机制:按照原型链从下往上进行查找,如果原型链上都有则就近原则

什么是原型:
1、每个函数都有一个 prototype 属性,该属性指向的是原型对象。其有两个值:constructor、__proto__。
2、每个实例对象都有一个 __proto__ 属性,该属性指向的是其构造函数的原型。即:构造函数.prototype === 其实例对象的.__proto__。
什么是原型链:
1、查找对象的属性时先在自身找,若没找到则沿着 __proto__ 找构造函数的原型对象。
2、如果构造函数的原型对象还没有,继续沿着 __proto__ 查找,直到找到 Object 的原型。
3、如果还没找到,则返回 undefined。
4、沿着 __proto__ 查找的就是原型链。

构造函数、实例对象、原型三者之间的关系以及原型链验证代码:

function Demo(){}
console.log(Demo.prototype)	//{constructor: ƒ Demo(),__proto__: Object}
console.log(Demo.prototype.constructor)	//ƒ Demo(){}
console.log(Demo.prototype.__proto__ === Object.prototype)	//true
console.log(Demo.prototype.__proto__.__proto__)	//null
		
let oo = new Demo()
console.log(oo)	//Demo {}
console.log(oo.__proto__ === Demo.prototype)	//true,对象的__proto__指向构造函数的prototype
console.log(oo.__proto__.constructor)//指向构造函数

构造函数的继承(ES6之前的写法)

function Father(){
	this.name = 'fname'	
	this.age = 18
}
Father.prototype.money = function(){
	console.log('两毛钱!')
}
			
function Son(){
	this.name = 'Sname'
	Father.call(this)
	//调用之后 Father() 的this指向 Son() ,所以相当于son.name = 'fname',即子构造函数继承了父构造函数的name属性
	this.age = 12	//上面继承,这里添加自己的。这样既可以继承父构造函数的属性又可以添加自己的属性	
}
			
//上面只是继承了父构造函数的属性,但是还没继承父构造函数添加在原型上的方法
Son.prototype = new Father()//继承父构造函数添加在原型的方法
Son.prototype.constructor = Son//修改constructor指回原来的构造函数
let s1 = new Son()
console.log(s1)	//Son {name: "fname", age: 12}
console.log(Son.prototype.constructor)	//ƒ Son(){}
  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值