Introduction to EcmaScript5
First of all, we must understand that ECMAScript is a magic horse. We know that JavaScript or LiveScript was first created by Netscape. Later, Microsoft also followed up and created Jscript. ScriptEase also has its own CENvi, so there are three versions. Browser scripts all do their own thing, and everyone understands this confusion, so the issue of standardization has been put on the agenda. In 1997, a proposal based on JavaScript 1.1 was submitted to the European Computer Manufacturers Association (European Computer Manufacturers Association). In the end, everyone sang and danced to come up with ECMA-262, a new scripting language standard called ECMAScript. . The next year, ISO/IEC (International Organization for Standardization and International Electrotechnical Commission) also adopted ECMAScript as a standard. Since then, the world has been peaceful. Major browser manufacturers use ECMAScript as the basis for their respective implementations of JavaScript. Of course, it is only a basis and not fully followed. Otherwise, We also won’t have as many browser compatibility issues.
What is ECMAScript5? As the name suggests, it is the fifth version of this strange thing like iPhone5. The one we commonly use now is ECMAScript3. Compared with the first two versions, this version is a real programming language rather than a toy, and it has become very popular.
Text:
My previous understanding of get/set was wrong. I thought get set was an object property method. I also had a lot of questions after reading other people's blogs. Today I did a lot of testing on the system and finally figured it out. (I have passed the test by reading books and writing demos. If there are any mistakes, you are welcome to criticize and correct me)
The get/set accessors are not properties of the object, but characteristics of the property. Everyone must distinguish clearly. Properties are only used internally, so they cannot be accessed directly from JavaScript. In order to indicate the attribute, the internal value is enclosed in two sets of square brackets, such as [[Value]].
1. Let’s briefly introduce these characteristics of attributes (here is a simple endorsement)
(1) Data attribute - contains the location of a data value. Values can be read and written to this location.
Data attributes have four characteristics that describe their behavior:
[[Configurable]]: Whether it is configurable
[[Enumerable]]: Whether it is enumerable
[[Writable]]: Whether it is readable
[[Value]]: Attribute value
(2) Accessor attribute attribute - does not contain data value, contains a getter and setter function (these two functions are not necessary)
Accessor properties also have four characteristics that describe their behavior:
[[Configurable]]: Whether it is configurable
[[Enumerable]]: Whether it is enumerable
[[Get]]: Function called when reading properties, the default is undefined
[[Set]]: Function called when writing attributes, the default is undefined
2. Here we focus on [[Get]]/[[Set]], which is what we call the get/set accessor
First let’s talk about the behavioral characteristics of the get/set accessor mentioned in the book: the get/set accessor does not need to be defined, and it can read and write attribute values without defining it. You can also define only one. If only get is defined, the described attribute can only be read, not writable. If only set is defined, the described attribute can only be written, not readable.
(1) Our original get set method is like this:
function Foo(val){ var value=val; this.getValue=function(){ return value; }; this.setValue=function (val){ value=val; }; } var obj=new Foo("hello"); alert(obj.getValue());//"hello" obj.setValue("hi"); alert(obj.getValue());//"hi"
The above code is just the get set method implemented using the closure scope. Note that the method is the attribute method of the instance object, not the attribute characteristic. If it is not defined, the value value
will not be accessible.
function Foo(val){ var value=val; /* this.getValue=function(){ return value; }; this.setValue=function (val){ value=val; }; */ } var obj=new Foo("hello"); alert( obj.value);//undefined
The following examples are also property methods of objects, not property characteristics.
var obj={ name:"john", get:function (){ return this.age; }//只定义了get ,没有定义set,但是仍然可以读,写,name属性,即使这里是age //这里这样定义的方法不会影响属性的get,set 特性。只是普通的对象属性 }; alert(obj.name);//john 可读 obj.name="jack";//可写 alert(obj.name);//jack
(2) Get/set accessor as a characteristic of the accessor attribute.
Again, they are not the properties of the object, they determine whether and how the properties can be read and written. It’s ok if you don’t set it, it will be the same as usual reading and writing (the attribute can be read
Write, read and write access are the value of the attribute itself)
To change the get/set properties of an attribute, there are two ways:
a. Just use Object.defineProperty()
var object={ _name:"Daisy" }; Object.defineProperty(object,"name",{//这里的方法名name,就表示定义了一个name属性(因此才能通过object.name访问),只定义了getter访问器,没有定义[[value]]值 get:function (){//只定义了get 特性,因此只能读不能写 return this._name; } }); alert(object.name);//"Daisy" object.name="jack";//只定义了getter访问器,因此写入失效 alert(object.name);//"Daisy"
Note that the property names in Object.defineProperty(object, pro, {}) must correspond to the properties accessed by object.pro
b. Just use the get set keyword:
var object={ _name:"Daisy", get name(){//这里的方法名name ,就表示定义了一个name属性(因此才能通过object.name访问),只定义了getter访问器,没有定义[[value]]值 return this._name; }//get,set方法只是属性的特性 ,不是对象方法,决定属性能否、怎么读写 }; alert(object.name);// Daisy这里去掉下划线 方法就是Daisy ;加上就是undefined object.name="jack";//只定义了getter访问器,因此只能读不能写 alert(object.name);//Daisy
以上两种方法等效。注意的是以上两种方法object对象当中都将有有两个属性:_name(有初始值) name(无初始值),通过浏览器控制台可以看到
那么这个name属性实在什么时候定义的呢?我们知道Object.defineProperty(object,pro,{})可以给对象定义一个新属性pro,既然get pro(){}/set pro(){}和Object.defineProperty(object,pro,{})等效,则也会定义一个新属性pro .这就是为什么object里面有两个属性的原因。
(3)在此篇文章中网络之美 JavaScript中Get和Set访问器的实现代码关于标准标准的Get和Set访问器的实现:引发的思考
我自己也写了一个一样的例子
function Foo(val){ this.value=val;//定义了value属性 并没有定义_value } Foo.prototype={ set value(val){//注意方法名和属性名相同,在prototype里定义了value属性 this._value=val; }, get value(){//方法名和属性名相同,在prototype里面定义了value属性和它的get 特性 return this._value; } }; //访问器返回和设置的都是_name,这里并没有定义_name属性为什么也可以读可以写???? var obj=new Foo("hello"); alert(obj.value);//"hello" obj.value="yehoo"; alert(obj.value);//"yehoo"
为了解决以上这个疑问,做了很多测试,我们一一来看:
先看这个例子,在prototype里面只定义get 特性,在obj.value读value属性时,在实例里面寻找没有,然后在原型里面找到,调用的是原型的get方法,只能读不能写
function Foo(val){ this._value=val;//这里 的属性是带下划线的,初始化实例对象的_value属性,_value属性可读可写 } Foo.prototype={ // set value(val){//注意方法名和属性名相同,在prototype里定义了value属性 // this._value=val; // }, get value(){//方法名和属性名相同,在prototype里面定义了value属性和它的get 特性 return this._value; } }; var obj=new Foo("hello"); alert(obj.value);//hello 访问的是prototype里面的value 属性 obj.value="yehoo";//只定义了name 属性的get 特性,因此只能读不能写,写入失效 alert(obj.value);//hello
如果构造函数里面this._value 去掉下划线,在prototype里面定义的value属性,定义了get 特性。依然可以控制value属性的读写 。也就是说obj.value访问属性时,会调用get方法,先在对象本身寻找,如果没有,再到prototype寻找,如果都没有才算没有定义,默认的既可读又可写
function Foo(val){ this.value=val;//在原型里面只定义了value的get特性,因此这里写入失效 } Foo.prototype={ // set value(val){//注意方法名和属性名相同,在prototype里定义了value属性的set特性 // this._value=val; //}, //value:"hah",//即使手动写入value值,由于get方法返回的是this._value,因此也不能正确读取value:"hah" //只要声明了get pro (){}和set pro (){}属性就都能读能写,但是如果函数定义错误,依然不能按要求访问到正确的属性值 get value(){//方法名和属性名相同,在prototype里面定义了value属性和它的get 特性 return this._value; } }; var obj=new Foo("hello");//"hello"没有写入成功 alert(obj.value);//undefined obj.value="yehoo";//只定义了get 特性,因此只能读不能写,写入失效 alert(obj.value);//undefined
为了证明上面例子是可读不可写的:手动写入_value:"hah",就可以读取value 但不能写入。
function Foo(val){ this.value=val;//在原型里面只定义了value的get特性,因此这里写入失效 } Foo.prototype={ // set value(val){//注意方法名和属性名相同,在prototype里定义了value属性的set特性 // this._value=val; //}, _value:"hah",//即使手动写入value值,由于get方法返回的是this._value,因此也不能正确读取value:"hah" //只要声明了get pro (){}和set pro (){}属性就都能读能写,但是如果函数定义错误,依然不能按要求访问到正确的属性值 get value(){//方法名和属性名相同,在prototype里面定义了value属性和它的get 特性 return this._value; } }; var obj=new Foo("hello");//"hello"没有写入成功 alert(obj.value);//"hah" obj.value="yehoo";//只定义了get 特性,因此只能读不能写,写入失效 alert(obj.value);//"hah"
如果手动写入的是value:"hah",那么可以争取读取value的值吗?由于get方法返回的this._value并没有定义,obj.value读取value值调用get value(){}方法失效,但是value仍然不能写入。
function Foo(val){ this.value=val;//在原型里面只定义了value的get特性,因此这里写入失效 } Foo.prototype={ // set value(val){//注意方法名和属性名相同,在prototype里定义了value属性的set特性 // this._value=val; //}, value:"hah",//即使手动写入value值,由于get方法返回的是this._value,因此也不能正确读取value:"hah" //只要声明了get pro (){}和set pro (){}属性就都能读能写,但是如果函数定义错误,依然不能按要求访问到正确的属性值 get value(){//方法名和属性名相同,在prototype里面定义了value属性和它的get 特性 return this._value; } }; var obj=new Foo("hello");//"hello"没有写入成功 alert(obj.value);//undefined 读取失效 因为只要obj.value就会调用get ,而get返回的是this._value,没有这个值,因此undefined obj.value="yehoo";//只定义了get 特性,因此只能读不能写,写入失效 alert(obj.value);//undefined
再看这个例子,get set 都定义了,但是返回没有定义的this._value。可以发现value既可读又可写。去掉原型里面的get set方法,依然可读可写
function Foo(val){ this.value=val; } Foo.prototype={ set value(val){ this._value=val; }, get value(){ return this._value; } }; var obj=new Foo("hello"); alert(obj.value);//hello obj.value="yehoo"; alert(obj.value);//yehoo function Foo(val){ this.value=val; } //和平时的操作是一样的了,就是回到了不定义get /set访问器特性的默认状态 var obj=new Foo("hello"); alert(obj.value);//hello obj.value="yehoo"; alert(obj.value);//yehoo
总结
只声明了get pro(){}属性 可读不可写;
只声明 set pro(){}属性可写不可读。
如果都不声明,属性可读可写;
如果都声明就按照,get set 定义的方法,读写;
如果都声明了,但是定义的读写方法不能正确读写,get/set失效。变成默认的可读可写
在prototype里面定义的value属性,定义了get 特性。依然可以控制value属性的读写 。也就是说obj.value访问属性时,会调用get方法,先在对象本身寻找,如果没有,再到prototype寻找,如果都没有才算没有定义,默认的既可读又可写。
补充:
不管是用get pro(){}/set pro (){} 还是用Object.defineProperty(object,pro,{ get:function (){ return this._name; } });
pro不能和 return this. 后面的属性一样,不然会报下面的错误:(具体我也不知道为什么,好像是自身调用引起的栈溢出)
经大神指正,明白为什么这里报错:在get value(){}方法里返回 this.value,就会又去调用value的get 方法,因此陷入死循环,造成方法栈溢出。