JS对象分类

一个小程序,输出各种形状的面积和周长

1
2
3
4
5
6
7
8
9
10
// 正方形 边长、面积、周长
let square = {
width: 5,
getArea() {
return this.width * this.width;
},
getLength() {
return this.wdith * 4;
}
}

二,生成12个正方形

1
2
3
4
5
6
7
8
9
10
11
12
let squareList = []
for(let i = 0; i<12; i++){
squareList[i] = {
width: 5,
getArea(){
return this.width * this.width
},
getLength(){
return this.width * 4
}
}
}

三,width 是 5 和 6相间

1
2
3
4
5
6
7
8
9
10
11
12
13
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.widht * 4;
}
}
}

三,垃圾代码,浪费太多内存,画内存图

squareMemory

四,借助原型,将12个对象的共有属性放到原型里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);
squareList[i].width = widthList[i];
}

// 还是垃圾代码,创建 square 的代码太分散了

五,把代码抽离到一个函数里,然后调用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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]);
}

六,函数和原型的结合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 这段代码 几乎完美
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);
}

七,函数和原型(重写)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let squareList = []
let widthList = [5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6]

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]);
console.log(squareList[i].constructor);
}
// 完美

代码六七对比

Snipaste_2021-08-31_12-16-01

总结

  • new X()
    • 自动创建空对象
    • 自动为空对象关联原型,原型地址指定为 X.prototype
    • 自动将空对象作为 this 关键字运行构造函数
    • 自动 return this
  • 构造函数 X
    • X 函数本身负责给对象本身添加属性
    • X.prototype 对想负责保存对象那个的公用属性

题外话 代码规范:

  • 大小写

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

    • new 后面的函数,使用名词形式
    • 如 new Person()、new Object()
    • 其他函数,一般使用动词开头
    • 如 createSquare(5)、createElement(‘div’)

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

Square 最终版(存疑)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Square(width){
this.width = width;
}

Square.prototype.getArea = function(){
return this.width * this.width;
}
Square.prototype.getLenth = function(){
return this.width * 4;
}

let square = new Square(5);
square.width;
square.getArea();
square.getLength();

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 圆形
function Circle(radius){
this.radius = radius;
}
Circle.prototype.getArea = function(){
return this.radius * 2 * Math.PI;
}
Circle.prototype.getLength = function(){
return Math.pow(this.radius, 2) * Math.PI;
}
let c1 = new Circle(10);
c1.getArea();
c1.getLength();

// 长方形
function Rect(width, height) {
this.width = width;
this.height = height
}
Rect.prototype.getArea = function(){
return this.width * this.height
}
Rect.prototype.getLength = function(){
return (this.width + this.height) * 2;
}
let r1 = new Rect(2, 3)
r1.getArea()
r1.getLength()

对象分类

分类

  • 理由一
    • 有很多对象拥有一样的属性和行为
    • 需要把他们分为同一类
    • 如 square 和 square1
    • 这样创建类似对象的时候就很方便
  • 理由二
    • 但是还有很多对象拥有其他的属性和行为
    • 所以就需要不同的分类
    • 比如 Square / Circle / Rect 就是不同的分类
    • Array / Function 也是不同的分类
    • 而 Object 创建出来的对象,是最没有特点的对象

类型和类

类型

类型 JS 数据的分类,有七种

四基两空一对象,String,number,bool,symbol,null,undefined,Object

类是针对对象的分类, 有无数种

常见的有 Array、Function、Date、RegExp 等

数组对象

定义一个数组

1
2
3
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’ 等

函数对象

定义一个函数

1
2
3
4
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.constructor

window._proto_ === Windows.prototype

window.Object 是谁构造的

window.Funcion

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

验证:window.Object.constructor === window.Function

window.Function 是谁构造的

window.Function

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

自己构造的自己?不是这样的,这是『上帝』的安排

浏览器构造了 Function,然后指定它的构造者是自己

验证:window.Function.constructor === window.Function

class

类是用于创建对象的模板。他们用代码封装数据以处理该数据。 ES6新语法

学习class对象初始化解构赋值

constructor 是一种用于创建和初始化 class 创建的对象的特殊方法.

1
2
3
4
5
6
7
8
9
10
11
class Square{
constructor(width){
this.width = width;
}
getArea(){
return this.width * this.width;
}
getLength(){
return this.width * 4;
}
}

class 引入更多概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}
}

class 重写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 重写 Circle
class Circle{
constructor(radius){
this.radius = radius;
}
getArea(){
return Math.pow(this.radius,2) * Math.PI;
}
getLength(){
return this.radius * 2 * Math.PI;
}
}
let c2 = new Circle(5);
c2.radius;
c2.getArea();
c2.getLength();

// 重写 Rect
class Rect{
constructor(width, height){
this.width = width;
this.height = height;
}
getArea(){
return this.width * this.height;
}
getLength(){
return (this.width + this.height) * 2;
}
}
let r2 = new Rect(3, 4);
r2.width;
r2.height;
r2.getArea();
r2.getLength();

class 中两种函数写法的区别

这两种写法的意思完全不一样:

语法1:

1
2
3
4
5
6
7
8
9
class Person{
sayHi(name){}
// 等价于
sayHi: function(name){}
// 注意,一般我们不在这个语法里使用箭头函数
}
//等价于
function Person(){}
Person.prototype.sayHi = function(name){}

语法2:注意冒号变成了等于号

1
2
3
4
5
6
7
8
class Person{
sayHi = (name)=>{} // 注意,一般我们不在这个语法里使用普通函数,多用箭头函数
}
// 等价于
function Person(){
this.sayHi = (name)=>{}
}

推荐阅读

  1. 方应杭:你可以不会 class,但是一定要学会 prototype
  2. 方应杭:JS 的 new 到底是干什么的?
  3. 方应杭:JS 中 proto 和 prototype 存在的意义是什么?
  4. 饥人谷整理的 ES6 所有新特性