JS对象分类

86 阅读1分钟

例一

输出正方形的面机和周长

let square = {
  width: 5,
  getArea(){ 
    return this.width * this.width     //面积
  },
  getLength(){
    return this.width * 4             //周长
  }
}
  • 来一堆正方形,且边长不一样 用for循环配合数组
let squareList = []
let widthList = [5,6,5,6,5,6,5,6,5,6,5,6]
for(let i = 0; i<12; i++){
  squareList[i] = {
    width: widthList[i],
    getArea(){                             //x(){} = x: function(){}
      return this.width * this.width 
    },
    getLength(){
      return this.width * 4
    }
  }
}

垃圾代码,浪费了太多内存,自己画内存图就知道了

image.png 在循环的时候会新生成函数也会占内存

借助原型

将12个对象的共用属性放到原型里

let squareList = []
let widthList = [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++){
  squareList[i] = Object.create(squarePrototype)     //修改原型为squarePrototype
  squareList[i].width = widthList[i]                 //添加自身属性width
}

还是垃圾代码!创建 square 的代码太分散了!

抽离到函数

把代码抽离到一个函数里,然后调用函数

let squareList = []
let widthList = [5,6,5,6,5,6,5,6,5,6,5,6]
function createSquare(width){                //此函数叫做构造函数
  let obj = Object.create(squarePrototype)
                                             // 以 squarePrototype 为原型创建空对象
  obj.width = width
  return obj
}
let squarePrototype = {
  getArea(){ 
    return this.width * this.width 
  },
  getLength(){
    return this.width * 4
  }
}
for(let i = 0; i<12; i++){
  squareList[i] = createSquare(widthList[i])
  // 这下创建 square 很简单了吧!
}

squarePrototype 原型 和 createSquare 函数 还是分散的

能不能组合在一起?

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) //不是先使用后定义,只是先写好
  obj.width = width
  return obj
}
createSquare.squarePrototype = {                       //把原型放到函数上,函数也是对象
  getArea(){ 
    return this.width * this.width 
  },
  getLength(){
    return this.width * 4
  },
  constructor: createSquare                           //方便通过原型找到构造函数
}
for(let i = 0; i<12; i++){
  squareList[i] = createSquare(widthList[i])          //这里才开始使用
  console.log(squareList[i].constructor) 
  // constructor 可以知道谁构造了这个对象:你妈是谁?
}

new 操作符

函数和原型结合(重写)

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(){      //prototype里面加的东西不一定是函数,也可以是字符串啥的
  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 属性,这是 JS 之父故意的
// 每个 prototype 都有 constructor 属性,也是故意的
  • constructor值等于函数自身
function f1(){}
f1.prototype.constructor === f1
//true
  • 对比

image.png

总结

  • new X() 自动做了四件事情
  1. 自动创建空对象
  2. 自动为空对象关联原型,原型地址指定为 X.prototype
  3. 自动将空对象作为 this 关键字运行构造函数
  4. 自动 return this ——这就是 JS 之父的爱
  • 构造函数 X
  1. X 函数本身负责给对象本身添加属性
  2. X.prototype 对象负责保存对象的共用属性

代码规范

  • 大小写
  1. 所有构造函数(专门用于创建对象的函数)首字母大写
  2. 所有被构造出来的对象,首字母小写
  • 词性
  1. new 后面的函数,使用名词形式
  2. 如 new Person()、new Object()
  3. 其他函数,一般使用动词开头
  4. 如 createSquare(5)、createElement('div')

原型公式

对象.__proto__ === 其构造函数.prototype

  • 例1: let x = {} 请问:
  1. x 的原型是什么?
  2. x.__proto__ 的值是什么?
  3. 上面两个问题是等价的吗?
  4. 请用内存图画出 x 的所有属性 image.png
  • 例2: 请问:
  1. Object.prototype 是哪个函数构造出来的?
  2. Object.prototype 的原型是什么?
  3. Object.prototype.__proto__
  4. 请用内存图画出上述内容
  • Square 最终版
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)
square.width
square.getArea()
square.getLength()
  • Circle
function Circle(radius){ 
  this.radius = radius
}
Circle.prototype.getArea = function(){ 
  return Math.pow(this.radius,2) * Math.PI  //Math.PI = π, 
                                             Math.pow(this.radius,2)2为平方,可为任意数字
}
Circle.prototype.getLength = function(){
  return this.radius * 2 * Math.PI
}
let circle = new Circle(5)
circle.radius
circle.getArea()
circle.getLength()
  • Rectangle
function Rect(width, height){ 
  this.width = width
  this.height = height
}
Rect.prototype.getArea = function(){ 
  return this.width * this.height  
}
Rect.prototype.getLength = function(){
  return (this.width + this.height) * 2
}
let react = new Rect(4,5)
rect.width
rect.height
rect.getArea()
rect.getLength()

对象需要分类

  • 理由一
  1. 有很多对象拥有一样的属性和行为,需要把它们分为同一类
  2. 如 square1 和 square2,这样创建类似对象的时候就很方便
  • 理由二
  1. 但是还有很多对象拥有其他的属性和行为 所以就需要不同的分类 比如 Square / Circle / Rect 就是不同的分类 Array / Function 也是不同的分类 而 Object 创建出来的对象,是最没有特点的对象

类型 V.S. 类

  • 类型
  1. 类型是 JS 数据的分类,有 7 种
  2. 四基两空一对象
  1. 类是针对于对象的分类,有无数种
  2. 常见的有 Array、Function、Date、RegExp(正则) 等

数组对象

  • 定义一个数组
let arr = [1,2,3]
let arr = new Array(1,2,3) // 元素为 1,2,3
let arr = new Array(3) // 长度为 3
  • 数组对象的自身属性
'0' / '1' / '2' / 'length'
//注意,属性名没有数字,只有字符串

数组如果只写一个元素,那它就会变成数组的长度,如果是多个参数就会变成[0,1,2,...N]

let arr1 = new Array(3)
//length:3   会变成长度为3的空数组
let arr2 = new Array(2,3)
//[2,3]
  • 数组对象的共用属性
  1. 'push':推,推元素进去
  2. 'pop':删除并返回数组最后一个元素(理解成弹出来一个元素)
  3. 'shift':删除第一个元素,并返回值(理解成把第一个元素提出来)
  4. 'unshift':将元素添加到开头,并返回length值(理解成把一个元素按到第一个位置)
  5. 'join':将数组所有元素连接成一个字符串,并返回这个值
  6. 'concat':连接两个数组,返回连接后的数组

函数对象

  • 定义一个函数
function fn(x,y){return x+y}
let fn2 = function fn(x,y){return x+y}
let fn = (x,y) => x+y
let fn = new Function('x','y', 'return x+y')
  • 函数对象自身属性 'name' / 'length'
  • 函数对象共用属性 'call' / 'apply' / 'bind'

JS 终极一问

  • window 是谁构造的 Window,可以通过 constructor 属性看出构造者
  • window.Object 是谁构造的 window.Function, 因为所有函数都是 window.Function 构造的
  • window.Function 是谁构造的 window.Function

因为所有函数都是 window.Function 构造的

自己构造的自己?

并不是这样,这是「上帝」的安排 浏览器构造了 Function,然后指定它的构造者是自己

ES 6 引入了新语法

class 语法引入了更多概念

class Square{
  static x = 1
  width = 0
  constructor(width){
    this.width = width
  } 
  getArea(){ 
    return this.width * this.width 
  }
  getLength(){
    return this.width * 4
  }
  get area2(){ // 只读属性
    return this.width * this.width
  }
}

用 class 重写 Circle

class Circle{
  constructor(radius){
    this.radius = radius
  } 
  getArea(){ 
    return Math.pow(this.radius,2) * Math.PI  
  }
  getLength(){
    return this.radius * 2 * Math.PI
  }
}
let circle = new Circle(5)
circle.radius
circle.getArea()
circle.getLength()

用 class 重写 Rect

class Rect{
  constructor(width, height){ 
    this.width = width
    this.height = height
  }
  getArea(){ 
    return this.width * this.height  
  }
  getLength(){
    return (this.width + this.height) * 2
  }
}
let react = new Rect(4,5)
rect.width
rect.height
rect.getArea()
rect.getLength()