# [译] JavaScript 线性代数：向量

·  阅读 4713

class Vector {
constructor(...components) {
this.components = components
}
}

const direction2d = new Vector(1, 2)
const direction3d = new Vector(1, 2, 3)
const color = new Vector(0.5, 0.4, 0.7, 0.15)
const probabilities = new Vector(0.1, 0.3, 0.15, 0.25, 0.2)

## 向量运算

### 加法与减法

class Vector {
constructor(...components) {
this.components = components
}

return new Vector(
...components.map((component, index) => this.components[index] + component)
)
}
subtract({ components }) {
return new Vector(
...components.map((component, index) => this.components[index] - component)
)
}
}

const one = new Vector(2, 3)
const other = new Vector(2, 1)
// Vector { components: [ 4, 4 ] }
console.log(one.subtract(other))
// Vector { components: [ 0, 2 ] }

## 缩放

scaleBy 方法中，我们对所有的向量分量都乘上传入参数的数值，得到新的向量并返回：

class Vector {
constructor(...components) {
this.components = components
}
// ...

scaleBy(number) {
return new Vector(
...this.components.map(component => component * number)
)
}
}

const vector = new Vector(1, 2)
console.log(vector.scaleBy(2))
// Vector { components: [ 2, 4 ] }
console.log(vector.scaleBy(0.5))
// Vector { components: [ 0.5, 1 ] }
console.log(vector.scaleBy(-1))
// Vector { components: [ -1, -2 ] }

## 长度

class Vector {
constructor(...components) {
this.components = components
}
// ...

length() {
return Math.hypot(...this.components)
}
}

const vector = new Vector(2, 3)
console.log(vector.length())
// 3.6055512754639896

## 点积

dotProduct 方法中，接收另一个向量作为参数，通过 reduce 方法来计算对应分量的乘积之和：

class Vector {
constructor(...components) {
this.components = components
}
// ...

dotProduct({ components }) {
return components.reduce((acc, component, index) => acc + component * this.components[index], 0)
}
}

const one = new Vector(1, 4)
const other = new Vector(2, 2)
console.log(one.dotProduct(other))
// 10

class Vector {
constructor(...components) {
this.components = components
}
// ...

normalize() {
return this.scaleBy(1 / this.length())
}
}

const vector = new Vector(2, 4)
const normalized = vector.normalize()
console.log(normalized)
// Vector { components: [ 0.4472135954999579, 0.8944271909999159 ] }
console.log(normalized.length())
// 1

const EPSILON = 0.00000001

const areEqual = (one, other, epsilon = EPSILON) =>
Math.abs(one - other) < epsilon

class Vector {
constructor(...components) {
this.components = components
}
// ...

haveSameDirectionWith(other) {
const dotProduct = this.normalize().dotProduct(other.normalize())
return areEqual(dotProduct, 1)
}
}

const one = new Vector(2, 4)
const other = new Vector(4, 8)
console.log(one.haveSameDirectionWith(other))
// true

class Vector {
constructor(...components) {
this.components = components
}
// ...
haveOppositeDirectionTo(other) {
const dotProduct = this.normalize().dotProduct(other.normalize())
return areEqual(dotProduct, -1)
}
}

const one = new Vector(2, 4)
const other = new Vector(-4, -8)
console.log(one.haveOppositeDirectionTo(other))
// true

class Vector {
constructor(...components) {
this.components = components
}
// ...

isPerpendicularTo(other) {
const dotProduct = this.normalize().dotProduct(other.normalize())
return areEqual(dotProduct, 0)
}
}

const one = new Vector(-2, 2)
const other = new Vector(2, 2)
console.log(one.isPerpendicularTo(other))
// true

## 叉积

class Vector {
constructor(...components) {
this.components = components
}
// ...

// 只适用于 3 维向量
crossProduct({ components }) {
return new Vector(
this.components[1] * components[2] - this.components[2] * components[1],
this.components[2] * components[0] - this.components[0] * components[2],
this.components[0] * components[1] - this.components[1] * components[0]
)
}
}

const one = new Vector(2, 1, 1)
const other = new Vector(1, 2, 2)
console.log(one.crossProduct(other))
// Vector { components: [ 0, -3, 3 ] }
console.log(other.crossProduct(one))
// Vector { components: [ 0, 3, -3 ] }

## 其它常用方法

const toDegrees = radians => (radians * 180) / Math.PI
const toRadians = degrees => (degrees * Math.PI) / 180

### 夹角

class Vector {
constructor(...components) {
this.components = components
}
// ...

angleBetween(other) {
Math.acos(
this.dotProduct(other) /
(this.length() * other.length())
)
)
}
}

const one = new Vector(0, 4)
const other = new Vector(4, 4)
console.log(one.angleBetween(other))
// 45.00000000000001

### 反向

class Vector {
constructor(...components) {
this.components = components
}
// ...

negate() {
return this.scaleBy(-1)
}
}

const vector = new Vector(2, 2)
console.log(vector.negate())
// Vector { components: [ -2, -2 ] }

## 投影

class Vector {
constructor(...components) {
this.components = components
}
// ...

projectOn(other) {
const normalized = other.normalize()
return normalized.scaleBy(this.dotProduct(normalized))
}
}

const one = new Vector(8, 4)
const other = new Vector(4, 7)
console.log(other.projectOn(one))
// Vector { components: [ 6, 3 ] }

### 设定长度

class Vector {
constructor(...components) {
this.components = components
}
// ...
withLength(newLength) {
return this.normalize().scaleBy(newLength)
}
}

const one = new Vector(2, 3)
console.log(one.length())
// 3.6055512754639896
const modified = one.withLength(10)
// 10
console.log(modified.length())

### 判断相等

class Vector {
constructor(...components) {
this.components = components
}
// ...

equalTo({ components }) {
return components.every((component, index) => areEqual(component, this.components[index]))
}
}

const one = new Vector(1, 2)
const other = new Vector(1, 2)
console.log(one.equalTo(other))
// true
const another = new Vector(2, 1)
console.log(one.equalTo(another))
// false