您的位置:首页 > Web前端 > JavaScript

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 undefined
Object.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 ES6 对象