面向对象Object Oriented
收益
结构化
什么是
把相关的数据组合在一个结构中,从而方便访问
案例
1 2 3 Player player = ...;player.atk = 10;player.def = 10;
JavaScript 中表达
使用 Object 就可以轻松做到
1 2 3 4 5 6 const player = { atk: 0, def: 0};player.atk = 10;player.def = 10;
封装
什么是
将部分资源进行隐藏
案例
1 2 3!Player player = ...;player.setAtk(10);player.atk = 10;
JavaScript 中表达
可以使用 Closure 进行模拟
1 2 3 4 5 6 7 8 9 10 11 12 function Player () { let _atk = 0; function setAtk(atk) { _atk = atk; } return { setAtk }}const player = Player()player.setAtk(10);player.atk = 10;
基于对象
什么是
方法操作可以基于对象的属性
案例
1 2 3 4 5 6 public class Player { private int atk; public int getAtk() { return this.atk; }}
JavaScript 中表达
可以使用上面的 closure 进行模拟
模型
什么是
构建好一套资源后,可以根据这个模板生成具体实例
案例
1 2 3 4 5 6 public class Player { private int atk; public int getAtk() { return this.atk; }}
JavaScript 中表达
可以使用上面的 closure 进行模拟
this
this 变量
干什么
函数调用时,如果使用 对象引导,则会传输为 this 变量
案例
1 2 3 4 5 6 7 const player = { name: "赵哲", print: function() { console.log(this.name); }}player.print();
函数 挂接/脱离 对象 对 this 的影响
案例 1
1 2 3 4 5 6 7 8 function print() { console.log(this.name);}const player = { name: "赵哲", print}player.print();
案例 2
1 2 3 4 5 6 7 8 function print() { console.log(this.name);}const player = { name: "赵哲", print}print();
案例 3
1 2 3 4 5 6 7 8 9 function print() { console.log(this.name);}const player = { name: "赵哲", print}const print2 = player.print;print2();
call 和 bind
使用 call 动态调用
调用无参函数
1 2 3 4 5 function print() { console.log("dynamic called");}print();print.call();
调用带参函数
1 2 3 4 5 function add(v1, v2) { return v1 + v2;}const result = add.call(null, 3, 4);console.log(result);
调用时动态绑定 this
1 2 3 4 5 6 7 function print() { console.log(this.name);}const player = { name: "赵哲"}print.call(player);
使用 bind 绑定 this
案例 1
1 2 3 4 5 6 7 function print() { console.log(this.name);}const player = { name: "赵哲"}print();
案例 2
1 2 3 4 5 6 7 8 function print() { console.log(this.name);}const player = { name: "赵哲"}const boundPrint = print.bind(player);boundPrint();
原型链机制Prototype Chain
背景
模板效果
类 与 对象
父类 与 子类
Object.create()
1 2 3 4 5 6 const obj1 = { a: 1};const obj2 = Object.create(obj1);console.log(obj2);console.log(obj2.a);
原型链
使用 Object.create()
可以根据现有对象作为模板,复制出一个新的对象
旧对象 叫做 新对象 的 原型 Prototype
obj1 -> a: 1 | obj2
__proto__
查找原型
使用 Object.create()
创建新对象时,新对象会保留一个指向旧对象的引用
使用 obj.__proto__
可以查找 obj
对象的原型是谁
1 2 3 4 5 const obj1 = { a: 1};const obj2 = Object.create(obj1);console.log(obj2.__proto__ === obj1);
指定原型
1 2 3 4 5 6 7 const obj1 = { a: 1};const obj2 = { __proto__: obj1};console.log(obj2.a);
属性访问实质
规则
每次访问时,系统会查找在原型链上最近的属性,并返回
如果新对象没有属性,系统会通过 __proto__
在原型上寻找属性
案例
1 2 3 4 5 6 const obj1 = { a: 1};const obj2 = Object.create(obj1);const obj3 = Object.create(obj2);console.log(obj3.a);
原型链
obj1 -> a: 1 | obj2 | obj3
属性赋值与删除
属性赋值实质
规则
只会对当前操作对象 添加 / 设置 属性值
不会影响到原型对象
案例
1 2 3 4 5 6 7 8 9 const obj1 = { a: 1};const obj2 = Object.create(obj1);obj2.b = 2;console.log(obj1.a);console.log(obj1.b);console.log(obj2.a);console.log(obj2.b);
原型链
obj1 -> a: 1 | obj2 -> b: 2
属性覆盖
规则
在新对象上 对原型对象同名属性 进行赋值,并不会修改 原型属性
而是在新对象上设置一个跟父级属性同名的属性,从而形成属性覆盖
访问时,采用就近原则
案例
1 2 3 4 5 6 7 8 const obj1 = { a: 1};const obj2 = Object.create(obj1);obj2.a = 2;console.log(obj1.a);console.log(obj2.a);console.log(obj2.__proto__.a);
原型链
obj1 -> a: 1 | obj2 -> a: 2
注意
因为 js 中函数可以存在变量里,所以属性也可能是函数
可以使用这个机制达到 方法覆盖的效果
删除属性
规则
可以使用 delete
关键字,对属性进行删除
删除时,不会影响原型对象
案例
1 2 3 4 5 6 7 8 const obj1 = { a: 1};const obj2 = Object.create(obj1);obj2.a = 2;delete obj2.a;delete obj2.a;console.log(obj2.a);
原型链
after line 5 obj1 -> a: 1 | obj2 -> a: 2 after line 6 obj1 -> a: 1 | obj2 after line 7 obj1 -> a: 1 | obj2
属性归属区分
hasOwnProperty()
查询当前对象自身是否拥有属性
1 2 3 4 5 6 7 const obj1 = { a: 1};const obj2 = Object.create(obj1);obj2.b = 2;console.log(obj2.hasOwnProperty("a"));console.log(obj2.hasOwnProperty("a"));
Object.keys()
调出来所有当前对象自身拥有的属性名
1 2 3 4 5 6 const obj1 = { a: 1};const obj2 = Object.create(obj1);obj2.b = 2;console.log(Object.keys(obj2));
in 语句
查询当前对象及原型链上是否拥有属性
1 2 3 4 5 6 7 const obj1 = { a: 1};const obj2 = Object.create(obj1);obj2.b = 2;console.log("a" in obj2);console.log("b" in obj2);
for in 语句
循环遍历当前对象 和原型链上 所有的属性。
重名属性,只会出现一次
1 2 3 4 5 6 7 8 9 10 const obj1 = { a: 1};const obj2 = Object.create(obj1);obj2.a = 2;obj2.b = 2; for (const key in obj2) { console.log(key + " -> " + obj2[key]);}
旧版面向对象
使用原型链模拟
思路
使用 原型对象 表达 类,放方法
使用 子对象 表达 对象,放属性
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const Point = { trace: function() { console.log(`(${this.x}, ${this.y})`); }} const p1 = Object.create(Point);p1.x = 1;p1.y = 1;const p2 = Object.create(Point);p2.x = 3;p2.y = 4; p1.trace();p2.trace();
原型链
Point -> trace: func | + p1 -> x: 1, y: 1 + p2 -> x: 3, y: 4
使用 new 与 prototype
三元素
构造方法、类、对象
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 function Point(x, y) { this.x = x; this.y = y;} Point.prototype.trace = function() { console.log(`(${this.x}, ${this.y})`);} const p1 = new Point(1, 1);const p2 = new Point(3, 4);p1.trace();p2.trace();
new
1 const p1 = new Point(3, 4);
等效于
1 2 3 const obj = Object.create(Point.prototype);obj.Point(3, 4);const p1 = obj
三元素关系

ES 2015 面向对象
基础结构
新语法可以使用 class
去描述一个类
属性写在 构造方法里
方法 不用 function
关键字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Point { constructor(x, y) { this.x = x; this.y = y; } trace() { console.log(`(${this.x}, ${this.y})`); }} const p1 = new Point(1, 1);const p2 = new Point(3, 4);p1.trace();p2.trace();
与旧版的共同性
虽然使用了新的语法,但实际内部的机制 跟 旧版 一样
依然存在 三元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Point { constructor(x, y) { this.x = x; this.y = y; } trace() { console.log(`(${this.x}, ${this.y})`); }} const p1 = new Point(1, 1);const p2 = new Point(3, 4);console.log(p1.__proto__.hasOwnProperty("trace"));console.log(Point.prototype.hasOwnProperty("trace"));
原型链
Point.prototype -> trace(); | + p1 -> x: 1, y: 1 + p2 -> x: 3, y: 4
原型链在 js 中 表达 类 和 对象
继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Point { constructor(x, y) { this.x = x; this.y = y; } trace() { console.log(`(${this.x}, ${this.y})`); }} class Point3D extends Point { constructor(x, y, z) { super(x, y); this.z = z; } trace() { console.log(`(${this.x}, ${this.y}, ${this.z})`); }} const p1 = new Point3D(1, 2, 3);p1.trace();
原型链
Point.prototype -> trace(); | Point3D.prototype -> trace(); | p1 -> x: 1, y: 2, z: 3
原型链在 js 中 表达 父类 和 子类
接口 与 抽象类
接口
不支持接口,因为接口 是 调用保障,类型检查使用,而 js 没有编译时类型检查机制
抽象类
不支持抽象类
静态资源
也支持 static
关键字声明 静态属性 和 静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Point { constructor(x, y) { this.x = x; this.y = y; } trace() { console.log(`(${this.x}, ${this.y})`); } static zero = new Point(0, 0); static ofSize(size) { return new Point(size, size); }} Point.zero.trace();Point.ofSize(3).trace();
class 与 module
使用 default export 类
Point.js
1 2 3 4 5 6 7 8 9 10 class Point { constructor(x, y) { this.x = x; this.y = y; } trace() { console.log(`(${this.x}, ${this.y})`); }}export default Point;
main.js
1 2 3 4 import Point from "./Point" const p1 = new Point(3, 4);p1.trace();
使用 module 作为 namespace
Graph.js
1 2 3 4 5 6 7 8 9 export class Point { constructor(x, y) { this.x = x; this.y = y; } trace() { console.log(`(${this.x}, ${this.y})`); }}
main.js
1 2 3 4 import { Point } from "./Graph" const p1 = new Point(3, 4);p1.trace();
class 与 module
如果一个类 全是静态属性和资源,我们可以不使用 面向对象 的 class
转而使用 基础的 module 和 function
Dice.js
1 2 3 4 5 6 export function roll(amount) { return Math.floor(Math.random() * amount);}export function roll6() { return roll(6);}
main.js
1 2 3 import Dice from "./Dice" console.log(Dice.roll6());