JavaScript OOP
Getting Start - 1- What is OOP
- 面向对象的编程是一种编程范例
- 围绕对象而不是函数
- 一些OOP语言
- C#
- Java
- Ruby
- Python
- JavaScript
Getting Start - 2- Four Pillars of OOP
OOP的4个概念:
- 封装
- 使用封装重新组合相关的变量和函数
- 减少复杂性
- 增加代码的可复用性
- 抽象
- 通过抽象隐藏细节和复杂性
- 隔离了代码更改的影响
- 继承
- 继承可以消除冗余代码
- 多态
- 多态可以避免写出复杂的选择性代码
Getting Start - 3- Setting Up the Development Environment
- 软件:vscode
- 插件:Live Server
Objects - 1- Introduction
Objects - 2- Object Literals
const circle = {
radius: 1,
location: {
x: 1,
y: 1
},
draw: function() {
console.log('draw');
}
};
circle.draw();
Objects - 3- Factories
function createCircle(radius) {
return {
radius,
draw: function() {
console.log('draw');
}
}
}
const circle = createCircle(1);
circle.draw();
Objects - 4- Constructors
function Circle(radius) {
this.radius = radius;
this.draw = function() {
console.log('draw');
}
}
const another = new Circle(1);
Objects - 5- Constructor Property
每个对象都有一个构造函数属性 (.constructor),该属性引用了用来创建这个对象的构造函数
Objects - 7- Value vs Reference Types
Objects - 8- Adding or Removing Properties
function Circle(radius) {
this.radius = radius;
this.draw = function() {
console.log('draw');
}
}
const circle = new Circle(1);
circle.location = { x: 1 };
const pName = 'center location';
circle[pName] = { x: 2};
delete circle['location'];
console.log(circle);
Objects - 9- Enumerating Properties
function Circle(radius) {
this.radius = radius;
this.draw = function() {
console.log('draw');
}
}
const circle = new Circle(1);
for (let key in circle) {
console.log(key, circle[key]);
}
Objects - 11- Private Properties and Methods
function Circle(radius) {
this.radius = radius;
this.defaultLocation = { x:0, y:0 };
this.computeOptimumLocation = function(factor) {
// ...
};
this.draw = function() {
this.computeOptimumLocation(0.1);
console.log('draw');
};
}
const circle = new Circle(10);
function Circle(radius) {
this.radius = radius;
// 将过程变量和函数设为本地变量和本地函数,而不是对象的属性与方法(抽象)
let defaultLocation = { x:0, y:0 };
let computeOptimumLocation = function(factor) {
// ...
};
this.draw = function() {
computeOptimumLocation(0.1);
console.log('draw');
};
}
const circle = new Circle(10);
Objects - 12- Getters and Setters
function Circle(radius) {
this.radius = radius;
let defaultLocation = { x:0, y:0 };
let computeOptimumLocation = function(factor) {
// ...
};
this.draw = function() {
computeOptimumLocation(0.1);
console.log('draw');
};
// 通过defineProperty的方式,设置getter和setter
Object.defineProperty(this, 'defaultLocation', {
get: function() {
return defaultLocation;
},
set: function(value) {
if (!value.x || !value.y)
throw new Error('Invalid location.');
defaultLocation = value;
}
});
}
const circle = new Circle(10);
circle.defaultLocation = {x:10,y:100};
console.log(circle.defaultLocation);
Objects - 14- Exercise- Stopwatch
function StopWatch() {
let startTime, endTime, running, duration = 0;
this.start = function() {
if (running)
throw new Error('Already running')
running = true;
startTime = new Date();
};
this.stop = function() {
if (!running)
throw new Error('Not running')
running = false;
endTime = new Date();
};
this.reset = function() {
running = false;
startTime = 0;
endTime = 0;
duration = 0;
};
Object.defineProperty(this, 'duration', {
get: function() {
return parseInt(endTime - startTime) / 1000;
}
});
}
sw = new StopWatch();
Prototypes - 1- Inheritance
Prototypes - 2- Prototypes and Prototypical Inheritance
- 当访问一个对象的成员时,js引擎会沿着原型链路一直找,直到找到为止
- 原型也是一般的对象
- js中创建的对象都直接或间接的继承自元对象
- 在内存中只有一个元对象的实例
let x = {};
let y = {};
console.log(Object.getPrototypeOf(x) === Object.getPrototypeOf(y));
Prototypes - 3- Multilevel Inheritance
- 如果对象由给定的构造函数创造,那么它们拥有同一个原型
Prototypes - 4- Property Descriptors
let person = { name: 'Mosh' };
Object.defineProperty(person, 'name', {
// 将name改为不可写入,不可配置
writable: false,
enumerable: true,
configurable: false
});
person.name = 'John';
delete person.name;
console.log(person);
Prototypes - 5- Constructor Prototypes
对象. __ proto __ = 构造函数.prototype
// myObi.__proto__ (parent of myObj)
// Constructor.prototype (parent of myObj)
let obj = {};
console.log(obj.__proto__);
console.log(Object.prototype);
Prototypes - 6- Prototype vs Instance Members
function Circle(radius) {
// Instance members
this.radius = radius;
this.move = function() {
this.draw();
console.log('move');
}
}
// Prototype members
Circle.prototype.draw = function() {
console.log('draw');
}
// 就近原则
Circle.prototype.toString = function() {
return 'Circle with radius '+ this.radius;
}
const c1 = new Circle(1);
c1.move();
console.log(c1.toString());
Prototypes - 7- Iterating Instance and Prototype Members
- Object.keys 只返回实例的成员
- for in 循环返回所有的成员
function Circle(radius) {
// Instance members
this.radius = radius;
this.move = function() {
this.draw();
console.log('move');
}
}
// Prototype members
Circle.prototype.draw = function() {
console.log('draw');
}
// 就近原则
Circle.prototype.toString = function() {
return 'Circle with radius '+ this.radius;
}
const c1 = new Circle(1);
// Return instance members
console.log(Object.keys(c1));
// Return all members (instance + prototype)
for (let key in c1) console.log(key);
console.log(c1.hasOwnProperty('move'));
console.log(c1.hasOwnProperty('draw'));
Prototypes - 8- Avoid Extending the Built-in Objects
不要修改不属于你的对象
Prototypes - 10- Exercise
function StopWatch() {
this.startTime = 0;
this.endTime = 0;
this.running = 0;
this.duration = 0;
Object.defineProperty(this, 'duration', {
get: function() {
return parseInt(this.endTime - this.startTime) / 1000;
}
});
}
StopWatch.prototype.start = function() {
if (this.running)
throw new Error('Already running')
this.running = true;
this.startTime = new Date();
}
StopWatch.prototype.stop = function() {
if (!this.running)
throw new Error('Not running')
this.running = false;
this.endTime = new Date();
};
StopWatch.prototype.reset = function() {
this.running = false;
this.startTime = 0;
this.endTime = 0;
this.duration = 0;
};
sw = new StopWatch();
Prototypes Inheritance - 1- Creating Your Own Prototypical Inheritance
function Shape() {
}
Shape.prototype.duplicate = function() {
console.log('duplicate');
}
function Circle(radius) {
this.radius = radius;
}
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.draw = function() {
console.log('draw');
}
const s = new Shape();
const c = new Circle(1);
c.duplicate();
c.draw();
Prototypes Inheritance - 2- Resetting the Constructor
当重设原型对象之后,应该同时重设属性构造器
function Shape() {
}
Shape.prototype.duplicate = function() {
console.log('duplicate');
}
function Circle(radius) {
this.radius = radius;
}
// 当重设原型对象之后,应该同时重设属性构造器
// Circle.prototype.constructor = Circle;
// new Circle.prototype.constructor() <=> new Circle(); 即可以通过prototype的constructor方法创建对象
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;
Circle.prototype.draw = function() {
console.log('draw');
}
const c = new Circle(1);
console.log(c.constructor);
Prototypes Inheritance - 3- Calling the Super Constructor
function Shape(color) {
this.color = color;
}
Shape.prototype.duplicate = function() {
console.log('duplicate');
}
function Circle(radius, color) {
// Child对象设置Parent对象的参数
Shape.call(this, color);
this.radius = radius;
}
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;
Circle.prototype.draw = function() {
console.log('draw');
}
const c = new Circle(1, 'red');
console.log(c);
Prototypes Inheritance - 4- Intermediate Function Inheritance
// 继承函数的写法
function extend(Child, Parent) {
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
}
function Shape(color) {
this.color = color;
}
Shape.prototype.duplicate = function() {
console.log('duplicate');
}
function Circle(radius, color) {
Shape.call(this, color);
this.radius = radius;
}
extend(Circle, Shape);
Circle.prototype.draw = function() {
console.log('draw');
}
const c = new Circle(1, 'red');
console.log(c);
Prototypes Inheritance - 5- Method Overriding
function extend(Child, Parent) {
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
}
function Shape(color) {
this.color = color;
}
Shape.prototype.duplicate = function() {
console.log('duplicate');
}
function Circle() {
}
extend(Circle, Shape);
// 方法的重写
Circle.prototype.duplicate = function() {
Shape.prototype.duplicate.call(this);
console.log('duplicate circle');
}
const c = new Circle();
c.duplicate();
Prototypes Inheritance - 6- Polymorphism
function extend(Child, Parent) {
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
}
function Shape(color) {
this.color = color;
}
Shape.prototype.duplicate = function() {
console.log('duplicate');
}
function Circle() {
}
extend(Circle, Shape);
Circle.prototype.duplicate = function() {
console.log('duplicate circle');
}
function Square() {
}
extend(Square, Shape);
Square.prototype.duplicate = function() {
console.log('duplicate square');
}
const shapes = [
new Circle(),
new Square()
];
for (let shape of shapes)
shape.duplicate();
Prototypes Inheritance - 7- When to Use Inheritance
- 应避免创建层级式继承关系
- 好的组合胜过继承
Prototypes Inheritance - 8- Mixins
function mixin(target, ...sources) {
Object.assign(target, ...sources);
}
const canEat = {
eat: function() {
this.hunger--;
console.log('eating');
}
};
const canWalk = {
walk: function() {
console.log('walking');
}
};
const canSwim = {
swim: function() {
console.log('swimming')
}
};
function Person() {
}
mixin(Person.prototype, canEat, canWalk);
function Goldfish() {
}
mixin(Goldfish.prototype, canEat, canSwim);
const person = new Person();
const goldfish = new Goldfish();
console.log(person);
console.log(goldfish);
Prototypes Inheritance - 9- Exercise- Prototypical Inheritance
function HtmlElement() {
this.click = function() {
console.log('clicked');
}
}
HtmlElement.prototype.focus = function() {
console.log('focused')
};
function HtlmSearchElemen(items = []) {
this.items = items;
this.addItem = function(item) {
this.items.push(item);
};
this.removeItem = function(item) {
this.items.filter(v => v!==item);
// this.items.splice(this.items.indexOf(item), 1);
}
}
// 若采用 HtlmSearchElemen.prototype = Object.create(HtmlElement.prototype); 只能继承baseHtmlElemenr,无法继承click方法
HtlmSearchElemen.prototype = new HtmlElement();
HtlmSearchElemen.prototype.constructor = HtlmSearchElemen;
const e = new HtmlElement();
const se = new HtlmSearchElemen();
Prototypes Inheritance - 11- Exercise- Polymorphism
// HtmlElement
function HtmlElement() {
this.click = function() {
console.log('clicked');
}
}
HtmlElement.prototype.focus = function() {
console.log('focused')
};
// HtlmSearchElemen
function HtlmSearchElemen(items = []) {
this.items = items;
this.addItem = function(item) {
this.items.push(...item);
};
this.removeItem = function(item) {
this.items.splice(this.items.indexOf(item), 1);
}
this.render = function() {
s = this.items.map(item => '<option>'+item+'</option>').join('\n'+' ');
s = `<select> \n ${s}\n<select>`;
return s;
// another way
// return `
// <select>${this.items.map(item => `
// <option>${item}</option>`).join('')}
// </select>`;
// }
}
HtlmSearchElemen.prototype = new HtmlElement();
HtlmSearchElemen.prototype.constructor = HtlmSearchElemen;
// HtmlImageElement
function HtmlImageElement(src = '') {
this.src = src;
this.render = function() {
return `<img src="${this.src}"/>`;
}
}
HtmlImageElement.prototype = new HtmlElement();
HtmlImageElement.prototype.constructor = HtmlImageElement;
const elements = [
new HtlmSearchElemen([1,2,3]),
new HtmlImageElement('https://')
];
for (let ele of elements)
console.log(ele.render());
ES6 Classes - 1- ES6 Classes
class Circle {
constructor(radius) {
this.radius = radius;
this.move = function() {}
}
draw() {
console.log('draw');
}
}
const c = new Circle(1);
ES6 Classes - 2- Hoisting
- 函数声明会置顶(可以在定义之前被引用),函数表达式不会
// Class Declaration
class Circle {
}
// Class Expression
const Square = class{
};
ES6 Classes - 3- Static Methods
// 利用静态方法的方式创建不属于具体实例的工具函数
class Circle {
constructor(radius) {
this.radius = radius;
}
// Instance Method
draw() {
}
// Static Method
static parse(str) {
const radius = JSON.parse(str).radius;
return new Circle(radius);
}
}
const circle = Circle.parse('{"radius": 1}');
console.log(circle);
ES6 Classes - 4- The This Keyword
class Circle {
draw() {
console.log(this);
}
}
const c = new Circle();
// Method Call
c.draw();
// Function Call
const draw = c.draw;
draw();
ES6 Classes - 5- Private Members Using Symbols
// 利用Symbol来隐藏某些属性和方法(使其名称不可见,无法调用)
const _radius = Symbol();
const _draw = Symbol();
class Circle {
constructor(radius) {
this[_radius] = radius;
}
[_draw]() {
}
}
const c = new Circle(1);
// 如果非要调用
const key = Object.getOwnPropertySymbols(c)[0];
console.log(c[key]);
ES6 Classes - 6- Private Members Using WeakMaps
const _radius = new WeakMap();
const _move = new WeakMap();
class Circle {
constructor(radius) {
_radius.set(this, radius);
_move.set(this, () => {
console.log('move', this);
});
}
draw() {
_move.get(this)();
console.log('draw');
}
}
const c = new Circle(1);
c.draw();
ES6 Classes - 7- Getters and Setters
const _radius = new WeakMap();
class Circle {
constructor(radius) {
_radius.set(this, radius);
}
get radius() {
return _radius.get(this);
}
set radius(value) {
if (value <= 0) throw new Error('invalid radius');
_radius.set(this, value);
}
}
const c = new Circle(1);
console.log(c.radius);
ES6 Classes - 8- Inheritance
class Shape {
constructor(color) {
this.color = color;
}
move() {
console.log('move');
}
}
// 使用extends之后不用重设constructor
class Circle extends Shape {
constructor(color, radius) {
// 如果父类中由一个构造器,则子类的构造器必须先调用父类的构造器,以创建一个父类实例
super(color);
this.radius = radius;
}
draw() {
console.log('draw');
}
}
const c = new Circle('red', 1);
console.log(c);
ES6 Classes - 9- Method Overriding
class Shape {
move() {
console.log('move');
}
}
// 使用extends之后不用重设constructor
class Circle extends Shape {
move() {
super.move();
console.log('Circle move')
}
}
const c = new Circle();
c.move();
ES6 Classes - 11- Exercise
const _items = new WeakMap();
class Stack {
constructor() {
_items.set(this, []);
}
get count() {
return _items.get(this).length;
}
peek() {
if (_items.get(this).length === 0)
throw new Error('Empty');
return _items.get(this)[_items.get(this).length];
}
pop() {
if (_items.get(this).length === 0)
throw new Error('Empty');
return _items.get(this).pop();
}
push(obj) {
_items.get(this).push(obj);
}
}
const s = new Stack();
ES6 Tooling - 1- Modules
ES6 Tooling - 2- CommonJS Modules
- 高度关联的东西应该放在一起
// Implementation Detail
const _radius = new WeakMap();
// Public Interface
class Circle {
constructor(radius) {
_radius.set(this, radius);
}
draw() {
console.log('Circle with radius' + _radius.get(this));
}
}
module.exports = Circle;
// require调用的模块和exports公开的对象是一样的
const Circle = require('./circle ');
const c = new Circle(10);
c.draw();
ES6 Tooling - 3- ES6 Modules
// index
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Hello World</h1>
<script type = "module" src="index.js"></script>
</body>
</html>
// circle.js
// Implementation Detail
const _radius = new WeakMap();
// Public Interface
export class Circle {
constructor(radius) {
_radius.set(this, radius);
}
draw() {
console.log('Circle with radius' + _radius.get(this));
}
}
// index.js
import { Circle } from "./circle.js";
const c = new Circle(10);
c.draw();
ES6 Tooling - 4- ES6 Tooling
ES6 Tooling - 5- Babel
- node里的npm功能
npm init --yes
npm i babel -cli@6.26.0 babel -core@6.26.0 babel-preset-env@1.6.1 --save
修改package.json
npm run babel
ES6 Tooling - 6- Webpack
sudo npm i -g webpack -cli@2.0.14
webpack-cli init
npm init --yes
npm run build