# 在JavaScript中实现链式调用

## 什么是链式调用

``````new Promise((resolve, reject) => {
resolve();
})
.then(() => {
throw new Error('Something failed');
})
.then(() => {
console.log('Do this whatever happened before');
})
.catch(() => {
console.log('Do that');
})

## 逐步实现链式调用

``````const math = require('math');
const c = { a, b };

console.log(a.times(2) + b + 1); // 22
console.log(a.times(2) + b + 2); // 23
console.log(JSON.stringify(c)); // {"a":6,"b":9}

### 基本的链式调用

``````export default {
return this;
},
minus(...args) {
// minus
return this;
},
times(...args) {
// times
return this;
},
divide(...args) {
// divide
return this;
},
}

### 方法如何返回值

``````export default {
value: NaN,
this.value = args.reduce((pv, cv) => pv + cv, this.value || 0);
return this;
},
}

### 问题真的解决了吗

``````const a = math.add(5, 6).value; // 11
const b = math.add(5, 7).value; // 23 而非 12

``````const math = function() {
if (!(this instanceof math)) return new math();
};

math.prototype.value = NaN;

this.value = args.reduce((pv, cv) => pv + cv, this.value || 0);
return this;
};

``````const m = math().add(5, 6);
const c = m.add(5).value; // 16
const d = m.add(5).value; // 21 而非 16

``````math.prototype.add = function(...args) {
const instance = math();
instance.value = args.reduce((pv, cv) => pv + cv, this.value || 0);
return instance;
};

## 最终的完整实现代码

``````class Math {
constructor(value) {
let hasInitValue = true;
if (value === undefined) {
value = NaN;
hasInitValue = false;
}
Object.defineProperties(this, {
value: {
enumerable: true,
value: value,
},
hasInitValue: {
enumerable: false,
value: hasInitValue,
},
});
}

const init = this.hasInitValue ? this.value : args.shift();
const value = args.reduce((pv, cv) => pv + cv, init);
return new Math(value);
}

minus(...args) {
const init = this.hasInitValue ? this.value : args.shift();
const value = args.reduce((pv, cv) => pv - cv, init);
return new Math(value);
}

times(...args) {
const init = this.hasInitValue ? this.value : args.shift();
const value = args.reduce((pv, cv) => pv * cv, init);
return new Math(value);
}

divide(...args) {
const init = this.hasInitValue ? this.value : args.shift();
const value = args.reduce((pv, cv) => pv / cv, init);
return new Math(value);
}

toJSON() {
return this.valueOf();
}

toString() {
return String(this.valueOf());
}

valueOf() {
return this.value;
}

[Symbol.toPrimitive](hint) {
const value = this.value;
if (hint === 'string') {
return String(value);
} else {
return value;
}
}
}

export default new Math();