如何定义类内所有方法可访问的变量?JS类方法调用问题咨询
嘿,作为从Ruby转JavaScript的新手,遇到this指向混乱和变量传递的问题真的太常见啦!你拆分getSubcategories和displaySubcategories的思路完全没问题——单一职责的代码更易维护,这和Ruby的编程理念是一致的。咱们来一步步优化你的实现,解决那些头疼的问题:
1. 告别self = this:用箭头函数固定this指向
JavaScript里普通函数的this是动态绑定的,而箭头函数会继承外层作用域的this,刚好能解决你在fetch回调里的this异常问题。同时,forEach里也可以换成箭头函数,不用手动绑定this参数:
import {Controller} from "stimulus" export default class extends Controller { static targets = ["categoryId", "categoryLabel", "subcategoryList", "subcategoryHeader"] getSubcategories(event) { // 直接把event作为参数传入方法(Stimulus会自动传递事件对象) const categoryId = event.target.firstElementChild.value const url = `/api/categories/${categoryId}/subcategories?levels=1` fetch(url) .then(response => response.json()) // 箭头函数里的this就是当前Controller实例,不用存self了 .then(jsonResponse => this.displaySubcategories(jsonResponse, event)) } displaySubcategories(jsonResponse, event) { // 用箭头函数替代普通函数,this自动指向Controller实例 jsonResponse.subcategories.forEach(subcategory => { const currentLabel = event.target.closest('.category-label') // 假设你需要这个元素 const categoryLabelCopy = this.cloneLabel(currentLabel, subcategory) currentLabel.classList.add("chosen") this.subcategoryListTarget.appendChild(categoryLabelCopy) }) } // 假设你有cloneLabel方法,这里也可以用箭头函数或者保持普通函数(只要调用时this正确) cloneLabel(original, subcategory) { const copy = original.cloneNode(true) copy.textContent = subcategory.name copy.querySelector('input').value = subcategory.id return copy } }
2. 类似Ruby实例变量的实现:用类实例属性共享数据
如果你想在多个方法间共享变量(比如当前选中的分类ID、事件对象),可以在Controller类里定义实例属性,就像Ruby的@instance_var一样:
import {Controller} from "stimulus" export default class extends Controller { static targets = ["categoryId", "categoryLabel", "subcategoryList", "subcategoryHeader"] // 定义实例属性,类似Ruby的@current_event和@current_category_id currentEvent = null currentCategoryId = null getSubcategories(event) { this.currentEvent = event this.currentCategoryId = event.target.firstElementChild.value const url = `/api/categories/${this.currentCategoryId}/subcategories?levels=1` fetch(url) .then(response => response.json()) .then(jsonResponse => this.displaySubcategories(jsonResponse)) } displaySubcategories(jsonResponse) { // 直接访问实例属性,不用再传参数 const currentLabel = this.currentEvent.target.closest('.category-label') jsonResponse.subcategories.forEach(subcategory => { const categoryLabelCopy = this.cloneLabel(currentLabel, subcategory) currentLabel.classList.add("chosen") this.subcategoryListTarget.appendChild(categoryLabelCopy) }) } }
这样一来,你就不用在方法间传递event这类参数了,和Ruby里用实例变量共享状态的方式几乎一致。
3. 要不要拆分方法?当然要!
你拆分方法的思路非常正确——getSubcategories专注于数据请求,displaySubcategories专注于DOM渲染,这种分离让代码更易测试、更易修改。比如以后要改API地址,只需要动getSubcategories;要改子分类的渲染样式,只需要动displaySubcategories,完全符合“单一职责原则”,这在任何编程语言里都是好实践。
额外小提示:利用Stimulus的Target特性
你已经用到了Stimulus的Target,记得在方法里用this.subcategoryListTarget这种方式访问DOM元素,比手动用querySelector更可靠,也更符合Stimulus的设计理念~
内容的提问来源于stack exchange,提问作者sloneorzeszki

