ES6转ES5,javascript---第三季之浅谈对象
2017-03-16 18:12
134 查看
1,属性简洁表示法
var n1 = 'andy'; var n = {n1}; console.log(n);//{n1: "andy"} //等同于 var baz = {n1:n1}; console.log(baz);//{n1: "andy"} //ES6 允许在对象之中,直接写变量。这时,属性名为变量名, 属性值为变量的值 function f1(x, y) { return {x, y}; } //等同于 function f(x,y){ return {x:x,y:y}; } console.log(f(1,2));//{x: 1, y: 2} //除了属性简写,方法也可以简写 var n1 = { init:function(){ return 'andy'; } } //等同于 var n2 = { init(){ return 'andy'; } }
var age = '27'; var person = { name:'andy', age,//相当于age:age hello(){ console.log(this.name); } } person.hello();//andy //如果某个方法的值是一个Generator函数,前面需要加上星号 var obj = { * m(){ yield 'hello world'; } };
2,属性名表达式
//JavaScript语言定义对象的属性,有两种方法。 var obj ={}; obj.name = 'andy'; obj['a'+'bc'] = '1'; console.log(obj); //方法一是直接用标识符作为属性名,方法二是用表达式作为属性名 //但是,如果使用字面量方式定义对象(使用大括号),在 ES5 中只能使用方法一(标识符)定义属性 var obj1 = { 'name':'andy', 'age':27 }; //ES6 允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内。 let scholl = '香港城市大学'; let obj2 = { [scholl]:true, ['a'+'bc']:123 }; console.log(obj2);//{香港城市大学: true, abc: 123} //表达式还可以用于定义方法名 let obj3 = { ['f'+'oo'](){ return '1'; } }; console.log(obj3.foo());//1 //注意,属性名表达式与简洁表示法,不能同时使用,会报错。 //报错 var foo = 'bar'; var bar = 'abc'; var baz = {[foo]}; //正确 var foo = 'bar'; var baz = { [foo]: 'abc'};
3,方法的name属性
函数的name属性,返回函数名。对象方法也是函数,因此也有
name属性
let person = { sayName(){ console.log('hello!'); } }; console.log(person.sayName.name);//sayName //如果对象的方法使用了取值函数(getter)和存值函数(setter), //则name属性不是在该方法上面,而是该方法的属性的描述对象的get和set属性上面,返回值是方法名前加上get和set let obj = { get foo(){}, set foo(x){} }; //console.log(obj.foo.name);//Uncaught TypeError: Cannot read property 'name' of undefined let descriptor = Object.getOwnPropertyDescriptor(obj,'foo'); console.log(descriptor.get.name);//get foo console.log(descriptor.set.name);//set foo有两种特殊情况:
bind方法创造的函数,
name属性返回
bound加上原函数的名字;
Function构造函数创造的函数,
name属性返回
anonymous。
console.log((new Function()).name);//anonymous var dos = function(){ // }; console.log(dos.bind().name);//bound dos
4,Object.is()
ES5比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(
===)。它们都有缺点,前者会自动转换数据类型,后者的
NaN不等于自身,以及
+0等于
-0。JavaScript缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。ES6提出“Same-valueequality”(同值相等)算法,用来解决这个问题。
Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致
console.log(Object.is('foo','foo'));//true console.log(Object.is({},{}));//false不同之处只有两个:一是
+0不等于
-0,二是
NaN等19db0于自身
console.log(+0 === -0);//true console.log(NaN === NaN);//false console.log(Object.is(+0,-0));//false console.log(Object.is(NaN,NaN));//ture
5,Object.assgin()
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)
//如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。 var target = { a: 1, b: 1 }; var source1 = { b: 2, c: 2 }; var source2 = { c: 3 }; Object.assign(target, source1, source2); console.log(target); // {a:1, b:2, c:3}如果只有一个参数,
Object.assign会直接返回该参数
var obj = {a:1}; console.log(Object.assign(obj) === obj);//true如果该参数不是对象,则会先转成对象,然后返回。
console.log(typeof Object.assign(2));//object由于
undefined和
null无法转成对象,所以如果它们作为参数,就会报错。如果非对象参数出现在源对象的位置(即非首参数),那么处理规则有所不同。首先,这些参数都会转成对象,如果无法转成对象,就会跳过。这意味着,如果
undefined和
null不在首参数,就不会报错
let obj = {a:1}; console.log(Object.assign(obj,undefined) === obj);//true console.log(Object.assign(obj,null) === obj);//true其他类型的值(即数值、字符串和布尔值)不在首参数,也不会报错。但是,除了字符串会以数组形式,拷贝入目标对象,其他值都不会产生效果
var n1 = 'abc'; var n2 =1; var n4 = true; var obj = Object.assign({},n1,n2,n4); console.log(obj);//{0: "a", 1: "b", 2: "c"} //上面代码中,分别是字符串、布尔值和数值,结果只有字符串合入目标对象(以字符数组的形式), //数值和布尔值都会被忽略。这是因为只有字符串的包装对象,会产生可枚举属性 //Object.assign拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性(enumerable: false) var obj1 = Object.assign({b:'c'}, Object.defineProperty({},'invisible',{ enumerable:false, value:'hello' }) ); console.log(obj1);//{b: "c"} //属性名为Symbol值的属性,也会被Object.assign拷贝 console.log(Object.assign({a:'b'},{[Symbol('c')]:'d'}));//{a: "b", Symbol(c): "d"}注意:
Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用
var obj1 = {a:{b:1}}; var obj2 = Object.assign({},obj1); obj1.a.b = 2; console.log(obj2.a.b);//2 //上面代码中,源对象obj1的a属性的值是一个对象,Object.assign拷贝得到的是这个对象的引用。 //这个对象的任何变化,都会反映到目标对象上面 //对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换,而不是添加 var target = {a:{b:'c',d:'e'}}; var source = {a:{b:'andy'}}; console.log(Object.assign(target,source));//{ a: { b: 'andy' } } //上面代码中,target对象的a属性被source对象的a属性整个替换掉了,而不会得到{ a: { b: 'hello', d: 'e' } }的结果。 //这通常不是开发者想要的,需要特别小心常见用途:a,为对象添加属性
class Point{ constructor(x,y){ Object.assign(this,{x,y}); } } //上面方法通过Object.assign方法,将x属性和y属性添加到Point类的对象实例b,为对象添加方法
Object.assign(SomeClass.prototype,{ init(arg1,arg2){ }, create(){ } }); //等同于 SomeClass.prototype.init = function(arg1,arg2){ }; SomeClass.prototype.create = function(){ // } //上面代码使用了对象属性的简洁表示法,直接将两个函数放在大括号中,再使用assign方法添加到SomeClass.prototype之中c,克隆对象
function clone(origin){ return Object.assign({},origin); } //上面代码将原始对象拷贝到一个空对象,就得到了原始对象的克隆 //不过,采用这种方法克隆,只能克隆原始对象自身的值,不能克隆它继承的值。 //如果想要保持继承链,可以采用下面的代码 function clone(origin) { let originProto = Object.getPrototypeOf(origin); return Object.assign(Object.create(originProto), origin); }d,合并对象
e,为属性置顶默认值
f,属性的可枚举性
对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。
Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象[/code]
g,属性的遍历
ES6一共有5种方法可以遍历对象的属性(1)for...in
for...in循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。(2)Object.keys(obj)
Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)。(3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)。(4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有Symbol属性。(5)Reflect.ownKeys(obj)
Reflect.ownKeys返回一个数组,包含对象自身的所有属性,不管是属性名是Symbol或字符串,也不管是否可枚举。以上的5种方法遍历对象的属性,都遵守同样的属性遍历的次序规则。首先遍历所有属性名为数值的属性,按照数字排序。其次遍历所有属性名为字符串的属性,按照生成时间排序。最后遍历所有属性名为Symbol值的属性,按照生成时间排序。h,_proto_属性,Object.setPrototypeOf(),Object.getPrototypeOf()
__proto__属性(前后各两个下划线),用来读取或设置当前对象的
prototype对象。目前,所有浏览器(包括 IE11)都部署了这个属性
// es6 var obj = { method(){} }; ob._proto_ = someOthorObj; // es5 var obj1 = Object.create(someOthorObj); obj1.method = function(){} //该属性没有写入 ES6 的正文,而是写入了附录,原因是__proto__前后的双下划线,说明它本质上是一个内部属性, //而不是一个正式的对外的 API,只是由于浏览器广泛支持,才被加入了 ES6。 //标准明确规定,只有浏览器必须部署这个属性,其他运行环境不一定需要部署,而且新的代码最好认为这个属性是不存在的。 //因此,无论从语义的角度,还是从兼容性的角度,都不要使用这个属性, //而是使用下面的Object.setPrototypeOf()(写操作)、Object.getPrototypeOf()(读操作)、Object.create()(生成操作)代替。Object.setPrototypeOf()
Object.setPrototypeOf方法的作用与
__proto__相同,用来设置一个对象的
prototype对象,返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法。
//格式 //Object.setPrototypeOf(object,prototype); //用法 var o = Object.setPrototypeOf({},null); //等同于 function (obj,proto){ obj._proto_ = proto; return obj; }// 注意
let proto = {}; let obj = { x: 10 }; Object.setPrototypeOf(obj, proto); proto.y = 20; proto.z = 40; console.log(obj.x); // 10 console.log(obj.y); // 20 console.log(obj.z); // 40上面代码将
proto对象设为
obj对象的原型,所以从
obj对象可以读取
proto对象的属性。
如果第一个参数不是对象,会自动转为对象。但是由于返回的还是第一个参数,所以这个操作不会产生任何效果
console.log(Object.setPrototypeOf(1,{}) === 1);//trueconsole.log(Object.setPrototypeOf('foo',{}) === 'foo');//trueconsole.log(Object.setPrototypeOf(true,{}) === true);//true//由于undefined和null无法转为对象,所以如果第一个参数是undefined或null,就会报错Object.setPrototypeOf(undefined, {});//TypeError: Object.setPrototypeOf called on null or undefinedObject.setPrototypeOf(null, {});//TypeError: Object.setPrototypeOf called on null or undefinedObject.getPrototypeOf()
该方法与[code]Object.setPrototypeOf方法配套,用于读取一个对象的原型对象[/code]
Object.keys()
ES5 引入了[code]Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。[/code]
var obj = {foo:'bar',age:27};console.log(Object.keys(obj));//["foo", "age"]
ES2017 引入了跟[code]Object.keys配套的
Object.values和
Object.entries,作为遍历一个对象的补充手段,供
for...of循环使用。[/code]
let {keys,values,entries} = Object;let obj = {a:1,b:2,c:3};for(let key of keys(obj)){console.log(key);//a,b,c}for(let val of values(obj)){console.log(val);//1 2 3}for(let [key,value] of entries(obj)){console.log([key,value]);//['a', 1], ['b', 2], ['c', 3]}
Object.values()
Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。[/code]
var obj = {'name':'刘德华','age':50};console.log(Object.values(obj));//["刘德华", 50]//返回数组的成员顺序,与本章的《属性的遍历》部分介绍的排列规则一致。var obj1 = {100:'a',1:'b',2:'c'}console.log(Object.values(obj1));//["b", "c", "a"]//上面代码中,属性名为数值的属性,是按照数值大小,从小到大遍历的,因此返回的顺序是b、c、a。//Object.values只返回对象自身的可遍历属性。var obj2 = Object.create({},{p:{value:42}});console.log(Object.values(obj2));//[]//上面代码中,Object.create方法的第二个参数添加的对象属性(属性p),//如果不显式声明,默认是不可遍历的,因为p的属性描述对象的enumerable默认是false,Object.values不会返回这个属性。//只要把enumerable改成true,Object.values就会返回属性p的值var obj3 = Object.create({},{p:{value:42,enumerable:true}});console.log(Object.values(obj3));//[42]//Object.values会过滤属性名为 Symbol 值的属性。console.log(Object.values({ [Symbol()]: 123, foo: 'abc' }));//["abc"]//如果Object.values方法的参数是一个字符串,会返回各个字符组成的一个数组。console.log(Object.values('andy'));//["a", "n", "d", "y"]//上面代码中,字符串会先转成一个类似数组的对象。字符串的每个字符,就是该对象的一个属性。//因此,Object.values返回每个属性的键值,就是各个字符组成的一个数组//如果参数不是对象,Object.values会先将其转为对象。由于数值和布尔值的包装对象,都不会为实例添加非继承的属性。//所以,Object.values会返回空数组。console.log(Object.values(50));//[]console.log(Object.values(true));//[]Object.entries()
Object.entries方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。[/code]
var obj = {'name':'梁朝伟','age':27};console.log(Object.entries(obj));//[['name','梁朝伟'],['age',27]]除了返回值不一样,该方法的行为与
Object.values基本一致。
如果原对象的属性名是一个 Symbol 值,该属性会被忽略。
console.log(Object.entries({ [Symbol()]: 123, foo: 'abc' }));//[ [ 'foo', 'abc' ] ]//上面代码中,原对象有两个属性,Object.entries只输出属性名非 Symbol 值的属性。//将来可能会有Reflect.ownEntries()方法,返回对象自身的所有属性。[/code]Object.entries的基本用途是遍历对象的属性。
let obj = {'name':'andy','age':50};for(let [k,v] of Object.entries(obj)){console.log(`${JSON.stringify(k)}:${JSON.stringify(v)}`// "name":"andy",'age':50);}//Object.entries方法的另一个用处是,将对象转为真正的Map结构。var obj1 = {'name':'andy','age':50};var map = new Map(Object.entries(obj1));console.log(map);// {"name" => "andy", "age" => 50}6,对象的扩展符运算
《数组的扩展》一章中,已经介绍过扩展预算符([code]...)。[/code]
let [a,...b] = [1,2,3];console.log(a);//1console.log(b);//[2,3]ES2017 将这个运算符引入了对象。
提示:注意配置插件
transform-object-rest-spread
a,解构赋值
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };console.log(x);//1console.log(y);//2console.log(z);//{a:3,b:4}//上面代码中,变量z是解构赋值所在的对象。它获取等号右边的所有尚未读取的键(a和b),将它们连同值一起拷贝过来。//由于解构赋值要求等号右边是一个对象,所以如果等号右边是undefined或null,就会报错,因为它们无法转为对象。let {x,y,...z} = null;//运行错误//解构赋值必须是最后一个参数,否则会报错。let { ...x, y, z } = obj; // 句法错误let { x, ...y, ...z } = obj; // 句法错误
//注意,解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、//那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。let obj = {a:{b:1}};let {...x} = obj;obj.a.b = 2;console.log(x.a.b);//2//上面代码中,x是解构赋值所在的对象,拷贝了对象obj的a属性。a属性引用了一个对象,修改这个对象的值,会影响到解构赋值对它的引用//另外,解构赋值不会拷贝继承自原型对象的属性。let o1 = {a:1};let o2 = {b:2};o2.__proto__ = o1;let o3 = { ...o2};console.log(o3);//{b:2}//上面代码中,对象o3是o2的拷贝,但是只复制了o2自身的属性,没有复制它的原型对象o1的属性。b,扩展运算符
扩展运算符([code]...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。[/code]
let x ={a:1,b:2};let y = {...x};console.log(y);// {a:1,b:2}//这等同于使用Object.assign方法。let n1 = {...a};//等同于let n2 = Object.assign({},a);扩展运算符可以用于合并两个对象。
let ab = {...a,...b};//等同于let ab = Object.assign({},a,b);//如果扩展运算符的参数是null或undefined,这个两个值会被忽略,不会报错。let emptyObject = { ...null, ...undefined }; // 不报错7,Object.getOwnPropertyDescriptors()
ES5有一个[code]Object.getOwnPropertyDescriptor方法,返回某个对象属性的描述对象(descriptor)[/code]
var obj = {'name':'andy'};console.log(Object.getOwnPropertyDescriptor(obj,'name'));
// {value: "andy", writable: true, enumerable: true, configurable: true}ES2017 引入了
Object.getOwnPropertyDescriptors方法,返回指定对象所有自身属性(非继承属性)的描述对象。
相关文章推荐
- JavaScript Prototype对象浅谈
- Javascript浅谈之变量及变量对象
- 浅谈Javascript中Object与Function对象
- 通过一道笔试题浅谈javascript中的promise对象
- 通过一道笔试题浅谈javascript中的promise对象
- JavaScript_ECMAScript 定义类或对象浅谈
- 浅谈JavaScript 浏览器对象
- 浅谈JavaScript的Polymer框架中的behaviors对象
- 浅谈JavaScript的Polymer框架中的behaviors对象
- 浅谈JavaScript 标准对象
- 【javascript】浅谈javascript中的对象和类型
- 浅谈JavaScript中Date(日期对象),Math对象
- 浅谈JavaScript中的String对象常用方法
- 浅谈JavaScript Array对象
- 浅谈PHP与javascript之JSON XML 数组 对象三者的区别
- 浅谈JavaScript Date日期和时间对象
- 浅谈Javascript中Promise对象的实现
- 浅谈JavaScript中的对象及Promise对象的实现
- 浅谈JavaScript的内置对象和浏览器对象