JS的构造函数与对象分类

240 阅读5分钟

构造函数(原型)

function Circle(radius){  // 约定俗成构造函数名字大写字母开头
	this.radius = radius
}
Circle.prototype.getLength = function(){
	return this.radius * 2 * Math.PI
}
Circle.prototype.getArea = function(){
	return Math.pow(this.radius,2) * Math.PI 
}
let c1 = new Circle(5)  // 使用new操作符调用构造函数来创建对象

new Circle() 自动做了以下四件事

  1. 自动创建空对象
  2. 自动为空对象关联原型,原型地址指定为Square.prototype,也就是此构造函数的原型
  3. 自动将空对象作为this关键字运行构造函数
  4. 自动return this

构造函数Square

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

ES6 class语法

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)
console.log(circle.radius)
console.log(circle.getArea())
console.log(circle.getLength())

题外话--关于代码规范

大小写

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

词性

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

原型公式(JS 里唯一的一个公式)

规律

  • let obj = new Object() 的原型是 Object.prototype
  • let arr = new Array() 的原型是 Array.prototype
  • let square = new Square() 的原型是 Square.prototype
  • let fn = new Function() 的原型是 Function.prototype

结论

你是谁构造的
你的原型就是谁的 prototype 属性对应的对象

原型公式

对象.__ proto __ === 其构造函数.prototype

答疑

let x = {}

请问:

  1. x 的原型是什么?
    x.__proto__
    Object.prototype严谨一点为Object.prototype所对应的对象)
    2. x.__ proto __ 的值是什么?
    Object.prototype (严谨一点为Object.prototype所对应的对象)
    3. 上面两个问题是等价的吗?
    等价
    4. 请用内存图画出 x 的所有属性
    image.png

ps: 关于属性名与属性值

图中window.Object 为Object的属性名
#101的内容才是 Object的属性值

let square = new Square(5)

请问:

  1. square 的原型是什么?
    square.__ proto __
    Square.prototype (严谨一点为Square.prototype所对应的对象)
    2. square.__ proto __ 的值是什么?
    Square.prototype (严谨一点为Square.prototype所对应的对象)

关于Object.prototype 请问:

  1. Object.prototype 是哪个函数构造出来的?
    天生存在,不是谁构造出来的
  2. Object.prototype 的原型是什么?
    没有原型
  3. Object.prototype.__ proto __ ?
    null

对象分类

对象需要分类吗

需要

理由一

有很多对象拥有一样的属性和行为
需要把它们分为同一类
如 square1 和 square2
这样创建类似对象的时候就很方便

理由二

但是还有很多对象拥有其他的属性和行为
所以就需要不同的分类
比如 Square / Circle / Rect 就是不同的分类
Array / Function 也是不同的分类
而 Object 创建出来的对象,是最没有特点的对象

类型 VS 类

类型

类型是 JS 数据的分类,有 7 种
四基两空一对象

类是针对于对象的分类,有无数种
常见的有 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'
注意,属性名没有数字,只有字符串

数组对象的共用属性

'push' / 'pop' / 'shift' / 'unshift' / 'join'

函数对象

定义一个函数

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,然后指定它的构造者是自己

测试题

  1. 关于「原型」,正确的是
  • 「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
  1. 关于 prototype 属性,正确的有
  • 所有函数一出生就有一个 prototype 属性(除了箭头函数)
  • 所有 prototype 一出生就有一个 constructor 属性
  • 所有 constructor 属性一出生就保存了对应的函数的地址
  • 如果一个函数不是构造函数,它依然拥有 prototype 属性,只不过这个属性暂时没什么用
  • 如果一个对象不是函数,那么这个对象一般来说没有 prototype 属性,但这个对象一般一定会有 __ proto __ 属性
  1. new X() 操作其实自动帮我们做了很多事情,这些事情包括
  • 自动创建一个空对象
  • 自动将该空对象的原型指向 X.prototype(即将 X.prototype 保存的地址复制到空对象.__ proto __ 里)
  • 自动将空对象作为 this 来运行构造函数
  • 自动 return this
  1. window.Object 是一个函数对象,那么这个「函数对象」的构造函数是 Function

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

  3. window.Object 是一个函数对象,那么这个「函数对象」的 __ proto __ 是 Function.prototype

  4. window.Function 是一个函数对象,那么这个「函数对象」的 __ proto __ 是 Function.prototype

  5. 关于 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 的原型
  1. 所有「函数对象」的「构造函数」都是 Function