构造函数案例

171 阅读3分钟

主要内容

通过代码完成需求,逐步优化代码,提高代码性能,减少内存占用。最终理解构造函数的使用方法、new操作符的运行机制

需求1

  • 正方形边长为5,求面积、长度属性
let square = {                                 创建需求对象
width:5,                                       长度属性
get Area(){ return this.width*this.width },    面积函数
get Length(){ return this.width*4}             周长函数 
}

需求2

  • 多个正方形,长度为5,求面积、长度属性
  • 方法1
get square1={
    width:5,
    get Area(){  return this.width*this.width},        第一个
    get Length(){ return this.width*4}

get square2={
    width:5,
    get Area(){  return this.width*this.width},        第二个  
    get Length(){ return this.width*4}
                   .
                   .
                   .
                   .
get squaren={
     width:5,                             
     get Area(){.................. }                   第n个
     get Length(){.................}

缺点:繁琐,比较傻

  • 方法2
let squareList=[ ]                                 定义数组(由square对象组成)
for(let i=0,i<12,i++){                             循环控制条件(12次) 
squareList[i]={                                    调用第i个对象
     width:5,                                      对象宽度属性
     getArea(){return this.width*this.width},      对象面积属性 
     getLength(){ return this.width*4}             对象周长属性 
     }

需求变化: 边长不全是5,例如5,6,5,6,5,6,5,6,5

let squareList=[]                         定义数组(由square对象组成)      
let widthList=[5,6,5,6,5,6,5,6,5,6,5,6]   定义边长,width数组 
forlet i=0,i<12,i++){                   循环控制条件,循环12次   
squareList[i]={                           调用squareList数组第i项对象
    width:widthList[i],                   调用边长width数组第i项
    getArea(){ return this.width*this.width},  面积属性
    getLength(){ renturn this.width*4}         周长属性 
}

缺点:占用内存
内存调用机制分析

9ce695e9978ccc6c4f7618b72326ec8.png 每新生成一个square对象,就新生成面积和周长函数并由square内的相关属性调用,两个函数不可重复调用,只能重复地新生成,被新的square内的属性调用

  • 方法3:借助原型
    • 思路:将12个对象的共有属性放到原型里面
let suqareList=[]                             创建square对象组成的数组
let width list=[5,6,5,6,5,6,5,6,5,6,5,6]      创建边长数组
let squarePrototype={                           创建由共有属性组成的对象
     getArea(){return this.width*this.width}
     getLength(){ return this.width*4}
}
for(let i=0,i<12,i++){                             循环条件,循环12次
      squareList[i]=Object.creat(squarePrototupe)  在square对象原型内加入共有属性
      squareList[i].width=widthList[i]             在square对象内加入width属性    
      }

思路:将对象square的共有属性加进原型内,避免重复生成函数
缺点:创建square的代码太分散了

  • 方法4:把代码抽离到一个函数里,然后调用函数
let squareList=[]
let widthList=[5,6,5,6,5,6,5,6,5,6,5,6]
function creatSquare(width){                      构造函数              
    let obj=Object.creat(squarePrototype)         构造空对象obj,其原型里面加入square的共有属性
    obj.width=width                               obj的width属性就是square的width属性
    return obj                                    返回obj对象
 }
 
                                            
 let squarePrototype = {                          申明square的共有属性组成的对象
  getArea(){ return this.width * this.width },
  getLength(){ return this.width * 4 }
}

for(let i=0,i<12,i++){                              循环条件
      squareList[i] = createSquare(widthList[i])    对象squareList调用构造函数(见第三行)  
      }

缺点:squarePrototype 原型和createSquar函数还是分散的

  • 方法5:**把原型直接放到构造函数里面
let squareList = []
let widthList = [5,6,5,6,5,6,5,6,5,6,5,6]

function createSquare(width){                             构造函数
  let obj = Object.create(createSquare.squarePrototype)  将square共有属性直接加入函数原型中
  obj.width = width
  return obj
}
此时的函数不执行,只有在最终函数被调用的时候才会运行,此块代码与下方代码设置顺序不影响

createSquare.squarePrototype = {                             
  getArea(){                                       定义函数里面square共有属性中的具体内容
    return this.width * this.width 
  },
  getLength(){
    return this.width * 4
  },

constructor: createSquare                       方便通过原型找到构造函数
}

for(let i = 0; i<12; i++){
  squareList[i] = createSquare(widthList[i])     square直接调用函数里面的width
  console.log(squareList[i].constructor)         通过square里面的constructor找到构造函数
 
}

-- 重构代码

let squareList = []
let widthList = [5,6,5,6,5,6,5,6,5,6,5,6]

function Square(width){ 
  this.width = width
  }
  
Square.prototype.getArea = function(){ 
  return this.width * this.width 
}

Square.prototype.getLength = function(){
  return this.width * 4
}

for(let i = 0; i<12; i++){
  squareList[i] = new Square(widthList[i])
  console.log(squareList[i].constructor)
}


每个函数都有 prototype 属性
每个 prototype 都有 constructor 属性

new操作符

对比代码

function createSquare(width){
  let obj = Object.create(createSquare.squarePrototype)
  obj.width = width
  return obj
}
createSquare.squarePrototype = {  
  getArea(){ 
    return this.width * this.width 
  },
  getLength(){
    return this.width * 4
  },
  constructor: createSquare
}
let square = createSquare(5)

最终简化版:引入new操作符

function Square(width){              
this.width = width                           自身属性有宽度
}
Square.prototype.getArea = function(){       共有属性 面积函数
  return this.width * this.width 
}
Square.prototype.getLength = function(){     共有属性 周长函数
  return this.width * 4
}
let square = new Square(5)



最基本结构案例

7897766af03ec48f74c04cb5e8018a2.png

new操作符总结

new X() 自动做了四件事情

  • 自动创建空对象
  • 自动为空对象关联原型,原型地址为X.prototype
  • 自动将空对象作为this关键字进行构造函数
  • 自动return this

构造函数X

  • X本身负责给对象本身添加属性
  • X.prtotype对象负责保存对象的共同属性

代码规范

代码规范

  • 所有构造函数(专门用于创建对象的函数)首字母大写
  • 所有被构造出来的对象,首字母小写

词性

  • new 后面的函数,使用名词形式
  • 如 new Person()、new Object()
  • 其他函数,一般使用动词开头
  • 如 createSquare(5)、createElement('div')