构造函数(原型)
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() 自动做了以下四件事
- 自动创建空对象
- 自动为空对象关联原型,原型地址指定为Square.prototype,也就是此构造函数的原型
- 自动将空对象作为this关键字运行构造函数
- 自动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 = {}
请问:
- x 的原型是什么?
x.__proto__
即Object.prototype严谨一点为Object.prototype所对应的对象)
2. x.__ proto __ 的值是什么?
Object.prototype(严谨一点为Object.prototype所对应的对象)
3. 上面两个问题是等价的吗?
等价
4. 请用内存图画出 x 的所有属性
ps: 关于属性名与属性值
图中window.Object 为Object的属性名
#101的内容才是 Object的属性值
let square = new Square(5)
请问:
- square 的原型是什么?
square.__ proto __
即Square.prototype(严谨一点为Square.prototype所对应的对象)
2. square.__ proto __ 的值是什么?
Square.prototype(严谨一点为Square.prototype所对应的对象)
关于Object.prototype 请问:
- Object.prototype 是哪个函数构造出来的?
天生存在,不是谁构造出来的 - Object.prototype 的原型是什么?
没有原型 - Object.prototype.__ proto __ ?
null
对象分类
对象需要分类吗
理由一
有很多对象拥有一样的属性和行为
需要把它们分为同一类
如 square1 和 square2
这样创建类似对象的时候就很方便
理由二
但是还有很多对象拥有其他的属性和行为
所以就需要不同的分类
比如 Square / Circle / Rect 就是不同的分类
Array / Function 也是不同的分类
而 Object 创建出来的对象,是最没有特点的对象
类型 VS 类
类型
类
类是针对于对象的分类,有无数种
常见的有 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')
函数对象自身属性
函数对象共用属性
'call' / 'apply' / 'bind'
JS 终极一问
window 是谁构造的
**Window**
可以通过 constructor 属性看出构造者
window.Object 是谁构造的
**window.Function**
因为所有函数都是 window.Function 构造的
window.Function 是谁构造的
**window.Function**
因为所有函数都是 window.Function 构造的
自己构造的自己?并不是这样,这是「上帝」的安排
浏览器构造了 Function,然后指定它的构造者是自己
测试题
- 关于「原型」,正确的是
- 「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 __ 属性
- new X() 操作其实自动帮我们做了很多事情,这些事情包括
- 自动创建一个空对象
- 自动将该空对象的原型指向 X.prototype(即将 X.prototype 保存的地址复制到空对象.__ proto __ 里)
- 自动将空对象作为 this 来运行构造函数
- 自动 return this
-
window.Object 是一个函数对象,那么这个「函数对象」的构造函数是 Function
-
window.Function 是一个函数对象,那么这个「函数对象」的构造函数是 Function
-
window.Object 是一个函数对象,那么这个「函数对象」的 __ proto __ 是 Function.prototype
-
window.Function 是一个函数对象,那么这个「函数对象」的 __ proto __ 是 Function.prototype
-
关于 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 的原型
- 所有「函数对象」的「构造函数」都是 Function