如果尚未声明属性,如何链接到该属性? (在一堂课中)

时间:2019-05-09 06:45:15

标签: javascript

因此,父类Selectthis.elem声明为DOM元素<select>this.value,它们链接到选定选项的值

class Select  {

    constructor(classList, isTwoLevel, index){

        this.elem = document.createElement("select") 
        this.value = this.elem.children[this.elem.selectedIndex].value;// error here!   
    }
}

子类MySelect添加选项,为其分配值,并将其附加到this.elem

class MySelect extends Select {

    constructor(){

        super();

        let opt1 = document.createElement("option");
        opt1.value = "foo";
        this.elem.appendChild(opt1);

        let opt2 = document.createElement("option");
        opt2.value = "bar";
        this.elem.appendChild(opt2);

    }
}

按预期,在创建MySelect类的新示例时,会发生错误:

let testSelect = new MySelect(); // Uncaught TypeError: Cannot read property 'value' of undefined

document.body.appendChild(testSelect.elem);

我不想将this.value的声明移到子类中,因为它应该是所有子类的通用属性,我该怎么办?

3 个答案:

答案 0 :(得分:4)

您可以将value变成吸气剂:

class Select  {

    constructor(classList, isTwoLevel, index){

        this.elem = document.createElement("select") 
    }
    get value() {
        return this.elem.children[this.elem.selectedIndex].value;
    }
}
class MySelect extends Select {

    constructor(){

        super();

        let opt1 = document.createElement("option");
        opt1.value = "foo";
        this.elem.appendChild(opt1);

        let opt2 = document.createElement("option");
        opt2.value = "bar";
        this.elem.appendChild(opt2);

    }
}

const testSelect = new MySelect();
document.body.appendChild(testSelect.elem);
console.log(testSelect.value);

您也可以在首次访问实例时直接在实例上分配属性,以提高性能,从而使getter仅运行一次:

class Select  {

    constructor(classList, isTwoLevel, index){

        this.elem = document.createElement("select") 
    }
    get value() {
        console.log('getter running');
        const theValue = this.elem.value;
        Object.defineProperty(this, 'value', { value: theValue });
        return theValue;
    }
}
class MySelect extends Select {

    constructor(){

        super();

        let opt1 = document.createElement("option");
        opt1.value = "foo";
        this.elem.appendChild(opt1);

        let opt2 = document.createElement("option");
        opt2.value = "bar";
        this.elem.appendChild(opt2);

    }
}

const testSelect = new MySelect();
document.body.appendChild(testSelect.elem);
console.log(testSelect.value);
console.log(testSelect.value);

您还可以简化

this.elem.children[this.elem.selectedIndex].value;

this.elem.value;

如果需要。这也回避了{-1的selectedIndex抛出错误(value是空字符串)的问题:

class Select  {

    constructor(classList, isTwoLevel, index){

        this.elem = document.createElement("select") 
    }
    get value() {
        console.log('getter running');
        const theValue = this.elem.value;
        Object.defineProperty(this, 'value', { value: theValue });
        return theValue;
    }
}
class MySelect extends Select {

    constructor(){

        super();

        let opt1 = document.createElement("option");
        opt1.value = "foo";
        this.elem.appendChild(opt1);

        let opt2 = document.createElement("option");
        opt2.value = "bar";
        this.elem.appendChild(opt2);

    }
}

const testSelect = new MySelect();
document.body.appendChild(testSelect.elem);
console.log(testSelect.value);
console.log(testSelect.value);

答案 1 :(得分:2)

这些不是声明,它们只是赋值。

如果您想在父级使用value,但是在子类完成初始化之前没有要分配的值,则可以:

  1. 在父级别将其分配给null,然后再为其分配另一个值,或者

  2. 将选择的选项传递到父构造函数中,或

  3. value作为访问者属性(获取器)设为CertainPerformance pointed out

答案 2 :(得分:0)

这是对先前答案的稍微修改的方法。如果将cfg对象传递给类,则可以选择要绑定的元素的哪些属性。此代码段还添加了一个事件处理程序,以演示value属性的绑定。

属性的绑定已移至其自己的方法。

对代码的改进可能包括检查以确保元素上的属性首先存在。

class Select  {

    constructor(cfg){
        this.elem = document.createElement("select") 
        this.bindProps(cfg.bindProps)
    }
    
    bindProps(props){
      if( !props ) return;
      props.forEach((prop) => {
        Object.defineProperty(this, 'value', {
          get: () => this.elem[prop],
          set: (val) => this.elem[prop] = val
        })
      })
    }
}

class MySelect extends Select {

    constructor(cfg){

        super(cfg);
        
        let children = [
          { label: 'Foo', value: 'foo' },
          { label: 'Bar', value: 'bar' }
        ]
        children.forEach(({label, value}) => {
          let opt = document.createElement("option");
          opt.label = label;
          opt.value = value;
          this.elem.appendChild(opt);
        })
    }
}

const testSelect = new MySelect({bindProps: ['value']});
document.body.appendChild(testSelect.elem);

testSelect.elem.addEventListener('change', (evt) => {
  console.log('testSelect.elem.value', testSelect.elem.value);
  console.log('testSelect.value', testSelect.value);
})