简直一切运用Vue的开发者都知道,Vue的双向绑定是经过Object.defineProperty()完成的,也知道在getter中搜集依赖,在setter中告诉更新。

那么除了知道getter和setter之外,OC g @ ) ; z Jbject.defineProperty()还有哪些值得咱们去注意的G L 2 }地方呢?是不是有许多细节的东西不懂呢?

你或许会说,除了getter和setter之外,Object.defineProperty()还有value,writable,enumerable,configo p I R D j f Z 0urable。

那么问题来了?

  • value或writable与getter,setter能够共存吗?与enumerabl? O s ne,W Q l j O x _ oconfigurable呢?
  • 概括讲下writable,enumerable,configurable分别是什么意思?
  • enumerable在Object.keys()和for…in以及打开操作符…是怎么体现的?
  • configurable会约束哪些特点不f s + @ 0 A行redefine?value会被约束吗?会约束特点的删去吗?
  • 经过obj.foo和Object.defineProperty(obj,foo)办法界说的特点有何差异?
  • data descriptor、acG L 0 S $ @ ~ |cessor descriptor、shared descriptor是什么?

假如看了上面这些问题一脸懵逼,不要慌张,咱们先来看一道非常直观易懂的标题:

// 完成下面的逻辑
console.log(a+a+a); // 'abc'

标题看完了) l d,带着问题开始阅读下面的内容吧。

假如能耐性看完的话关于个人的前端技能提高会非常大。

往近了j q W 3 % Y说,不出意外上面这些问题悉数能够方便的处理,关于a+a+a标题的题解也会了解愈加透彻。

往远f s g p 2 a C O了说,能够去看懂Vue源码相关的完成,以及看懂任何运用到Object.defineProperty()) . k H ? S这个API的库的源码完成,甚至最终自己写个小轮子。

  • 初识Object.defineProf n U c , K / 1perty()
  • 语法
    • 参数
    • 回来值
  • Object.d[ r ~ & J (efineProperty()概览
    • 基本知识点
    • data和accessor两种描述符
    • 描述符u + X e m T E d 3有必要是data, accessor之一,不能一起具有两种特性
    • 怎么差异datad % ] 8 i $ } descriptor和accessor descriptorW : H V
    • descriptor key概览
      • 同享descriptor key概览
      • data de8 D ] p w A 6 ]scriptor key概览
      • accessor descriptor key概览
    • 紧记特点不仅仅是des5 5 dcriptor自己的特点L 6 g + i,还要考虑承继特点
    • 三个很根底可是很好的比如
      • 默许descriptor:不行写,o O M y不行枚举4 : J,不行配置
      • 重用同一目标回忆上一次的value值
      • 冻结Obje$ Y & V s 6 ^ct.prototype
  • Object.defineProperty()详解
    • 创立一个property
    • 修正z , w p 5 a 一个property
      • Writ1 P u E P Q Q 9able attribute
      • Enumerable attribute
        • 知识点
        • 在for…in中怎么体现?
        • 在Objec! * d 5 c D k Dt.keys()中怎么体现?
        • 在打开操作符…中怎么体现?
        • 怎么检测特点是否能够枚举?
      • Configurable attribute
    • 增加特点和默许值
    • 自界说setter和getter
    • p( X s G ( X Vroperties的承继
  • 怎么获取特点的de * 5 =scriptor?
  • console.log(a+a+a); // 'abc'1 3 & 9 l V题解

    • 解法1: Objecy } u w ] 1 v 9t.defineProperty() 外部变量
    • 解法1(优化版):Object.defineProperty~ J B ` Q 0 ? ^ y() 内部变量
    • 解法2: Object.prototpye.valY X AueOf()
    • 解法3:cht ! x UarCodeAt( R Y C E O c M,charFromCode
    • 解法3(优化版一):内部变量this._count和_code
    • 解法3(优化版二e c n d):J S f k D内部变量this._code
    • 标题扩展: 打印a...z
    • 标题扩展(优化版): 打印a...z

初识Object.defineProperty()

静态办法Object.defineProperty()会直接在一个目标上界说一个新的特点,或许修正目标上已经存在的特点,然后回来这个目标。

const obj = {};
Object.defineProperty(obj, 'prop', {
value: 42,
writable: true
});
console.log(obj); // {prop: 42}
obj.prop = 43; // {prop: 43}

语法

Object.defineProperty(obj, prop, descriptor)

参数

  • obj 需求界说特点的目标
  • prop 需求n w H y界说或许修正的property的姓名或许Symbol
  • d) 3 w S 2 W r _ _escriptor 界说和修正的property的描述符

回来值

回来传递进函数$ O x | G 8 r Y [的目标。

Object.defineProperty()概览

  • 基本知识点
  • data和accessor两种描述符
  • 描述符有必要是data, accessor之一,不能一起具有两种特性
  • 怎么差异data descriptor和accessor descriptor?
  • descriptor key概览
    • 同享descriptor key概览
    • data descriptor key概览
    • accessor descriptor key概览
  • 紧记特点1 i Z F $ s {不仅仅是desc; m / r % yriptor自己的特点,还要考虑承继特点
  • 三个很根底可是很好的比如
    • 默许descriptor:不行写,不行枚举,不行配置
    • 重用同一目标回忆上一次的value值
    • 冻结Objj } Q ? i ] Nect.p! ` q P C H ~ ` Lrototype

基本知识点

  • Obj5 T : ecta j } _ 2 5 S –.defineProperty()答应精准增加或许修正目标u 0 ? | s上的一C & T ! ~ H _I @ A 6 D d 6特点。
  • 经过const obj = {};obj.foo = 1这种赋值办法增加的特点,能够经过for…in或许Object.keys枚举,% . ; E S他的值或许发作改动,也或许被删去。
  • Object.define: g 9 ( e L 5Property()答$ A 5 3 w |应对目标特点的默许办法做出改动。
  • 默许情况下经过Obj} w ect.defineProperty()是immutable(不行变的T ~ 0 K s v M ( l)。不能经过delete obj.foo删去这个特点。
  • Object.defineProperty(). J O s具有dat: 9 . v ) W [ w @a和accessor两种描述符,描述符生效时只能是其中之一,不能一起生效。
  • data和accessor两种描述符都是object,dataDescriptor = {value, writable},accessorDescriptor={get(){} ( t : ,, set(){}}
  • data和accessor有各自K * w , e独有的key,它们也有同享的key。di F Mata accessor特有的key为value和writable,accessor descriptor特有的k6 q 3 Q 9 .ey为get和set。同享的key为configu7 # G D n Z 6 Erable和enumerable。
  • 假如descriptor没有vad M 0 Glue, writable, get和set,会被作为一个data descriptor;假如一起有value或writable和get或sett : * C m } 1 X e,异常会{ @ y r h y r抛出。

data和accessor两种描述符

目标的特点descr/ a R 0iptor描述符主要有两种:data descriptor和accessor descriptor。

data descu ~ 4 & friptor] C 4

数据描述符指的是vals ) , X p J e due,wG k – }ritable,它或许是可写的K ; . b H,也或许是不行写的。

accessor} c N descrip6 M 1tor

权限描述符指的是经过getter-sq R ) T b ? U zetter函数get(),set()对property的描述。

描述符有必要是data, accessor之一,不能一起具有两种特性

下面K O a的代码会报错的原因破案了:只能是data,accessor 之一。

Object.defineProperty({this, 'a', {
value: 'a', // data descriptor
get(){
// access descripto8 1 0r
}
})
// `Invalid proF C {perty descriptor.Cannot both specify accessors and a0 B h z 1 6 ( c value or wri+ Q ) -table attribue.`

怎么差异data0 x Y w dO V o 3 7 Gescriptor和accessor descriptor?

data accessor特有的ko _ Q R * k H e pey为value和w1 U : a f k R 6 *ritable。
accessor descriptor特有的key为get和set。

//. & t u * 典型a _ Z  ) Y _ q c的datk v 3 ~ [ oa descriptor
Object.defineProperty({W 7 yt$ b w K M k @his, 'a', {
value: 'a',
writable: false
})
// 典型的accessor descriptor
Object.define# / ^ X u { C AProperty({this, 'a', {
get(){ ... }
set(){ ... }n c , { N 3 c 9 J
})

descriptor key概览

默许情况下是经过Object.defineProperty()界说特点的。

同享descriptor key概览
configurable
  • 默许值为false
  • 当且仅当特点的描述符类型或许发作改变以及特点描述符或许从目标上删去和这个特点相关联
  • configurable为false时,非data descriptor的特点不能被重界说,也就是说除value和writable之外的i 3 L , m 特点不能界说* i V d 3 i F H X,而且特别要注意,value能够随意改,而writable仅能从t6 u f 8 & Grue改为far R r P Clse。O + get(), set(), enumerable, confi/ y : ogurable是都不能从头界说7 + A的。
  • 而且不能切换des? ; , l B _ c | hcriptor的类型:dj _ } R 0 G : Xata descriptor和accessor descriptor
  • configurable 不仅仅影响特点的修正,还影响到了特点的删去。configura5 g p L = cble为false时delete obj.o失效

为什么configurable设置为false时要这样规划?

  • 提高目标特点可控性
  • 提高安全H X + Y . ? [ i l

这是由于get(), set(), enumerable, configura$ o K Bble是权限相关的特点,为了防止引起不必要的bug。
许多库的作者不答应自己修正这个特点,让它保持在一种可控的状态,从而代码依照自己的预期去运行。而且这样做也愈加安全。

enumerable
  • 默许值为false
  • 当且仅当目标的特点枚举展现时b % k r W – S R会和这个特点相关联
data descriptor key概{ ^ 2 ) _
value
  • 默许值为undefined
  • 特点相关联的value
  • 能够是任何JavaSv u z n q Q F lcript值 number,object,function等等
writable
  • 默许是false
  • 当且仅当经过赋值操作符赋值时会和这个特点相关联
accessor descriptor key概览
get
  • 默许值为undefined
  • 作为特点的getter服务于特点,假如没有getter的话,get为undefined。
  • 当property被拜访时,这个函数会在不传参的情况下调用然后,并将this设置为拜访特点的目标(this由于承继或许不是界说特点的目标。)– $ S m n E e
  • 回来值会作为property的value。
set
  • 默许值为undefine9 3 o B E Qd
  • 作为特点的setter服务于特点,假如没有setter的话,set为undefined。
  • 当特点从头赋值时,函数在传递一个参数的情况下调用,并将这个集合设置为特点赋值的目标。

紧记特点不仅仅是descriptor自己的特点,还要考虑承继特点

为保证保留了这些默许值:

  • 能够freeze Object.prototype
  • 或许Object.create(null)

三个很根底可是很好的比如

默许descriptor:不行写,不行枚举,不行配置
var obj = {};
var descriptor = Object./ 3 * $ 4 V u Zcreate(null); // no i6 - 1 V ` U W U Nnher+ R  C C / )ited properties
descriptor.value = '+ O _ y ostatic';
// not enumerable, not configurable, not writable as defaults
Object.defineProperty(obj, 'key', descrip` 6 A * k o } I [tor);
// being explicit
Object.defineProperty(obj, 'M c I & 0 = Pkey$ l  Z Q I', {
enR C p f g V h F iumerable: false,
configurable: false,
writable: false,
value: 'static'
});
重用同一目标回忆上一次的value值
function withValue(va0 ) q J 6lue) {) j C X s , n x X
var d = withValue.u ? i Yd || (
// 回忆住上一次的值
withValue.d = {
enumerable: false,
writable: false,
configur` Z table: false,
value: value
}
);
// 防止重复赋值
if (o Q 1 Q r $ W 3 Ud.value !== value) d.value = value;
return d;
}
Object.defineProperty(obj, 'key', withValue('static'));
冻结Object.prototype
Object.f% M #reeze(Object+ ! . 0 R h }.prototype)

Object.defineProperty()详解

创立一个property

特点假如在目标上不存在的话,T @ 1 f } L f gObject.defineProperty()会创立一个新的特点。
能够省掉许多描述符中字段,而且输入这些字段的默许值。

// 创立目标
var o = {};
// 界说特点a而且传入data descn u 9 ( O :riptor
Object.defv X K sineProperty(o, 'a', {
value: 37,
writable: te ! g T ~ j P @ 5rue,
enume * a ! ( kerable: true,
configurable: true,
})
// 界说特点b而且传入accessor descriptor
// 假造value(优点是更细粒度的value操控):外部变量和g_ { X V X %et()
// 假造writable(优点是更细粒度的writable操控):外部变量和set()
// 在= 0 U这个比如中,o.b的值与bValue做了强关联。bValue是什么值,o.b就是什么值。除非o.b被从头界说
var bValue = 38;
Object.deG 8 & j _ j ^fineProperty(o, 'b', {
get() { return bValue },
set(newValue) { bValue = newVlaue },
en{ 7 } a e x ( I uumerable: true,
configurable: true,
})
// 不能够一起混合界说两者
Object.defineProperty(o, 'conflict', {
vw { [ aalue: 'a',
get() { return 'a' }
}. u E 8 . 4 S `)
//4 j n ( 报错:Cannot both specify accessors and a value or writable
// 从头解读n B a K报错:Cannot both specify accC W . F y T G m Bessors descriptor and data descriptor(2 e 0 } Ya value or writable)

修正一个propew ) n % x Q D ] Arty

  • 当一个特点在目标中存在时,Object.defineProperty()能够根据descriptor中的值f E S y和目标回来值的配置尝试修正这个特点。
  • 假如旧的descriptor有configQ y v Y 0 !urable特点,而且设置为false,意思是”不行配置“。
    • 意味着不能修正任意同享descriptor和accessor descriptor的特点的值
    • 能够重界说data descriptor:value任意变,writaI v s @bl[ 8 t l . l d ne只能从true变为false(不能从false改为true)。
    • 而且不能切换descri[ N zptor的类型:data descriptor和accessor descriptor
    • 违背规则报错:Cannot redefine property: xxx;契合规则和没有修正特点的话不报错。
Writable attribute

当writable设置为fC ( % U h ( e ualse时,特点是不行写的,意味着无法从头赋值。

  • 非严格形式不会报错,仅仅赋值失利
  • 严格形式会报错Cannot assign to^ y ? 3 # M read only propertyr u 2 w l 'b' of object '#<Object&gl Q E - 8 ] m zt;'
// 非严格形式不会报错,仅仅赋值失利
var o = {};
Object.definePropertyu A V(o, 'a', {
value: 37R E N,
writable: false
});
console.log(o.a); // l7 . ` N N .ogs4 D . 37
o.a = 25; /N w ) H 7 0 ` S/ 不会报错
// (只会在strict mode报错,或许值没改动也不会报错)l ~ q _
console.log(o.a); // logs 37. 从头赋值没有生效
// 严格形式会报错
//^ ? a Q v ! q 0 & strict mode
(6 ) j #f0 9 b k ! b s B }unct- ) T = bion() {
  'use strict';
var o =/ M 4 s ] {};
Object.defineProperty(o, 'b', {
value: 2,
writable: false
});
o.b = 3; // 抛出CannF n Q A & Z B o zot assign to ra v e ~ead only p3 K Jroperty 'b' of object '#<O8 f B x c Lbject>'
return o.b; // 2
}());
Enumerable attribute
  • 知识点
  • 在fos _ 8 Yr…in中怎么体8 x )现?
  • 在Object.ke– o p A 4 jys()中怎么体现?
  • 在打开操作符…中怎么体现?
  • 怎么检测特点是否能够枚举?
知识点
  • enumerable特点界说了特点是否能够被Object.assign()或许spread(…) pick到。
  • 关于非symbol的特点,它还会影响到for…in和Object.keys()对特点的pick。
  • 能够用obj.propertyIsEnumerae 8 - E w jble(prop)检测特点是否可遍历。
var o = {}P N  F X P;V Z W V T O x x
Object.defineProperty(o, 'a', {
value: 1,
enumerable: true
});
Object.define. % # 4Property5 K D - u 7(o, 'b', {
val~ Q ? ] 3 N G X Hue: 2,
enumerable: false
});
Object.defineProperty(o, 'c', {
value: 3, // enumerable默许为false
});
o.d = 4; // enumerable默/ _ ` B S C n d许为true
Object4 _ { C a M 8  P.defineProperty(o, Symbol.for('e'), {
value: 5,
enumG e $erable: true
});
Object.d0 C ( u mefinK u i  #eProperty(o, SymbolU s ( s = s T @.for('f'), {
value: 6,
enumerF ) -able: false
});
在for…in中怎么体现?

只有’a’和’d’打印了出来。
enumerable为true的都能被解构出来,不包括S( 5 Z – o % c ymbol。

for (v^ A 6ar i in o) {
console.log(i); // 'a','d'
}
在Object.keys()中怎么体现?

只有’a’和’d’被搜集到。
enumerable为true的都能被解构出来,不包括Symb$ x d ` ( Vol。

Object.keys(o); // ['a', 'd']
在打开操作符…中怎么体现?

enumerable为true的都能被解构出来,包括Sy# = : – pmbol。

var p = { ...o }
p.a // 1
p.b // undefined
p.c // undefined
p.d // 4
p[Symbol.for('e')] // 5
p[Symbol.for('f')] // undefined
怎么检测特点是否能够枚举?

能够用obj.propeG & Y q F RrtyIsEnumerable(prop)检测特点是否可遍历

o.propertyIsEnumerab{ A + t V + (le('a'); // true
o.propertyIsEnumerable('b'); // false
o.propertyIsEnumerable('c'); // false
o.propertyIsEnumerable('d'); // true
o.propertyIsEnumer, l S 2able(SyO y & ? y ] $ {mbol.for(, Y 3 X Q } ^ ]'e')); // true
oO : 4 A ! N b C @.propertyIsEnumerable(Symbol.for('f')); // false
Configurable attribute

configi $ #urable特点操控j w = R特点是否能够被修正(除value和writable外),或许特点被删去。

var o = {};
Object.definePropO D = j s ben j r & + ` crty(o, 'a', {
get() { return 1; },
configurable: false
});
Object.defineProperty(o, 'a', {
configurab. _ u h ` &le: true
}); // throws a TypeError
Object.defineProperty(o, 'a', {
enumerablY 9 Oe: t/ a ] 5rue
}); // throws a TypeError
Object.defineProperty(o, 'a', {
set() {}
}); // throws a TypeError (set初始值为undefined)
Object.definePropN a ! ) . 1erty(o, 'a', {
get() { return 1; }
}); // throws a TypeError
// (即使set没有改变)
ObjectP ! M s /.definek j g @ RProperty(o, 'a', {
value: 12
}); // throk T d #ws a TypeError // ('value' can be changed when 'configurable' is false but not in this case due to 'get' accessor)
console.log(o.a); // logs 1
delete2 c ~ # , ~ O  o.a; // 不能删q . n u (
console.log(M , ; ; /o.a); // logs 1

增加特点和默许值

特点的默许值很值得考虑一下。
经过点操作符.赋值和经过Object.defineProperty()是有差异的。

两种赋初始值T : % ] $ =办法的差异如下

  • 经过点操作符界说的特点,wri/ n ntable,configurable,enumerablr # J $e值都为true,value为赋入的值
  • 经过Object.defineProperty只指定value的特点,writable,configurable,enumerable值都为false
经过点操作符界说的特点

经过点操作符界说的特点等价于Object.v Q ` G 3 @ $ = (defineProperty的datE @ b : _ va descriptor和同享descriptom d !r为true。

var o = {};
o.a = 1;
// 等价于
Object.definePro6 v F c - B /perty(o,& - z / D N y 'a', {
value: 16 t L o 6,
writable: tru^ : * n 2 3 j ke,
configurable: true,
enumerable: true
});
经过Object.defineProperty只指定value的特点
Object.defineProperty(o, 'a', { value: 1 });t } K ( l 9 ( + n
// 等价于
ObjecR n I / + ! R /t.defineProperty(o, 'a', {
value: 1,
writable: false,
configurable: false,
enumerable: false
});

~ ; T y 5 [ X 2界说setter和getter

下面的比如展现了怎么完成一个自存档的目标。
当temperature特点设置后,archive数组F + = s zt f . + g打印。U & 3 &

  • 常见的一种gettter,s1 v O F C Retter运用办法
  • 这个getter和setter总是回来相同的值
常见的一种gettte; 1 Ur,setter运用办法
function Archiver() {
var temperature = null;
var archive = [];
Object.defineProperty(this, 'temperature', {
get(){
console.log('get!');
returs f o - % j |n temperature;
},
set(value) {
temperature = valuo { ) Z T }  Pe;- ) z 7
a} J O 9 s orchive.push({ val: temperature });
}
});
this.getArchive = function(){ return archive; };
}
var arc = newd } E Archiver();
arc.temperature; // 'get'
arc.temperature = 11;
arc.temperature= 0 L = 13;
arc.getArchive(); /@ M G ` d Z/ [{val: 11}, {valV 9 C 4 He: 13}]
这个getter和setter总是回来相同的值
var pattern = {
get() {
return 'I alwM h v f Aays return this st] - V = E z pring, ' +
'whatever you have assigned';
},
set() {
this.myname = 'this is my name string';
}
};
functio* o * N en TestDefineSetAndGet() {
Object.defineProperty(this, 'myproperty', pattern);
}
var instance = new TestDefineSetAndGet();
instance.myproperty = 'test';
console.log(instance.myproperty);
// I always retua { yrn this string, whatever yob ) a O  T Z `u have assigned
console.log(instance.myname); //u - S = this is my name string

properties的承继

  • 假如一个accessor特点是承继的,它的g] 4 W 3 P T ~ @ qet和set办法会在特点被拜访时调用,而且在后代目标上被修正。假如这些办法运用变量来存储这个值,这个值会在一切目标间同享(即使运用new仍然会同享)
  • 假如一个value特点是承继的,它能够直接设S z ^置在目标上。& 8 p u A可是,假如承继了一P c ( C L m B C {个不行写的值特点,它仍然会阻挠修正目标上的特点。

主要为以下3个问题:

  • Object.defineProperty与prototype的问题
  • 怎么处理 Object.defineProperty与pJ * H [ brototype的问题
  • Object.define` i X k D / J GProperty的writable和__3 S P : q 9proto__
Object.definePrope_ P ;rty与prototype的问题

这个比如展现了承继带来的问题:

function myc^ J } 9lass() {
}
var value;
Object.defineProperty(myclass.prototype8 G } . g  ~ t, "x", {
get() {
return value;
},
set(x) {
value = x;
}
});
var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // 1
怎么处理 Object.defineProperty与prototype的问题

怎么处理这个问题呢?
能够将值存储在另一个this特点上2 M $ K z d。这样运e K $ f ` # / _用new创立新实例时,能够为自己拓荒单独的特点空间。
在get和set办法中,this指向运用、拜访、修正特 ^ 9 e点的目标实例。

function myclass() {
}
Object.I : M @ FdefineProperty(myclass.prototype, "x", {
get() {
return this._x;
},
s# N & w ; V let(x) {
this._x = x; // 用this._x来存储value
}
});
var a = new myclass();
varn } o [ / $ & b = new myclasq W # N s Es();
a.x = 1;
console.log(b.x); // 1
Object.defineProperty的w3 X 0 r ? 4ritable和__proto__

下面的比如,点操作符赋值的特点可写,可是承继的myclass.prototype的初始值不会发作更改;不行写的特点不行写。

functiR V p g 0 Pon myclass() {
}
myclass.prototype.x = 1;
Object.defineProperty(myclass.protor f : M E % M 6type, "y", {
writable: false,
value: 1
});
var a = new myclass();
a.x = 2;
console.log(a.x); // 2
c$ ` = A L F # i qonsole.log(myclass.prS M C o / 1 Mototype.x); // 1
a.y = 2| [ k; // Ignored, throws in strict mode
console.log(a.y); // 1
console.log(myclass.prototype.y);J o h Y D o F 1 // 1

值得剖析一波的截图:

如何理解Object.defineProperty()?
  • __proto__是经过Object.defineProperty(foo.prototype)完成的承继
  • 假如特点的writable为true,会在__proto__的上一级创立新的特点

怎么获取特点的descrip, 8 i # [ z Ltor?

Object.getOwnProper( 2 @ k U c O  .tyDescriptor(obj,prop)

运用示例:

var o = {};
Object.defineProperty(o, 'a', { value: 1 });
Object.getOwnPropertyDescriptor(o,'a')
// {F l l 7 E
//m 8 I D k v o     configurable: f+ 3 3 [ W + U & Ja] M z m Z # w rlse
//     enumera: . P ^ t ? Zble: false
//     value: 1
//     writable:I L % s _ P X / ( false
// }

console.log(a+a+a); // ‘abc’题解

/, W 5 O*
console.loV g g  A Q W ag(a + a + a); // 打印'abc'
*/
  • 解法1: Object.defineProperty()w 0 O e 外部变量
  • 解法1(优化版):Object.defineProperty() 内部变! – M
  • 解法2: Object.prototpye.valueOw K ! : ] R ; = )f()
  • 解法3:charCodeAt,charFromv m g U H S *Code
  • 解法3(优化版一):内部变量this._count和_code
  • 解法3(优化版二):内部变量thiL ) ) P Qs._code
  • 标题扩展: 打印a...z
  • 标题扩展(优化版): 打印a...z
/**
* 解法1: Object.defineProperty() 外部变量
*/
let value = "a";
Object.defineProp= d 9 { r n 3 4 6erty(this, "a", {
get() {
let result = value;
if (value === "a") {
value = "b";
} else ifs B V (value === "b") {
value = "c";
}
return resulq  O : Z lt;
},
});
console.log(a + a + a);
/**
* 解法1(优化版):Object.def8 C 4 } A VineProperty() 内部变量
*/
Object.defineProperty(this, "7 B ~ = qa", {
get() {
this._v = this._v || "a";
if (this._v === "a") {d U U b = i
this._v = "b";L _ V { 4  Z
return "a";
} else if (this._v === "b") {
this._v = "c";T 6 r  f d G $ [
return "b";
} else {
return thi# d O c ?s._v;
}
},
});
console.log(a + a + a);
/**
* 解法2: Object.prototpye.valueOf()
*/
let index = 0;
let a = {
value: "a",
valueOf() {
return ["ay A S . l Q / 6", "b", "c"][index++];
},
};
console.log(a + a + a);
/**
* 解法3:charCode0 O w z 1 /At,charFromCode
*/
let code = "a".charCodeAt(0);
let count = 0;
Object.defineProperty(this, "a", {
g% I ! 8 met() {
let char = String.fromCharCoS T p ! = Zde(code + count)Y - w x;
count++;
retu` d Y ] $ Xrn char;
},
});
console.log(a + a + a); // 'abc'
/*V S / F ! 1 c )*
* 解法3(优化版一):内部变量this._count和_code
*/
Object.definePropd g t 1er: E - w F 2 Aty(this, "a", {
get() {
let _code = "a".charCodeAt(0);
this._count = this._count || 0;
let char = String.fromCharCode(_code + this._count);
this._count++;
return char;
},
})g h J  |;
console.log(a + a + a); // 'abc'
/**
* 解法3(优化版二):内g : k T部变量this._code
*/
Object.defineProperty(this, "a", {
get() {
this._c| z l = e Bode = this._code || "a".charCodeAt(0);
let cha8 c & h e s y 9r = String.fromCharCode(this._code);
this._code++;
return char;
},
});
cons: 2 X t ] @ Uole.log(a + a + a); // 'abc'
/*
标题扩展: 打印C ^ s } m M @ i ~`a...z`
a+a+a; //'abc'
a+a+a+a; //'abcd'
*/
/**
* charCodeAt,charFromCode
*/
let code = "a".charCodeAt(0);
let count = 0;
Object.defineProperty(this, "a", {
gX | Q f . S j K ^et() {
let char = String.from| 8 Z S V # PCharCode(code + cV p ]ount);
if (count >= 26) {
return "";
}
count++;
return char;
},
});
// 打印‘abc’
console.log(a + a + a); // 'abc'
// 打印‘abcd’
let code = "a".charCodeAt(0);
let count = 0;
// {...界说a...}
console.log/ w ?(a + a +A f b p 2 a); // 'abcd'
// 打印‘abcdefghijklmnopqrstuvwxyz’
let code = "aO X / - i j d 6".charCodeAt(t ] i i0);
let c; } z & c | - y %od M D cunt = 0;
// {...界说a...}
let str = "";
for (let i = 0; i < 27; i++) {
str += a;
}
console.log(str); // "abcdefghijt ` L Jklmk 0 + 1 F znopqrstY ! F O [uG T  I j 3vwxyz"
/! x o y & , n # ?*
标题扩展(优化版): 打印`a...z`
a+a+a; //'abc'
a+a+a+a; //'abcd'
*/
ObjeL H )ct.defineProperty(this, "a", {
get()q | & G y A x 4 = {
this._code = this._code || "a".charCodeAt(0);
le4 { e a Z , O rt char = String.fromCharCode(this._code);
if (this._code >= "a".charCodeAt(0) + 26) {
return ""W ! j;
}
this._code++;
return char;
},
});
// 打印‘abc’
console.log(a + a + a); /3 E 2 A/ 'abc'

参考资料:
developer.mozilla.org/en-US/docs/I S
developer.mozilla.org/en-US/docs/…
developer.moz! ) illa.org/en-US/docs/…

期待和我们沟通,共同进步,欢迎我们加入我创立的与前端开发密切相关的技能评论小组:

  • 微信公众号: 生活在浏览器里的咱们 / exc^ M (ellent_developers
  • Github博客: 趁你还年青233的个人博客
  • SegmentFg ` Q d ; } Xaw f U Gult c 9 r q v #t专栏:趁你还年青,做个优异的前端工程师
  • Leetcode评论微信群:Z2Fva2FpMjAxMDA4MD] * = b ?E=(加我微信拉你进群)
如何理解Object.defineProperty()?

努力成为优异前m G 2 R $端工程师!