为什么在这里需要确切的二传手?

Igo*_*kov 1 javascript

HTML片段

 <div id="container">

    <span class="no"></span>
    <span class="yes"></span>
    <span class="no"></span>
    <span class="no"></span>

 </div>
Run Code Online (Sandbox Code Playgroud)

因此,一个getter会返回一个元素.yes并提供延迟的评估(我认为这是术语,对吧?

class Something{
    constructor(){
        this.elem = container;
    }

    get childYes(){
        let span = this.elem.querySelector(".yes");
        this.childYes = span;//Cannot set property clientName of #<Docum> which has only a getter
        return span;
    }   
}


var object = new Something();
console.log(object.childYes);
Run Code Online (Sandbox Code Playgroud)

但是,如果我添加一个空的setter,它可以正常工作:

class Something{
    constructor(){
        this.elem = container;
    }

    get childYes(){
        let span = this.elem.querySelector(".yes");
        this.childYes = span;
        return span;
    }   
    set childYes(a){};
}


var object = new Something();
console.log(object.childYes); // <span class="yes"></span>
Run Code Online (Sandbox Code Playgroud)

我在那里不需要二传手,浏览器到底要我做什么?

T.J*_*der 5

无法设置#<Docum>仅具有吸气剂的属性clientName

通过this.childYes = ...定义为访问器的属性,您正在尝试使用setter。要在实例上定义属性,请使用Object.defineProperty

get childYes(){
    let span = this.elem.querySelector(".yes");
    Object.defineProperty(this, "childYes", {
        value: span
    });
    return span;
}   
Run Code Online (Sandbox Code Playgroud)

get childYes(){
    let span = this.elem.querySelector(".yes");
    Object.defineProperty(this, "childYes", {
        value: span
    });
    return span;
}   
Run Code Online (Sandbox Code Playgroud)
class Something{
    constructor(){
        this.elem = container;
    }

    get childYes(){
        console.log("Getter ran");
        let span = this.elem.querySelector(".yes");
        Object.defineProperty(this, "childYes", {
            value: span
        });
        return span;
    }   
}

var object = new Something();
console.log(object.childYes); // Runs the getter
console.log(object.childYes); // Uses the data property
Run Code Online (Sandbox Code Playgroud)


在评论中,您询问:

因此,如果我错了,请纠正我:在调用object.childYes之后,程序首先查找.childYes对象的自身属性;失败 去原型;找到吸气剂 开始执行吸气剂,当该行this.childYes = span;出现时,程序在原型中“在这里”寻找它并失败,对吗?

不是因为this.childYes = span;行在哪里,而是因为那是对的。当您分配给对象属性时,将发生什么取决于该属性是否存在于对象或其原​​型上,如果存在,取决于该属性是数据属性还是访问器属性

  1. 如果该属性根本不存在(在对象或其任何原型上),则JavaScript引擎会将其创建为原始对象上的data属性,并为其赋值。
  2. 如果它作为数据属性存在(在对象或其任何原型上),则引擎将在原始对象上创建或更新它,并为其分配值。
  3. 如果它作为访问器属性存在,则
    1. 如果它有一个setter,它将调用该setter
    2. 如果不是,则抛出错误

在原始代码中,您结束了上面的步骤3.2,因为该属性在原型上作为访问器属性存在。

这是这些各种情况的示例:

<div id="container">
    <span class="no"></span>
    <span class="yes"></span>
    <span class="no"></span>
    <span class="no"></span>
</div>
Run Code Online (Sandbox Code Playgroud)
"use strict";

// A function to tell us if an object has a property and, if so
// what kind of property it is
function getPropertyType(obj, propName) {
    const descr = Object.getOwnPropertyDescriptor(obj, propName);
	if (!descr) {
		return "none";
	}
	if (descr.hasOwnProperty("get") || descr.hasOwnProperty("set")) {
		return "accessor";
	}
	if (descr.hasOwnProperty("value") || descr.hasOwnProperty("writable")) {
		return `data (${descr.value})`;
	}
	return "generic"; // Unlikely, but the spec allows for it
}

// An object to use as a prototype
const proto = {
    dataProperty1: "dataProperty1 value",

    _readWrite: "readWriteAccessor default value",
    get readWriteAccessor() {
      return this._readWrite;
    },
    set readWriteAccessor(value) {
      this._readWrite = value;
    },
    
    get readOnlyAccessor() {
      return "readOnlyAccessor value";
    }
};

// Create an object using `proto` as its prototype
const obj = Object.create(proto);

console.log(`obj dataProperty2:       ${getPropertyType(obj, "dataProperty2")}`);
console.log(`proto dataProperty2:     ${getPropertyType(proto, "dataProperty2")}`);

console.log(`--- Before obj.dataProperty1 = "dataProperty1 updated";`);
console.log(`obj dataProperty1:       ${getPropertyType(obj, "dataProperty1")}`);
console.log(`proto dataProperty1:     ${getPropertyType(proto, "dataProperty1")}`);
obj.dataProperty1 = "dataProperty1 updated";
console.log(`--- After obj.dataProperty1 = "dataProperty1 updated";`);
console.log(`obj dataProperty1:       ${getPropertyType(obj, "dataProperty1")}`);
console.log(`proto dataProperty1:     ${getPropertyType(proto, "dataProperty1")}`);

console.log(`--- Before obj.dataProperty2 = "dataProperty2 updated";`);
console.log(`obj dataProperty2:       ${getPropertyType(obj, "dataProperty2")}`);
console.log(`proto dataProperty2:     ${getPropertyType(proto, "dataProperty2")}`);
obj.dataProperty2 = "dataProperty2 updated";
console.log(`--- After obj.dataProperty2 = "dataProperty2 updated";`);
console.log(`obj dataProperty2:       ${getPropertyType(obj, "dataProperty2")}`);
console.log(`proto dataProperty2:     ${getPropertyType(proto, "dataProperty2")}`);

console.log(`--- Before obj.readWriteAccessor = "readWriteAccessor updated";`);
console.log(`obj readWriteAccessor:   ${getPropertyType(obj, "readWriteAccessor")}`);
console.log(`proto readWriteAccessor: ${getPropertyType(proto, "readWriteAccessor")}`);
obj.readWriteAccessor = "readWriteAccessor updated";
console.log(`--- After obj.readWriteAccessor = "readWriteAccessor updated";`);
console.log(`obj readWriteAccessor:   ${getPropertyType(obj, "readWriteAccessor")}`);
console.log(`proto readWriteAccessor: ${getPropertyType(proto, "readWriteAccessor")}`);

console.log(`obj readOnlyAccessor:    ${getPropertyType(obj, "readOnlyAccessor")}`);
console.log(`proto readOnlyAccessor:  ${getPropertyType(proto, "readOnlyAccessor")}`);
console.log(`--- Before obj.readOnlyAccessor = "readOnlyAccessor updated";`);
try {
	obj.readOnlyAccessor = "readOnlyAccessor updated"; // Would fail silently in loose mode, but we're using strict
	console.log(`Worked!`);
} catch (e) {
	console.error(`Assignment failed: ${e.message}`);
}
console.log(`--- After obj.readOnlyAccessor = "readOnlyAccessor updated";`);
console.log(`obj readOnlyAccessor:    ${getPropertyType(obj, "readOnlyAccessor")}`);
console.log(`proto readOnlyAccessor:  ${getPropertyType(proto, "readOnlyAccessor")}`);
Run Code Online (Sandbox Code Playgroud)