依赖文件地址 :https://github.com/chanceLe/ES6-Basic-Syntax/tree/master/js
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8"> 5 <title>[es6]-08-对象的扩展</title> 6 <script src="./js/browser.js"></script> 7 <script type="text/babel"> 8 /* 9 * 对象的扩展 10 */ 11 12 /*属性的简洁表示法 13 * ES6允许直接写入变量和函数,作为对象的属性和方法。 14 * 这样书写更加简洁。 15 */ 16 17 var foo = 'bar'; 18 var baz = {foo}; 19 console.log(baz); 20 //等同于 21 22 var baz2 = {foo:foo}; 23 /* 24 * 上面代码表明,ES6允许在对象之中,只写属性名,不写属性值。 25 * 这时,属性值等于属性名所代表的变量。下面还有个例子: 26 */ 27 function f(x,y){ 28 return {x,y} 29 } 30 //等价于 31 function f1(x,y){ 32 return {x:x,y:y} 33 } 34 console.log(f(1,2)); 35 // 方法也可以简写 36 var o = { 37 method(){ 38 return "hello!"; 39 } 40 } 41 console.log(o.method()); //hello! 42 //下面是一个实际的例子 43 var birth = '2000/01/01'; 44 var Person = { 45 name:"zhangdan", 46 birth, 47 hello(){ 48 console.log("my name is ",this.name); 49 } 50 } 51 Person.hello(); 52 //这种写法用于函数的返回值,将会非常方便。 53 function getPoint(){ 54 var x=1; 55 var y=10; 56 return {x,y}; 57 } 58 console.log(getPoint()); 59 60 //CommonJS模块输出变量,就非常合适使用简洁写法。 61 var ms = {}; 62 function getItem(key){ 63 return key in ms? ms[key]:null; 64 } 65 function setItem(key,value){ 66 ms[key] = value; 67 } 68 function clear(){ 69 ms = {}; 70 } 71 //module.exports = {getItem,setItem,clear}; 72 //等价于 73 /*module.exports = { 74 getItem:getItem, 75 setItem:setItem, 76 clear:clear 77 } 78 */ 79 //注意简洁写法的属性名总是字符串,这会导致一些看上去比较奇怪的结果。 80 var obj = { 81 class(){ 82 console.log("class"); 83 } 84 } 85 //上面代码中,class是字符串,所以不会因为它属于关键字,而导致语法解析报错。 86 //如果某个方法的值是一个Generator函数,前面需要加上星号。 87 /* 88 var obj = { 89 * m(){ 90 yield "hello world!"; 91 } 92 } 93 console.log(obj.m(),"haha"); 94 */ 95 //上面的代码中,报错??? 96 97 /* 98 * 属性名表达式 99 * js语言定义对象的属性,有两种方法: 100 * obj.foo = true; 101 * obj["a"+"bc"] = 123; 102 * 方法一直接用标识符作为属性名,方法二是用表达式作为属性名,这时要将表达式放在[]内。 103 * 但是,如果使用字面量方式定义对象(使用大括号),在es5中只能使用方法一(标识符)定义属性。 104 * 105 * es6允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在[]内。 106 */ 107 let propKey = "foo"; 108 let obj2 = { 109 [propKey]:true, 110 ['a'+'bc']:123 111 } 112 console.log(obj2); 113 //表达式还可用于定义方法名。 114 let obj3={ 115 ["h"+"ello"](){ 116 return "hi"; 117 } 118 } 119 console.log(obj3.hello()); 120 //属性名表达式与简洁表示法,不能同时使用会报错。 121 122 /* 123 * 方法的name属性 124 * 函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。 125 */ 126 var person = { 127 sayName(){ 128 console.log(this.name); 129 }, 130 firstName(){ 131 return "nicholas" 132 } 133 } 134 console.log(person.sayName.name); 135 console.log(person.firstName.name); 136 //上面代码中,方法的name属性返回函数名。 137 //如果使用了取值函数,则会在方法前加上get, 138 // 存值函数,会加上set。(但是,实际上,我并没能分出来这个区别。) 139 140 //两种特殊情况:bind方法创造的函数,name属性返回bound+函数名, 141 // Function构造函数创造的函数,name属性返回anonymous。 142 console.log((new Function()).name); //anonymous。 143 var doSomething = function(){} 144 console.log(doSomething.bind().name); //bound doSomething 145 146 //如果对象的方法是一个Symbol值,namname属性返回这个Symbol值的描述。 147 const key1 = Symbol("description"); 148 const key2 = Symbol(); 149 let obj4 = { 150 [key1](){}, 151 [key2](){} 152 } 153 console.log(obj4[key1].name); //'' 154 console.log(obj4[key2].name); //'' 155 //事实上,打出来是完全一样的,??? 156 157 /* 158 * Object.is() 159 * es5可以通过下面的代码,部署Object.is() 160 */ 161 Object.defineProperty(Object,"is",{ 162 value:function(x,y){ 163 if(x===y){ 164 //针对+0不等于-0的情况 165 return x!==0 || 1/x === 1/y; 166 } 167 //针对NaN的情况 168 return x !==x && y!==y; 169 }, 170 configurable:true, 171 enumerable:false, 172 writable:true 173 }) 174 175 /* 176 * Object.assign() 177 * 该方法用于对象的合并,将源对象的所有可枚举属性,复制到目标 178 * 对象。 179 * 第一个参数是目标对象,剩下的参数都是源对象。 180 * 如果有同名属性,后面的属性会覆盖前面的属性。 181 * 如果只有一个参数,就返回这个参数。如果这个参数不是对象,会先转换成对象。 182 * 由于undefined和null无法转成对象,所以他们作为参数会报错。 183 * 184 * 如果非对象出现在源对象的位置(即非首参数),首先这些参数都会转成对象,如果 185 * 无法转成对象,就会跳过。这意味着如果undefined和null不在首参数,就不会报错。 186 * 187 * 其他类型的值,不在首参数也不会报错,但只有字符串会合入目标对象,数值和布尔值都会被 188 * 忽略,因为只有字符串的包装对象会产生可枚举的实义属性。数值和布尔值转成的包装对象 189 * 他们的原始值都存放在[[PrimitiveValue]]:"false"属性中。不会被Object.aasign()拷贝。 190 * 191 * Object.assgn()不拷贝继承属性,也不拷贝不可枚举属性(enumerable:false) 192 */ 193 var target = {a:1}; 194 var source1 = {b:2}; 195 var source2 = {c:3}; 196 var source3 = {b:5}; 197 Object.assign(target,source1,source2,source3); 198 console.log(target); 199 console.log(Object.assign(target) === target); //true 200 201 console.log(Object.assign({a:"b"},{[Symbol("c")]:"d"})); 202 //结果显示属性名为Symbol值的属性,也会被Object.assign拷贝。 203 204 //注意点,Object.assign()方法实行的是浅拷贝,也就是说,如果属性值是一个对象,那么目标对象拷贝到的 205 //是这个对象的引用。 206 //对于嵌套的对象,一旦遇到同名属性,Object.assign()的处理方法是替换,而不是添加。 207 208 //Object.assign()可以用来处理数组,但是会把数组当做对象,并且一定会覆盖索引相同的值. 209 console.log(Object.assign([1,2,3],[4,5])); //[4,5,3] 210 211 /* 212 * 用途: 1.为对象添加属性 2.为对象添加方法 3.克隆对象 213 * 4.将多个对象合并到某个对象。 214 * 5.为属性指定默认值,这个跟jquery插件开发的那个东西一样。 215 */ 216 function clone(origin){ 217 return Object.assign({},origin); 218 } 219 //上面代码将原始对象拷贝到一个空对象,就得到了原始对象的克隆。不过上面这种不能克隆继承的值,如果 220 //要保持继承链,可以用下面的代码: 221 222 function clone2(origin){ 223 let originProto = Object.getprototypeOf(origin); 224 return Object.assign(Object.create(originProto),origin); 225 } 226 227 228 /* 229 * 属性的可枚举性。 230 * 对象的每个属性都有一个描述对象,用来控制该属性的行为。 231 * Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。 232 */ 233 let obj5 = {foo:123}; 234 console.log(Object.getOwnPropertyDescriptor(obj5,'foo')); 235 /* 236 * 描述对象的enumerable就是可枚举性,es5有三个操作会忽略enumerable:false的属性: 237 * for...in...循环:只遍历对象自身的和继承的可枚举属性。 238 * Object.keys() 返回对象自身的所有可枚举属性的键名 239 * JSON.stringify():只串行化对象自身的可枚举属性 240 * 241 * ES6新增的Object.assign会忽略enumerable:false的属性。 242 * 这四个操作中,只有for...in...会返回继承的属性。 243 * 另外,ES6规定,所有class的原型的方法都是不可枚举的。 244 */ 245 let k = Object.getOwnPropertyDescriptor(class {foo(){}}.prototype,"foo").enumerable; 246 console.log(k); //false 247 248 //总的来说,操作中引入继承的属性会让问题复杂化,大多数时候,我们只关心对象自身的属性。 249 //所以,尽量不要用for...in...循环,而用Object.keys()代替。 250 251 /* 252 * 属性的遍历 253 * ES6一共有5种方法遍历对象的属性。 254 * 1.for...in :循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。 255 * 2.Object.keys(obj) : 返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)。 256 * 3.Object.getOwnpropertyNames(obj) :返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)。 257 * 4.Object.getOwnPropertySymbols(obj) : 返回一个数组,包含对象自身的所有Symbol属性。 258 * 5.Reflect.ownKeys(obj) :返回一个数组,包含对象自身的所有属性,不管属性名是Symbol或字符串,也不管是否可枚举。 259 * 260 * 以上5种方法遍历对象的属性,都遵守同样的属性遍历次序规则: 261 * 首先遍历所有属性名为数值的属性,按照数字排序。 262 * 其次遍历所有属性为字符串的属性,按照生成时间排序。 263 * 最后遍历所有属性名为Symbol值的属性,按照生成时间排序。 264 * 下面有个例子: 265 */ 266 let m = Reflect.ownKeys({[Symbol()]:0,b:0,10:0,2:0,a:0}); 267 console.log(m); //结果体现了遍历的次序规则。 268 269 //_proto__属性,Object.setPrototypeOf()和Object.getprototypeOf() 270 /* 271 * __proto__属性,用来设置或读取当前对象的prototype对象。目前所有浏览器(包括IE11)都部署了这个属性。 272 */ 273 274 /* 275 //es6的写法 276 var obj6 = { 277 method(){} 278 } 279 obj6.__proto__ = someOtherObj; 280 281 //es5的写法 282 var obj7 = Object.create(someOtherObj); 283 obj.method = function(){} 284 */ 285 /* 286 * 该属性没有写入es6的正文,而是写入了附录,原因是双下划线说明它本质上是一个内部属性。而不是一个正式的对外的API, 287 * 只是由于浏览器的广泛支持,才被加入ES6。标准明确规定,只有浏览器必须部署这个属性,其他环境不一定要部署,而且新的 288 * 代码最好是认为这个属性不存在的。因此最好不要使用这个属性,而是用下面的Object.setprototypeOf()Object.getprototypeOf() 289 * 和Object.create()代替。 290 * 291 * 在实现上,__proto__调用的是Object.prototype.__proto__。 292 * 如果一个对象部署了__proto__属性,该属性的值就是对象的原型。 293 */ 294 295 /* 296 * Object.setPrototypeOf() 297 * 作用与__proto__相同,是es6官方推荐的设置对象原型的方法。 298 * 格式: Object.setPrototypeOf(object,prototype); 299 * 下面是一个例子: 300 */ 301 let proto = {}; 302 let obj8 = {x:10}; 303 Object.setPrototypeOf(obj8,proto); 304 proto.y = 20; proto.z = 25; 305 console.log(obj8.x,obj8.y,obj8.z) ; //10,20,25 306 307 /* 308 * Object.getPrototypeOf()与上一个配套,用于读取一个对象的prototype对象。 309 */ 310 console.log(Object.getPrototypeOf(obj8)); 311 </script> 312 </head> 313 <body> 314 </body> 315 </html>