问题思考
怎么实现一个正方形
假设我们用以下代码实现一个正方形
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
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__.__proto__ === Object.prototype
最最最最终总结
- 所有函数的__proto__都来自于Function.prototype,所有函数实例都继承于Function,包括Function.proto
- Object.prototype 是所有对象的原型
- 一般函数实例中__proto__来自于构造函数的,实例中的其他属性呢是它的构造函数用this.xxx写上去
约定俗成
- 我们一般把对象里的__proto__叫做原型,而不是prototype,只是__proto__恰好等于某个函数的prototype