Javascript对象类型(二)

279 阅读6分钟

问题思考

怎么实现一个正方形

假设我们用以下代码实现一个正方形

let square = {
    width : 5,
    getArea(){
        return this.width * this.width
    },
    getLength(){
        return this.width * 4
    }
}
  • 以上代码创建了一个square对象,square square拥有三个属性,边长,面积,周长

  • 那么为什么不直接添加属性area和length呢?

  • 答:一般情况下width的数值是未知的或者是变化的不可能每次换个width把area和length都换了

那么怎么实现一打正方形呢?

如果我们以上面代码一个一个实现

let square = {
    width : 5,
    getArea(){
        return this.width * this.width
    },
    getLength(){
        return this.width * 4
    }
}
let square2 = {
    width : 6,
    getArea(){
        return this.width * this.width
    },
    getLength(){
        return this.width * 4
    }
}
let square3 = {...

这样写太累那么我们会想到for循环

let squareList = []
let widthList = [5,5,6,5,8,6,1,5,5,5,5,5]
for(let i = 0; i<12; i++){
    squareList[i] = {
        width : widthList[i],
        getArea(){
            return this.width * width
        },
        getLength(){
            return this.width * 4
        }
    }
}
  • 这样就产生了12个的正方形,这样代码的问题在于浪费内存。
  • 每进入一次squareList[]循环体就会创建一次getArea()和getLength(),最后会创建24次一模一样的对象,太浪费内存

那么怎么很好的实现呢?

let squareList = []
let widthList = [5,5,6,5,8,6,1,5,5,5,5,5]  
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)
    squareList[i].width = widthList[i]
}

  • 这样每个新的squareList[i]的__proto__指向了新的原型squarePrototype
  • 现在代码问题是太冗余

怎么人代码精简呢?而且逻辑不紧密

let squareList = []
let widthList = [5,5,6,5,8,6,1,5,5,5,5,5]  
function createSquare(width){ 
    let obj = Object.create(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])
}

怎么样才能更加精简呢?

let squareList = []
let widthList = [5,5,6,5,8,6,1,5,5,5,5,5]  
function createSquare(width){ 
    let obj = Object.create(creteSquare.squarePrototype)
    obj.width= width
    return obj
}//创建构造函数
creatSquare.squarePrototype = {
    getArea(){
    return this.width * this. width
    },
    getLength(){
    return this.width * 4
    },
    constructor : creatSquare
}
for(let i = 0; i<12;i++){
squareList[i]=createSquare(widthList[i])
}
  • 现在把原型放在了构造函数上当成了属性,要访问新原型必须通过构造函数访问
  • 有在原型上加入了constructor属性
  • 这样由构造函数creatSquare创建的对象obj赋值给squareList[i],每个squareList[i]中的__proto__都会有getArea,getLength和constructor
  • 原型将来是要放在新创建的对象上的所有原型的constructor要写构造函数
  • 函数定义在使用之前就行

javascript的原生办法

let squareList = []
let widthList = [5,5,6,5,8,6,1,5,5,5,5,5]  
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])
}
  • 每个js函数都有prototype属性
  • 每个prototype都有constructor,它的值是函数自身
  • 根源:Function.__proto__指向了Function.prototype自己构造了自己(浏览器构造的)

现在你可能想问?

  • Square怎么返回对象,它没有return
  • new什么意思
  • prototype属性什么意思

解答,new的用法

  • new X()自动做了四件事情
  • 自动创建空对象
  • 自动将空对象关联原型,原型地址为函数的prototype即X.prototype
  • 自动将空对象作为this关键词运行构造函数
  • 自动return this

X和X的prototype

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

总结

  • 任何对象的.proto === 其构造函数.prototype

现在的缺点

  • 自己的构造函数可以知道什么参数,但是别人的构造函数怎么知道
  • 这就是js语言自身的缺陷,js语言对文档的依赖性

class写法

class Square{
    constructor(width){
    this.width = width
    }
}
    getArea(){
    return this. width * this .width
    }
    getLength: function(){
    return this . width * 4
 //函数的两种写法都对
}
  • 创建一个对象类
  • constructor里面写属性
  • 外面写函数

方法

  • 方法:只有通过某个特定对象才能调用的函数,区别于对象里的属性,属性之间函数之间用逗号隔开
  • 方法虽然在对象里面,但函数也是对象,所以对象里的方法存的是方法的地址

函数

  • 构造函数就是可以构造出对象的函数
  • 原型里的所有属性叫做共有属性,一坨内存叫原型
  • 函数有prototype给new创建条件,所有对象有__proto__,被构造出来(new)的对象的__proto__来源于构造函数的prototype,为什么js要给函数prototype属性就是为了方便new
  • 所有的函数对象都是函数构建出来的Function,所有函数对象的__proto__就是Function的prototype

Object.prototype构造出Function.prototype,然后Function.prototype构造出Object和Function。 因为prototype也是函数对象__proto__指向了Object.prototype
Function.prototype指向了Function.proto__自己构造了自己
浏览器构造了Function(包括它的prototype和__proto
)然后指定它构造了它

对象

  • Object.prototype是天生就有的,原型为空
  • Object创建出来的对象是最没有特点的对象,构造函数new出来的加了点私货(X.prototype)
  • 类就是针对对象的分类有无数种,常见的有Array,Function
  • window是由Window构造的
  • 所有得对象(new Object)的__proto__是Object的prototype

命名规则

  • 命名规则构造函数首字母大写,被构造出来的对象首字母小写
  • new+名词(构造函数),其他函数用动词

总结

  • 每个函数有prototype浏览器给加上去的的里面没多少东西默认只有__proto__和constructor,因此Function.prototype.__proto__从Object.prototype来的浏览器在加上点东西就成了Function.prototype,对象有__proto__都是new继承来的
  • 所以__proto__有根肯定是一个prototype,谁呢?是Function.prototype,那么Function.__proto__又是哪来的是Function.prototype浏览器这样写的(理解这里就行)
  • Object 是一个函数对象它的__proto__是Function.prototype
  • Object.prototype是一个对象,Object.prototype 自己的原型为 null,Object.prototye 是 Object 构造出来的对象的原型

    f3.prototype是浏览器放进f3的,但是prototype是对象,所以它有__proto__来自于Object.protype

第二天醒来后的思考

let squareList = []
let widthList = [5,5,6,5,8,6,1,5,5,5,5,5]  
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])
}

以以上代码为例


Square是一个函数prototype是浏览器自己加上去的,prototype里面除了__proto__和constructor是浏览器给的其他是自己写的,Square也有__proto__是来自于Function的prototype,Square.__proto__ === Function.prototype
squareList[2]的__proto__是Square函数的prototype来的,里面还有一个__proto__是创建prototype对象时来的squareList[2].__proto__.__proto__ === Object.prototype

最最最最终总结

  • 所有函数的__proto__都来自于Function.prototype,所有函数实例都继承于Function,包括Function.proto
  • Object.prototype 是所有对象的原型
  • 一般函数实例中__proto__来自于构造函数的,实例中的其他属性呢是它的构造函数用this.xxx写上去

约定俗成

  • 我们一般把对象里的__proto__叫做原型,而不是prototype,只是__proto__恰好等于某个函数的prototype