当前位置: 首页 > Web前端 > JavaScript

从me.name='forceddd'开始

时间:2023-03-26 20:18:44 JavaScript

像me.name='forceddd'这样的赋值语句在JavaScript中无处不在。但是我们真的了解这段代码的作用吗?是否创建了新属性?您是否修改了原始属性的值?操作成功还是失败?这只是一个简单的赋值语句,但是我们仔细想想就会发现,这种细节其实并没有那么简单。一切都可以归类和讨论。首先分为两种:对象me有name属性和没有name属性。当然,我们都知道JavaScript中有原型链机制,所以当对象me中不存在name属性时,也可以分为两种情况:me的原型链上存在name属性和没有名称属性。也就是可以分三类来讨论。name属性已经存在于meconstme={name:'me',};me.name='forceddd';控制台日志(我的名字);//forceddd在这种情况下显然我们的目的是重置name属性的值,结果似乎也很明显,me.name被更改为'forceddd'。但是不要忘了,对象的属性也有它自己的属性,包括是否只读(可写),(通常用来描述对象某个属性的各种特性的对象称为属性描述符或描述对象的属性),那么当name属性可写为false时会发生什么?不仅如此,当name属性定义了getter或setter函数并成为访问描述符时会发生什么?当name属性的writable为falseconstme={name:'me',};Object.defineProperty(me,'name',{writable:false,value:me.name,});me.name='forceddd';//name的属性值还是'me'console.log(me.name);//me因为name属性是只读的,所以赋值操作me.name='forceddd'失败了,这个恒河狸。但需要注意的是,这种操作失败是无声的,如果我们在操作后不验证name值,是很难发现的。使用严格模式可以将这种静默失败转变为显式类型错误。'usestrict';//使用严格模式constme={name:'me',};Object.defineProperty(me,'name',{writable:false,value:me.name,});me.name='forceddd';//TypeError:当名称属性是访问描述符(定义了getter或setter)时,无法分配给对象'#'的只读属性'name'constme={_name:'me',得到名字(){返回这个._name;},setname(v){console.log('callsetter,setname');这个._name=v;},};me.name='forceddd';//调用setter,setnameconsole.log(me.name);//forceddd此时name属性有setter函数,会调用setter函数执行赋值操作时。一般来说,我们在定义访问描述符的时候,getter和setter是成对出现的,但是只定义其中一个是没有问题的,但是这样的话,可能会出现一些意想不到的情况,比如,当名字只有一个getter函数的时候定义了,没有setter函数,赋值操作是没有意义的。在非严格模式下,赋值失败,因为没有setter函数。constme={_name:'me',getname(){returnthis._name;},};me.name='forceddd';console.log(me.name);//mestrict模式,没有给setter函数的访问描述符赋值,会出现TypeError,也很海狸。'usestrict';constme={_name:'me',getname(){returnthis._name;},};me.name='forceddd';//TypeError:Cannotsetpropertynameof#whichhasonlyagetter总结一下,在我有name属性的情况下,执行一次有三种情况赋值操作:当属性为访问描述符时,如果有setter,则调用setter;如果没有setter,非Fails在严格模式下静默,在严格模式下引发TypeError。当属性的属性描述符为可写(false)时,在非严格模式下会静默失败,在严格模式下会产生TypeError。当属性不属于以上两种情况时,设置值为属性值,则赋值成功。我和它的原型链上不存在name属性。此时会在对象me上新建一个属性name,值为'forceddd'。这也是我们经常使用的一种方式。consthuman={};constme={};Object.setPrototypeOf(me,human);me.name='forceddd';console.log({me,human});//{me:{name:'forceddd'},human:{}}我们这样创建的属性当然可以通过赋值来修改,也就是说此时这个属性的属性描述符中的writable为true.其实这时候属性的可配置性和可枚举性也是成立的,区别于通过defineProperty添加的属性的默认值。defineProperty方法添加的属性描述符中的writable、configurable和enumerable的默认值都为false。这一点也值得注意。console.log(Object.getOwnPropertyDescriptor(me,'name'));//{//value:'forceddd',//writable:true,//enumerable:true,//configurable:true//}通过defineProperty方法添加属性Object.defineProperty(me,'prop',{value:'forceddd'});console.log(Object.getOwnPropertyDescriptor(me,'prop'));//{//value:'forceddd',//writable:false,//enumerable:false,//configurable:false//}没有name属性在我,但是它的原型链上有一个name属性。这时候我的原型对象human上就有了name属性。很多时候,我们使用me.name='forceddd'这样的赋值语句只是为了修饰对象me,并不想牵扯到其他对象。但是在JS中,访问对象属性时,如果对象上不存在该属性,就会去原型链中寻找,所以对象的原型链很难绕过。consthuman={};constme={};Object.setPrototypeOf(me,human);上面说到人对象上的name属性有三种情况:name属性的属性描述符中writable为false//设置人对象的name属性Object.defineProperty(human,'name',{writable:false,value:'human',});me.name='forceddd';console.log(me);//{}这时候我们在进行赋值操作的时候,勾选me对象,就会发现它没有名称属性。什么鬼?很难理解,因为human中的name属性是只读的,所以原型为human对象的对象不能通过=赋值操作添加name属性。其实这是模仿类的继承行为。如果父类中的name属性是只读的,那么继承它的子类中的name属性也应该是只读的。而由于JS中通过原型链模拟的类的继承行为,导致了这种看似奇怪的现象。同样,如果在严格模式下,不会静默失败,而是会引发TypeError。name属性是一个访问描述符Object.defineProperty(human,'name',{set(){console.log('Human'ssetterwascalled');}});me.name='forceddd';//'Calledhuman'console.log(me)的setter(me);//{}此时不会在me对象上创建name属性,而是会调用human上的name属性的setter函数。类似地,当只有getter存在而没有setter存在时,将在严格模式下引发TypeError,并在非严格模式下静默失败。name属性不是前两种情况。这个案例是最简单的,也是最符合我们预期的。将在me对象上创建名称属性。总结一下:当我里面没有name属性,但是它的原型链上有name属性时,有三种情况:如果属性是原型链上的访问描述符,并且有setter,那么setter会是称为;如果只有getter,它会在非严格模式下默默地失败,而在严格模式下会出现TypeError。如果该属性是原型链中writable设置为false的只读属性,则它在非严格模式下会静默失败,而在严格模式下会发生TypeError。如果该属性不是原型链上的前两种情况,而只是一个writable设置为true的普通属性,则该属性将在me对象上创建。如果必须、必须修改或设置me的name属性,可以使用defineProperty方法绕过=赋值的这些限制。Object.defineProperty(human,'name',{set(){console.log('Human'ssetterwascalled');//me.name='forceddd';},});Object.defineProperty(me,'name',{value:'forceddd',enumerable:true,writable:true,configurable:true,});console.log(me);//{name:'forceddd'}文字描述并不总是那么直观,最后总结成一张图,简单易懂。