JS: А если каждая функция вернет "this" в качестве значения по умолчанию?
У меня есть вопрос дизайна кода. Рассмотрим следующий код:
thatObj.doThis().setThat().add().update();
Чтобы разрешить цепочку, я часто пишу return this;
и иногда здесь или там я забываю это делать, тогда я получаю сообщение об ошибке.
Во многих случаях я не спрашиваю о конкретном результате (например, thatObj.getName()
или же thatObj.getChildren()
), но вместо этого хочет сделать некоторые обновления или вызвать сеттеры (например, thatObj.setName("foo")
или же thatObj.addChild(child)
или же thatObj.update()
), Мне было интересно, было бы удобнее return this;
для любого вызова методов я имею в виду поведение javascript по умолчанию, и если нет, то что может быть причиной, чтобы этого не делать.
1 ответ
- JS возвращается
undefined
если вы не вернете что-то явно, - Возвращение конструкторов JS
this
если ваш конструктор не возвращает объект. - CoffeeScript возвращает последнее выражение по умолчанию,
- Ты хочешь
this
должен быть возвращен по умолчанию всеми методами объекта
у каждого свое мнение, что такое "правильный" способ сделать это.
Может ли быть хорошей идеей сказать, что JS всегда будет возвращать это из любых методов?
И от одного момента к другому, по крайней мере 2/3 сети будет сломано. Итак, скажите мне, это хорошая идея?
JS уже давно установил свои правила, и что-то, что не изменится, не изменится (как уже упоминалось в Pointy). Так почему бы вам не позаботиться об этом поведении:
//extracted that from the function to avoid memory leakage
function wrapFunction(fn) {
return function() {
let result = fn.apply(this, arguments);
return result === undefined ? this : result;
}
}
//filter === false => only own methods
//filter === true => own methods and inherited methods
//filter is Array => only the passed keys (if they are methods)
//filter is RegExp => use the RegExp to filter the keys
//filter is function => regular filterFunction
function returnThisAsDefault(objectOrConstructor, filter = false) {
if (objectOrConstructor !== Object(objectOrConstructor))
throw new TypeError("Passed argument must be an object or a constructor. Got ", typeof objectOrConstructor);
const validKey = key => typeof proto[key] === "function" && key !== "constructor" && key !== "prototype";
let proto = typeof objectOrConstructor === "function" ?
objectOrConstructor.prototype :
objectOrConstructor;
let filterFn = Array.isArray(filter) ? filter.includes.bind(filter) :
filter === false || filter === true ? () => true :
filter instanceof RegExp ? filter.test.bind(filter) :
typeof filter === "function" ? filter :
() => false;
let wrapped = {};
for (let p = proto, done = new Set(["constructor", "prototype"]); p != null && p !== Object.prototype;) {
for (let key of Object.getOwnPropertyNames(p)) {
if (typeof proto[key] !== "function" || done.has(key) || !filterFn.call(p, key)) continue;
done.add(key);
let d = Object.getOwnPropertyDescriptor(p, key);
//typeof d.value !== "function" means that proto[key] contains a getter returning a function
if (!d.writable && !d.configurable || typeof d.value !== "function") {
console.log(`function ${JSON.stringify(key)} not fit to be wrapped`, d);
continue;
}
d.value = wrapFunction(d.value);
wrapped[key] = d;
}
if (filter === false) break;
else p = Object.getPrototypeOf(p);
}
Object.defineProperties(proto, wrapped);
return objectOrConstructor;
}
let thatObject = returnThisAsDefault({
doThis() {
console.log("doThis()");
},
setThat() {
console.log("setThat()");
},
add() {
console.log("add()");
},
update() {
console.log("update()");
return "success";
},
});
let result = thatObject.doThis().setThat().add().update();
console.log("result: ", result);
.as-console-wrapper {
top: 0;
max-height: 100%!important
}