JS对象分类

100 阅读4分钟

输出正方形的面积和周长

第一版

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() {
            return this.width * this.width
        },
        getLength() {
            return this.width * 4
        }
    }

}
//浪费了太多内存

第二版

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]
}
//创建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])
}
//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)
}
console.log('squareList',squareList)

49.png 函数是对象

最终版,使用new操作符

所有的js里面的函数,从它出生的时候就自带一个prototype的属性,这个属性从它出生的时候就有一个constructor,这个constructor从它出生的时候,它的值就是函数自身

50.png

/**
 * 函数和原型结合(重写)
 */
let squareList = []
let widthList = [5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6]
function Square(width) {
    this.width = width//用this代表新的对象,就是obj
    // return obj;//不用写,它会帮你写
}
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 X()自动做了四件事情

  • 自动创建空对象
  • 自动为空对象关联原型,原型地址指定为X.prototype(自动将该空对象的原型指向 X.prototype(即将 X.prototype 保存的地址复制到空对象.__proto__ 里))
  • 自动将空对象作为this关键字运行构造函数
  • 自动return this

构造函数X

  • X函数本身负责给对象本身添加属性
  • X.prototype对象负责保存对象的共用属性

51.png

53.png

原型与公用属性的关系

结论: 你是谁构造的,你的原型就是谁的prototype属性对应的对象

原型公式: 对象.proto === 其构造函数.prototype

square的原型是什么 等价于 square.proto 的值是什么

x 均代表普通对象

  • 「x 的原型」等价于「x._proto_ 所指的对象」 ,有时为了方便,我们可以认为「x 的原型」等价于「x._proto_ 」
  • 一个对象的原型指的是这个对象与其他同类对象的公有属性的集合,比如 obj1 和 ob2 同时拥有 toString / valueOf,那么 toString / valueOf 等属性组成的对象,就是 obj1 和 obj2 的原型,这个原型的地址一般储存在构造函数的 prototype 里
  • x.__proto__和 Object.prototype 存储着同一个对象的地址,这个对象就是 x 的原型
  • 每个对象都有原型,但除了「根对象 Object.prototype」比较特殊,Object.prototype 这个对象的原型为空 null

prototype属性

  • 所有函数一出生就有一个 prototype 属性(除了箭头函数)
  • 所有 prototype 一出生就有一个 constructor 属性
  • 所有 constructor 属性一出生就保存了对应的函数的地址
  • 如果一个函数不是构造函数,它依然拥有 prototype 属性,只不过这个属性暂时没什么用
  • 如果一个对象不是函数,那么这个对象一般来说没有 prototype 属性,但这个对象一般一定会有 _proto_ 属性

注意

  • window.Object 是一个函数对象,那么这个「函数对象」的构造函数是Function

56.png

  • window.Function 是一个函数对象,那么这个「函数对象」的构造函数是Function

57.png

  • window.Object 是一个函数对象,那么这个「函数对象」的 __proto__ 是Function.prototype
  • window.Function 是一个函数对象,那么这个「函数对象」的 __proto__ 是Function.prototype
  • window是Window构造的,可以通过constructor属性看出构造者

55.png

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

关于 Object.prototype

  • Object.prototye 是「Object 构造出来的对象 obj」的原型,即 obj.__proto__ === Object.prototype
  • Object._proto_ 是 Object 的原型,由于 Object 是函数,而所有函数的原型都是 Function.prototype,所以 Object._proto_ === Function.prototype
  • Object.prototye 不是 Object 的原型,Object.__proto__ 才是 Object 的原型(「x.原型 等价于 x._proto_」)
  • 所有「函数对象」的「构造函数」都是 Function

结论:你是谁构造的,你的原型就是谁的prototype属性对应的对象

原型公式:对象._proto_ === 其构造函数.prototype

class重写Square

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
  }
}


sayHi在原型上(语法定义的)

  • 所有定义到class的普通方法,都会被放到对象的原型里面,而不是对象身上

image.png

sayHi在对象自己身上(语法定义的)

image.png

等价写法:

image.png

资料来源:饥人谷