前端知识自检——js原型规则及设计模式

本文深入探讨了JavaScript中构造函数、原型及原型链的基本概念,解析了如何利用构造函数、原型对象实现对象实例的属性与方法共享,以及构造函数、实例、原型之间的关系。

在js中万物皆对象、

原型规则

原型及原型链基本概念

Javascript 规定,每一个函数都有一个 prototype 属性,它是一个指针,指向原型对象。 这个对象的所有属性和方法,都会被构造函数的实例继承。

这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上。

// 构造函数
function Person (name, age) {
  this.name = name
  this.age = age
}
// 原型对象
// Person.prototype
Person.prototype.type = 'sun'
Person.prototype.sayName = function () {
  console.log(this.name)
}
// 实例
var p1 = new Person(...)
var p2 = new Person(...)

console.log(p1.sayName === p2.sayName) // => true

这时所有实例的 type 属性和 sayName() 方法,其实都是同一个内存地址

构造函数、实例、原型三者之间的关系

构造函数和原型之间的关系:配偶关系
1.构造函数(妈妈)通过prototype属性来访问原型(对象)(爸爸)
2.原型(对象)(爸爸)通过constructor属性来访问构造函数(妈妈)
构造函数和实例对象之间的关系:母子关系
1.构造函数通过new得到实例对象(孩子)
2.实例对象不能直接访问构造函数,需要同过隐藏的constructor属性
原型(对象)和实例对象之间的关系:父子关系
1.实例对象通过_proto_属性访问原型(对象)
2.任何一个对象都有 proto 属性, 指向了原型对象,原型对象有 proto 属性, 指向了原型对象的原型对象,原型对象也有自己的原型对象,这样,形成一条链式结构,叫做原型链

__ proto __

任意一个对象,都会有__proto__属性,这个属性指向了构造函数的prototype属性,也就是原型对象。
获取原型对象:

  • 通过构造函数.prototype可以获取
  • 通过实例.__proto__可以获取(隐式原型)
  • 它们指向了同一个对象构造函数.prototype === 实例.proto
constructor属性

默认情况下,原型对象中只包含了一个属性:constructor,constructor属性指向了当前的构造函数。、

在这里插入图片描述

属性查找原则
  • 会先在自身上查找,如果没有
  • 则根据__proto__对应的原型去找,如果没有
  • 一直找到Object.prototyp,如果没有,那就找不到了。
原型链

任何一个对象,都有原型对象,原型对象本身又是一个对象,所以原型对象也有自己的原型对象,这样一环扣一环就形成了一个链式结构,我们把这个链式结构称为:原型链。

总结: Object.prototype是原型链的尽头,Object.prototype的原型是null。

设计模式

1.工厂模式

在函数内部创建一个对象,给这个对象赋予初始的属性和方法,再将这个对象返回

function Person(name, age) {
  let person= new Object()
  person.name = 'sun'
  Person.age= 24
  people.sex = function(){
	return 'boy'
  }
  return person
}

let a = Person()
console.log(a.name)   // sun
console.log(a.sex )  // boy

可以看到工厂模式的实现方法非常简单,解决了创建多个相似对象的问题,但是工厂模式却无从识别对象的类型,因为全部都是Object,不像Date、Array等,因此出现了构造函数模式。

2.构造函数模式

使用this代替在函数内部创建对象的方式,构造函数可以创建特定类型的对象,类似于Array、Date等原生JS的对象

function Person(name, age) {
  this.name = name
  this.age= age
  this.sex = function(){
	return 'boy'
  }
}

let a = new Person('sun', 24)
console.log(a.name)   // sun
console.log(a.sex )  // boy

构造函数虽然好用,但也并非没有缺点,使用构造函数的最大的问题在于每次创建实例的时候都要重新创建一次方法(理论上每次创建对象的时候对象的属性均不同,而对象的方法是相同的),然而创建两次完全相同的方法是没有必要的,因此,我们可以将函数移到对象外面(原型模式)

3.原型模式

利用prototype属性对属性进行定义,可以让所有的实例共享它的属性和方法

function Person() {
}
Person.prototype.name = 'sun'
Person.prototype.age= 24
Person.prototype.sex = function(){
  return 'boy'
}

let a = new Person()
console.log(a.name)   // sun
console.log(a.sex())  // boy

原型模式也不是没有缺点,首先,它省略了构造函数传递初始化参数这一环节,结果所有实例在默认情况下都取得了相同的属性值,这样非常不方便,但这还是不是原型的最大问题,原型模式的最大问题在于共享的本性所导致的,由于共享,因此因此一个实例修改了引用,另一个也随之更改了引用。因此我们通常不单独使用原型,而是结合原型模式与构造函数模式(混合模式)

4.混合模式

字面意思,混合:构造模式+原型模式,构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性

function Person(name, age) {
	this.name = name
	this.age= age
}
Person.prototype.sex = function(){
 return `${this.name}+${this.age}`
}

let a = new Person('sun', 24)
console.log(a.name)   // sun
console.log(a.sex()) // sun+24

混合模式中构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性。每个实例都会有自己的一份实例属性,但同时又共享着方法,最大限度的节省了内存。另外这种模式还支持传递初始参数。优点甚多。这种模式在ECMAScript中是使用最广泛、认同度最高的一种创建自定义对象的方法。

5.动态原型模式

动态原型模式将所有信息封装在了构造函数中,而通过构造函数中初始化原型(仅第一个对象实例化时初始化原型),这个可以通过判断该方法是否有效而选择是否需要初始化原型。

function Parent(name, age){  
	this.name=name
	this.age=age
	if(typeof Parent._sayname=="undefined"){     
	// 这块代码只执行了一次
	console.log('看这里')
		Parent.prototype.sayname=function(){  
			return this.name;  
        }
	}         
}
let a = new Parent('sun', 24)
let b = new Parent('三', 24)

可以看到上面的例子中只出现了一次 看这里 ,即当a初始化时,这样做b就不在需要初始化原型

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值